From 0597579115876943f37795ffe85d1612eaa7ea3f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 8 Dec 2024 18:19:37 +0100 Subject: [PATCH 01/91] First work with vulkano --- Cargo.lock | 339 ++++++++++++---- Cargo.toml | 10 +- build.rs | 24 -- res/shaders/main.frag | 8 - res/shaders/main.vert | 1 - res/shaders/vertex.frag | 7 +- res/shaders/vertex.vert | 2 +- src/display/app.rs | 93 ----- src/display/mod.rs | 5 - src/display/window.rs | 65 --- src/main.rs | 17 +- src/renderer/app.rs | 421 ++++++++++++++++++++ src/renderer/mod.rs | 22 +- src/renderer/render_context.rs | 133 +++++++ src/renderer/scene.rs | 210 ++++++++++ src/renderer/vulkan/mod.rs | 45 --- src/renderer/vulkan/utils/layers.rs | 51 --- src/renderer/vulkan/utils/mod.rs | 1 - src/renderer/vulkan/vertex.rs | 37 -- src/renderer/vulkan/vk_buffer.rs | 31 -- src/renderer/vulkan/vk_command_pool.rs | 52 --- src/renderer/vulkan/vk_device.rs | 109 ----- src/renderer/vulkan/vk_fence.rs | 30 -- src/renderer/vulkan/vk_framebuffer.rs | 44 -- src/renderer/vulkan/vk_graphics_pipeline.rs | 120 ------ src/renderer/vulkan/vk_instance.rs | 128 ------ src/renderer/vulkan/vk_physical_device.rs | 85 ---- src/renderer/vulkan/vk_render_context.rs | 206 ---------- src/renderer/vulkan/vk_render_pass.rs | 56 --- src/renderer/vulkan/vk_semaphore.rs | 29 -- src/renderer/vulkan/vk_shader_module.rs | 42 -- src/renderer/vulkan/vk_surface.rs | 94 ----- src/renderer/vulkan/vk_swapchain.rs | 297 -------------- src/scene/mod.rs | 4 - src/scene/triangle.rs | 49 --- src/scene/vertex.rs | 39 -- 36 files changed, 1059 insertions(+), 1847 deletions(-) delete mode 100644 build.rs delete mode 100644 res/shaders/main.frag delete mode 100644 res/shaders/main.vert delete mode 100644 src/display/app.rs delete mode 100644 src/display/mod.rs delete mode 100644 src/display/window.rs create mode 100644 src/renderer/app.rs create mode 100644 src/renderer/render_context.rs create mode 100644 src/renderer/scene.rs delete mode 100644 src/renderer/vulkan/mod.rs delete mode 100644 src/renderer/vulkan/utils/layers.rs delete mode 100644 src/renderer/vulkan/utils/mod.rs delete mode 100644 src/renderer/vulkan/vertex.rs delete mode 100644 src/renderer/vulkan/vk_buffer.rs delete mode 100644 src/renderer/vulkan/vk_command_pool.rs delete mode 100644 src/renderer/vulkan/vk_device.rs delete mode 100644 src/renderer/vulkan/vk_fence.rs delete mode 100644 src/renderer/vulkan/vk_framebuffer.rs delete mode 100644 src/renderer/vulkan/vk_graphics_pipeline.rs delete mode 100644 src/renderer/vulkan/vk_instance.rs delete mode 100644 src/renderer/vulkan/vk_physical_device.rs delete mode 100644 src/renderer/vulkan/vk_render_context.rs delete mode 100644 src/renderer/vulkan/vk_render_pass.rs delete mode 100644 src/renderer/vulkan/vk_semaphore.rs delete mode 100644 src/renderer/vulkan/vk_shader_module.rs delete mode 100644 src/renderer/vulkan/vk_surface.rs delete mode 100644 src/renderer/vulkan/vk_swapchain.rs delete mode 100644 src/scene/mod.rs delete mode 100644 src/scene/triangle.rs delete mode 100644 src/scene/vertex.rs diff --git a/Cargo.lock b/Cargo.lock index 85ed865..7092bb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,16 +145,8 @@ name = "ash" version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" - -[[package]] -name = "ash-window" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52bca67b61cb81e5553babde81b8211f713cb6db79766f80168f3e5f40ea6c82" dependencies = [ - "ash", - "raw-window-handle", - "raw-window-metal", + "libloading", ] [[package]] @@ -181,12 +173,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block2" version = "0.5.1" @@ -207,6 +193,20 @@ name = "bytemuck" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "bytes" @@ -270,33 +270,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] -name = "cocoa" -version = "0.25.0" +name = "cmake" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "libc", - "objc", + "cc", ] [[package]] @@ -364,12 +343,27 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[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" @@ -491,10 +485,15 @@ dependencies = [ ] [[package]] -name = "glob" -version = "0.3.1" +name = "half" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "bytemuck", + "cfg-if", + "crunchy", +] [[package]] name = "hashbrown" @@ -502,6 +501,12 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.4.0" @@ -530,6 +535,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[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" @@ -603,21 +614,22 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.7.4" @@ -633,6 +645,12 @@ dependencies = [ "libc", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "ndk" version = "0.9.0" @@ -644,7 +662,8 @@ dependencies = [ "log", "ndk-sys", "num_enum", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", "thiserror", ] @@ -663,6 +682,16 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num_enum" version = "0.7.3" @@ -684,15 +713,6 @@ dependencies = [ "syn", ] -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - [[package]] name = "objc-sys" version = "0.3.5" @@ -920,6 +940,29 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.7", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1009,6 +1052,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -1017,14 +1066,13 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "raw-window-metal" -version = "0.4.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e8caa82e31bb98fee12fa8f051c94a6aa36b07cddb03f0d4fc558988360ff1" +checksum = "b2000e45d7daa9b6d946e88dfa1d7ae330424a81918a6545741821c989eb80a9" dependencies = [ - "cocoa", - "core-graphics", - "objc", - "raw-window-handle", + "objc2", + "objc2-foundation", + "objc2-quartz-core", ] [[package]] @@ -1074,16 +1122,24 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + [[package]] name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", - "ash", - "ash-window", "env_logger", - "glob", "log", + "vulkano", + "vulkano-shaders", "winit", ] @@ -1100,6 +1156,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "same-file" version = "1.0.6" @@ -1115,6 +1177,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sctk-adwaita" version = "0.10.1" @@ -1148,6 +1216,39 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shaderc" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e07913ada18607bb60d12431cbe3358d3bbebbe95948e1618851dc01e63b7b" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73120d240fe22196300f39ca8547ca2d014960f27b19b47b21288b396272f7f7" +dependencies = [ + "cmake", + "libc", + "roxmltree", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1163,6 +1264,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slabbin" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd33b7a607dbd960b5e78bb4740d1f86e84250eb03a12960ee1482c2a256063" + [[package]] name = "smallvec" version = "1.13.2" @@ -1240,6 +1347,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -1328,6 +1445,71 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vk-parse" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3859da4d7b98bec73e68fb65815d47a263819c415c90eed42b80440a02cbce8c" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "vulkano" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#23606f05825adf5212f104ead9e95f9d325db1aa" +dependencies = [ + "ahash", + "ash", + "bytemuck", + "crossbeam-queue", + "half", + "heck", + "indexmap", + "libloading", + "nom", + "once_cell", + "parking_lot", + "proc-macro2", + "quote", + "raw-window-handle 0.6.2", + "raw-window-metal", + "serde", + "serde_json", + "slabbin", + "smallvec", + "thread_local", + "vk-parse", + "vulkano-macros", + "x11-dl", + "x11rb", +] + +[[package]] +name = "vulkano-macros" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#23606f05825adf5212f104ead9e95f9d325db1aa" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "vulkano-shaders" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#23606f05825adf5212f104ead9e95f9d325db1aa" +dependencies = [ + "ahash", + "heck", + "proc-macro2", + "quote", + "shaderc", + "syn", + "vulkano", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -1784,7 +1966,8 @@ dependencies = [ "orbclient", "percent-encoding", "pin-project", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", "redox_syscall 0.4.1", "rustix", "sctk-adwaita", @@ -1872,6 +2055,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +[[package]] +name = "xml-rs" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 3865769..3cf3338 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,13 +7,11 @@ publish = false [dependencies] anyhow = "1.0" -winit = { version = "0.30", features = ["rwh_06"] } -ash = { version = "0.38", default-features = false, features = ["linked", "debug", "std"] } -ash-window = "0.13" +winit = { version = "0.30", features = ["rwh_05"] } + +vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } +vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } # Log and tracing log = "0.4" env_logger = "0.11.5" - -[build-dependencies] -glob = "0.3" \ No newline at end of file diff --git a/build.rs b/build.rs deleted file mode 100644 index be949e6..0000000 --- a/build.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::process::Command; - -fn main() { - for shader in glob::glob("res/shaders/*").unwrap().filter_map(Result::ok) { - if !shader.is_file() { - continue; - } - - let shader_file_name = shader.to_str().unwrap(); - - let mut command = Command::new("glslc"); - command.arg(&shader); - - let out_file = match shader.extension().unwrap().to_str().unwrap() { - "vert" => shader_file_name.replace(".vert", ".vert.spv"), - "frag" => shader_file_name.replace(".frag", ".frag.spv"), - _ => continue, - }; - - command.arg("-o"); - command.arg(out_file); - command.output().unwrap(); - } -} diff --git a/res/shaders/main.frag b/res/shaders/main.frag deleted file mode 100644 index 33ca4af..0000000 --- a/res/shaders/main.frag +++ /dev/null @@ -1,8 +0,0 @@ -#version 450 - -layout (location = 0) in vec3 fragColor; -layout (location = 0) out vec4 outColor; - -void main() { - outColor = vec4(fragColor, 1.0); -} \ No newline at end of file diff --git a/res/shaders/main.vert b/res/shaders/main.vert deleted file mode 100644 index d141c1d..0000000 --- a/res/shaders/main.vert +++ /dev/null @@ -1 +0,0 @@ -#version 450 out gl_PerVertex { vec4 gl_Position; }; layout (location = 0) out vec3 fragColor; vec2 positions[3] = vec2[]( vec2(0.0, -0.5), vec2(0.5, 0.5), vec2(-0.5, 0.5) ); vec3 colors[3] = vec3[]( vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0) ); void main() { gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); fragColor = colors[gl_VertexIndex]; } \ No newline at end of file diff --git a/res/shaders/vertex.frag b/res/shaders/vertex.frag index 04f88bd..720d192 100644 --- a/res/shaders/vertex.frag +++ b/res/shaders/vertex.frag @@ -1,10 +1,9 @@ #version 450 -#extension GL_ARB_separate_shader_objects: enable -layout (location = 0) in vec3 fragColor; +layout (location = 0) in vec3 color; -layout (location = 0) out vec4 outColor; +layout (location = 0) out vec4 f_color; void main() { - outColor = vec4(fragColor, 1.0); + f_color = vec4(color, 1.0); } \ No newline at end of file diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index d7b1aac..3adb319 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1 +1 @@ -#version 450 #extension GL_ARB_separate_shader_objects: enable // NOTE: names must match the `Vertex` struct in Rust layout (location = 0) in vec2 pos; layout (location = 1) in vec3 color; layout (location = 0) out vec3 fragColor; out gl_PerVertex { vec4 gl_Position; }; void main() { gl_Position = vec4(pos, 0.0, 1.0); fragColor = color; } \ No newline at end of file +#version 450 layout (location = 0) in vec2 position; layout (location = 1) in vec3 color; layout (location = 0) out vec3 fragColor; void main() { gl_Position = vec4(position, 0.0, 1.0); fragColor = color; } \ No newline at end of file diff --git a/src/display/app.rs b/src/display/app.rs deleted file mode 100644 index 0d5fb1d..0000000 --- a/src/display/app.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::display::window::Window; -use crate::renderer::{vulkan::VkRenderContext, Renderable}; -use winit::application::ApplicationHandler; -use winit::event::WindowEvent; -use winit::event_loop::ActiveEventLoop; -use winit::window::WindowId; -use crate::scene::TriangleScene; - -pub struct App { - window: Window, - render_context: Option, - scene: Option>, -} - -impl App { - pub fn new(window: Window) -> Self { - Self { - window, - render_context: None, - scene: None, - } - } - - pub fn set_scene(&mut self, mut scene: Box) { - let result = self.render_context.as_mut() - .ok_or_else(|| anyhow::anyhow!("No render context")) - .and_then(|render_context| render_context.init_scene(&mut scene)); - - match result { - Ok(_) => self.scene = Some(scene), - Err(err) => log::warn!("{err}"), - } - } -} - -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - self.window - .create_window(event_loop) - .map_err(|err| format!("Failed to create window: {}", err)) - .unwrap(); - - self.render_context = VkRenderContext::init(&self.window).ok(); - - let scene = TriangleScene::new(); - self.set_scene(Box::new(scene)); - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => { - match self.render_context.as_ref() { - Some(render_context) => render_context.exit(), - None => log::warn!("Window closed but no render context found"), - }; - - log::debug!("The close button was pressed; stopping"); - event_loop.exit(); - } - WindowEvent::Resized(size) => { - match self.render_context.as_mut() { - Some(render_context) => { - if let Err(error) = - render_context.update_resolution(size.width, size.height) - { - log::error!( - "Failed to update resolution of render context : {}", - error - ); - } - } - None => log::warn!("Window resized but no render context found"), - }; - } - WindowEvent::RedrawRequested => { - if !event_loop.exiting() { - match self.render_context.as_mut() { - Some(render_context) => { - if let Err(error) = render_context.render(self.scene.as_ref()) { - log::error!("Failed to render with render context : {}", error); - event_loop.exit(); - } - } - None => log::warn!("Window resized but no render context found"), - }; - } - - self.window.request_redraw(); - } - _ => {} - } - } -} diff --git a/src/display/mod.rs b/src/display/mod.rs deleted file mode 100644 index f7758ab..0000000 --- a/src/display/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod app; -mod window; - -pub use app::App; -pub use window::Window; diff --git a/src/display/window.rs b/src/display/window.rs deleted file mode 100644 index 803fd35..0000000 --- a/src/display/window.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::ffi::c_char; -use winit::dpi::Pixel; -use winit::event_loop::ActiveEventLoop; -use winit::raw_window_handle::HasDisplayHandle; - -pub struct Window { - handle: Option, - window_attributes: winit::window::WindowAttributes, -} - -impl Window { - pub fn new(window_attributes: winit::window::WindowAttributes) -> Self { - Self { - handle: None, - window_attributes, - } - } - - pub fn create_window(&mut self, event_loop: &ActiveEventLoop) -> anyhow::Result<()> { - let window = event_loop.create_window(self.window_attributes.clone())?; - - self.handle = Some(window); - - Ok(()) - } - - pub fn required_extensions(&self) -> anyhow::Result> { - let display_handle = self - .handle - .as_ref() - .ok_or_else(|| anyhow::anyhow!("Window not found"))? - .display_handle()?; - - #[allow(unused_mut)] - let mut extension_names = - ash_window::enumerate_required_extensions(display_handle.as_raw())?.to_vec(); - - // TODO: Move this because is not related to Window extensions - #[cfg(any(target_os = "macos", target_os = "ios"))] - { - extension_names.push(ash::khr::portability_enumeration::NAME.as_ptr()); - // Enabling this extension is a requirement when using `VK_KHR_portability_subset` - extension_names.push(ash::khr::get_physical_device_properties2::NAME.as_ptr()); - } - - Ok(extension_names) - } - - pub fn handle(&self) -> Option<&winit::window::Window> { - self.handle.as_ref() - } - - pub fn physical_size(&self) -> Option> { - self.window_attributes - .inner_size - .and_then(|size| Some(size.to_physical::

(1.0))) - } - - pub fn request_redraw(&self) { - match self.handle.as_ref() { - Some(window) => window.request_redraw(), - None => log::warn!("Redraw requested but no window found"), - } - } -} diff --git a/src/main.rs b/src/main.rs index 2f48cfe..6c01c40 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,15 @@ +use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; -mod display; mod renderer; -mod scene; -fn main() { +fn main() -> Result<(), impl Error> { env_logger::init(); let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let window_attributes = winit::window::Window::default_attributes() - .with_title("Rust ASH Test") - .with_inner_size(winit::dpi::PhysicalSize::new( - f64::from(800), - f64::from(600), - )); + let mut app = renderer::App::new(&event_loop); - let window = display::Window::new(window_attributes); - let mut app = display::App::new(window); - - event_loop.run_app(&mut app).unwrap(); + event_loop.run_app(&mut app) } diff --git a/src/renderer/app.rs b/src/renderer/app.rs new file mode 100644 index 0000000..438f946 --- /dev/null +++ b/src/renderer/app.rs @@ -0,0 +1,421 @@ +use std::sync::Arc; +use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; +use vulkano::device::{Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags}; +use vulkano::device::physical::PhysicalDeviceType; +use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; +use vulkano::memory::allocator::StandardMemoryAllocator; +use vulkano::swapchain::{acquire_next_image, Surface, SwapchainCreateInfo, SwapchainPresentInfo}; +use vulkano::{sync, Validated, Version, VulkanError, VulkanLibrary}; +use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo}; +use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; +use vulkano::sync::GpuFuture; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::window::{Window, WindowId}; +use crate::renderer::render_context::RenderContext; +use crate::renderer::{window_size_dependent_setup, Scene}; + +pub struct App { + instance: Arc, + device: Arc, + queue: Arc, + memory_allocator: Arc, + command_buffer_allocator: Arc, + rcx: Option, + scene: Option, +} + +impl App { + pub fn new(event_loop: &EventLoop<()>) -> Self { + let library = VulkanLibrary::new().unwrap(); + + // The first step of any Vulkan program is to create an instance. + // + // When we create an instance, we have to pass a list of extensions that we want to enable. + // + // All the window-drawing functionalities are part of non-core extensions that we need to + // enable manually. To do so, we ask `Surface` for the list of extensions required to draw + // to a window. + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + + // Now creating the instance. + let instance = Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + enabled_layers: vec![ + String::from("VK_LAYER_KHRONOS_validation"), + String::from("VK_LAYER_MANGOHUD_overlay_x86_64"), + String::from("VK_LAYER_NV_optimus"), + ], + ..Default::default() + }, + ) + .unwrap(); + + // Choose device extensions that we're going to use. In order to present images to a + // surface, we need a `Swapchain`, which is provided by the `khr_swapchain` extension. + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + // We then choose which physical device to use. First, we enumerate all the available + // physical devices, then apply filters to narrow them down to those that can support our + // needs. + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + // For this example, we require at least Vulkan 1.3, or a device that has the + // `khr_dynamic_rendering` extension available. + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| { + // Some devices may not support the extensions or features that your application, + // or report properties and limits that are not sufficient for your application. + // These should be filtered out here. + p.supported_extensions().contains(&device_extensions) + }) + .filter_map(|p| { + // For each physical device, we try to find a suitable queue family that will + // execute our draw commands. + // + // Devices can provide multiple queues to run commands in parallel (for example a + // draw queue and a compute queue), similar to CPU threads. This is something you + // have to have to manage manually in Vulkan. Queues of the same type belong to the + // same queue family. + // + // Here, we look for a single queue family that is suitable for our purposes. In a + // real-world application, you may want to use a separate dedicated transfer queue + // to handle data transfers in parallel with graphics operations. You may also need + // a separate queue for compute operations, if your application uses those. + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + // We select a queue family that supports graphics operations. When drawing + // to a window surface, as we do in this example, we also need to check + // that queues in this queue family are capable of presenting images to the + // surface. + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + // The code here searches for the first queue family that is suitable. If none + // is found, `None` is returned to `filter_map`, which disqualifies this + // physical device. + .map(|i| (p, i as u32)) + }) + // All the physical devices that pass the filters above are suitable for the + // application. However, not every device is equal, some are preferred over others. + // Now, we assign each physical device a score, and pick the device with the lowest + // ("best") score. + // + // In this example, we simply select the best-scoring device to use in the application. + // In a real-world setting, you may want to use the best-scoring device only as a + // "default" or "recommended" device, and let the user choose the device themself. + .min_by_key(|(p, _)| { + // We assign a lower score to device types that are likely to be faster/better. + match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + } + }) + .expect("no suitable physical device found"); + + // Some little debug infos. + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + // If the selected device doesn't have Vulkan 1.3 available, then we need to enable the + // `khr_dynamic_rendering` extension manually. This extension became a core part of Vulkan + // in version 1.3 and later, so it's always available then and it does not need to be + // enabled. We can be sure that this extension will be available on the selected physical + // device, because we filtered out unsuitable devices in the device selection code above. + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + // Now initializing the device. This is probably the most important object of Vulkan. + // + // An iterator of created queues is returned by the function alongside the device. + let (device, mut queues) = Device::new( + // Which physical device to connect to. + physical_device, + DeviceCreateInfo { + // The list of queues that we are going to use. Here we only use one queue, from + // the previously chosen queue family. + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + + // A list of optional features and extensions that our program needs to work + // correctly. Some parts of the Vulkan specs are optional and must be enabled + // manually at device creation. In this example the only things we are going to + // need are the `khr_swapchain` extension that allows us to draw to a window, and + // `khr_dynamic_rendering` if we don't have Vulkan 1.3 available. + enabled_extensions: device_extensions, + + // In order to render with Vulkan 1.3's dynamic rendering, we need to enable it + // here. Otherwise, we are only allowed to render with a render pass object, as in + // the standard triangle example. The feature is required to be supported by the + // device if it supports Vulkan 1.3 and higher, or if the `khr_dynamic_rendering` + // extension is available, so we don't need to check for support. + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + + ..Default::default() + }, + ) + .unwrap(); + + // Since we can request multiple queues, the `queues` variable is in fact an iterator. We + // only use one queue in this example, so we just retrieve the first and only element of + // the iterator. + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command + // pools underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + Self { + instance, + device, + queue, + memory_allocator, + command_buffer_allocator, + rcx: None, + scene: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_attributes = winit::window::Window::default_attributes() + .with_title("Rust ASH Test") + .with_inner_size(winit::dpi::PhysicalSize::new( + f64::from(800), + f64::from(600), + )); + + let window = Arc::new( + event_loop + .create_window(window_attributes) + .unwrap(), + ); + + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + + self.rcx = Some(RenderContext::new(window, surface, &self.device)); + self.scene = Some(Scene::initialize(&self.device, &self.rcx.as_ref().unwrap().swapchain, &self.memory_allocator)); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + let rcx = self.rcx.as_mut().unwrap(); + + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; + } + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); + + // Do not draw the frame when the screen size is zero. On Windows, this can occur + // when minimizing the application. + if window_size.width == 0 || window_size.height == 0 { + return; + } + + // It is important to call this function from time to time, otherwise resources + // will keep accumulating and you will eventually reach an out of memory error. + // Calling this function polls various fences in order to determine what the GPU + // has already processed, and frees the resources that are no longer needed. + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + // Whenever the window resizes we need to recreate everything dependent on the + // window size. In this example that includes the swapchain, the framebuffers and + // the dynamic state viewport. + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain + .recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..rcx.swapchain.create_info() + }) + .expect("failed to recreate swapchain"); + + rcx.swapchain = new_swapchain; + + // Now that we have new swapchain images, we must create new image views from + // them as well. + rcx.attachment_image_views = window_size_dependent_setup(&new_images); + + rcx.viewport.extent = window_size.into(); + + rcx.recreate_swapchain = false; + } + + // Before we can draw on the output, we have to *acquire* an image from the + // swapchain. If no image is available (which happens if you submit draw commands + // too quickly), then the function will block. This operation returns the index of + // the image that we are allowed to draw upon. + // + // This function can block if no image is available. The parameter is an optional + // timeout after which the function call will return an error. + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + // `acquire_next_image` can be successful, but suboptimal. This means that the + // swapchain image will still work, but it may not display correctly. With some + // drivers this can be when the window resizes, but it may not cause the swapchain + // to become out of date. + if suboptimal { + rcx.recreate_swapchain = true; + } + + // In order to draw, we have to record a *command buffer*. The command buffer + // object holds the list of commands that are going to be executed. + // + // Recording a command buffer is an expensive operation (usually a few hundred + // microseconds), but it is known to be a hot path in the driver and is expected to + // be optimized. + // + // Note that we have to pass a queue family when we create the command buffer. The + // command buffer will only be executable on that given queue family. + let mut builder = AutoCommandBufferBuilder::primary( + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); + + builder + // Before we can draw, we have to *enter a render pass*. We specify which + // attachments we are going to use for rendering here, which needs to match + // what was previously specified when creating the pipeline. + .begin_rendering(RenderingInfo { + // As before, we specify one color attachment, but now we specify the image + // view to use as well as how it should be used. + color_attachments: vec![Some(RenderingAttachmentInfo { + // `Clear` means that we ask the GPU to clear the content of this + // attachment at the start of rendering. + load_op: AttachmentLoadOp::Clear, + // `Store` means that we ask the GPU to store the rendered output in + // the attachment image. We could also ask it to discard the result. + store_op: AttachmentStoreOp::Store, + // The value to clear the attachment with. Here we clear it with a blue + // color. + // + // Only attachments that have `AttachmentLoadOp::Clear` are provided + // with clear values, any others should use `None` as the clear value. + clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + ..RenderingAttachmentInfo::image_view( + // We specify image view corresponding to the currently acquired + // swapchain image, to use for this attachment. + rcx.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + // We are now inside the first subpass of the render pass. + // + // TODO: Document state setting and how it affects subsequent draw commands. + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) + .unwrap(); + + if let Some(scene) = self.scene.as_ref() { + scene.render(&mut builder); + } + + builder + // We leave the render pass. + .end_rendering() + .unwrap(); + + // Finish recording the command buffer by calling `end`. + let command_buffer = builder.build().unwrap(); + + let future = rcx + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.queue.clone(), command_buffer) + .unwrap() + // The color output is now expected to contain our triangle. But in order to + // show it on the screen, we have to *present* the image by calling + // `then_swapchain_present`. + // + // This function does not actually present the image immediately. Instead it + // submits a present command at the end of the queue. This means that it will + // only be presented once the GPU has finished executing the command buffer + // that draws the triangle. + .then_swapchain_present( + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + rcx.previous_frame_end = Some(future.boxed()); + } + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + } + Err(e) => { + println!("failed to flush future: {e}"); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + } + } + } + _ => {} + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index cca608a..e72d412 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,9 +1,19 @@ +mod render_context; +mod app; +pub use app::App; + +mod scene; +pub use scene::Scene; + use std::sync::Arc; -use ash::vk; +use vulkano::image::Image; +use vulkano::image::view::ImageView; -pub mod vulkan; -pub trait Renderable { - fn init(&mut self, device: &Arc, render_pass: &Arc) -> anyhow::Result<()>; - fn render(&self, device: &vulkan::VkDevice, swapchain: &vulkan::VkSwapchain, command_buffer: &vk::CommandBuffer) -> anyhow::Result<()>; -} \ No newline at end of file +/// This function is called once during initialization, then again whenever the window is resized. +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +} diff --git a/src/renderer/render_context.rs b/src/renderer/render_context.rs new file mode 100644 index 0000000..cd2bc24 --- /dev/null +++ b/src/renderer/render_context.rs @@ -0,0 +1,133 @@ +use std::sync::Arc; +use vulkano::device::Device; +use vulkano::image::{Image, ImageUsage}; +use vulkano::image::view::ImageView; +use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; +use vulkano::pipeline::graphics::viewport::Viewport; +use vulkano::pipeline::GraphicsPipeline; +use vulkano::render_pass::{Framebuffer, RenderPass}; +use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; +use vulkano::sync; +use vulkano::sync::GpuFuture; +use winit::window::Window; +use crate::renderer::window_size_dependent_setup; + +pub struct RenderContext { + pub(super) window: Arc, + pub(super) swapchain: Arc, + pub(super) attachment_image_views: Vec>, + pub(super) viewport: Viewport, + pub(super) recreate_swapchain: bool, + pub(super) previous_frame_end: Option>, +} + +impl RenderContext { + pub fn new(window: Arc, surface: Arc, device: &Arc) -> Self { + let window_size = window.inner_size(); + + + // Before we can draw on the surface, we have to create what is called a swapchain. + // Creating a swapchain allocates the color buffers that will contain the image that will + // ultimately be visible on the screen. These images are returned alongside the swapchain. + let (swapchain, images) = { + // Querying the capabilities of the surface. When we create the swapchain we can only + // pass values that are allowed by the capabilities. + let surface_capabilities = device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + // Choosing the internal format that the images will have. + let (image_format, _) = device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + // Please take a look at the docs for the meaning of the parameters we didn't mention. + Swapchain::new( + device.clone(), + surface, + SwapchainCreateInfo { + // Some drivers report an `min_image_count` of 1, but fullscreen mode requires + // at least 2. Therefore we must ensure the count is at least 2, otherwise the + // program would crash when entering fullscreen mode on those drivers. + min_image_count: surface_capabilities.min_image_count.max(2), + + image_format, + + // The size of the window, only used to initially setup the swapchain. + // + // NOTE: + // On some drivers the swapchain extent is specified by + // `surface_capabilities.current_extent` and the swapchain size must use this + // extent. This extent is always the same as the window size. + // + // However, other drivers don't specify a value, i.e. + // `surface_capabilities.current_extent` is `None`. These drivers will allow + // anything, but the only sensible value is the window size. + // + // Both of these cases need the swapchain to use the window size, so we just + // use that. + image_extent: window_size.into(), + + image_usage: ImageUsage::COLOR_ATTACHMENT, + + // The alpha mode indicates how the alpha value of the final image will behave. + // For example, you can choose whether the window will be opaque or + // transparent. + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; + + // When creating the swapchain, we only created plain images. To use them as an attachment + // for rendering, we must wrap then in an image view. + // + // Since we need to draw to multiple images, we are going to create a different image view + // for each image. + let attachment_image_views = window_size_dependent_setup(&images); + + // Dynamic viewports allow us to recreate just the viewport when the window is resized. + // Otherwise we would have to recreate the whole pipeline. + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + // In some situations, the swapchain will become invalid by itself. This includes for + // example when the window is resized (as the images of the swapchain will no longer match + // the window's) or, on Android, when the application went to the background and goes back + // to the foreground. + // + // In this situation, acquiring a swapchain image or presenting it will return an error. + // Rendering to an image of that swapchain will not produce any error, but may or may not + // work. To continue rendering, we need to recreate the swapchain by creating a new + // swapchain. Here, we remember that we need to do this for the next loop iteration. + let recreate_swapchain = false; + + // In the loop below we are going to submit commands to the GPU. Submitting a command + // produces an object that implements the `GpuFuture` trait, which holds the resources for + // as long as they are in use by the GPU. + // + // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to + // avoid that, we store the submission of the previous frame here. + let previous_frame_end = Some(sync::now(device.clone()).boxed()); + + Self { + window, + swapchain, + attachment_image_views, + viewport, + recreate_swapchain, + previous_frame_end, + } + } +} diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs new file mode 100644 index 0000000..174ba7e --- /dev/null +++ b/src/renderer/scene.rs @@ -0,0 +1,210 @@ +use std::sync::Arc; +use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}; +use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; +use vulkano::device::Device; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; +use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; +use vulkano::pipeline::{DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo}; +use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; +use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; +use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; +use vulkano::pipeline::graphics::multisample::MultisampleState; +use vulkano::pipeline::graphics::rasterization::RasterizationState; +use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; +use vulkano::pipeline::graphics::viewport::ViewportState; +use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; +use vulkano::swapchain::Swapchain; + +// We use `#[repr(C)]` here to force rustc to use a defined layout for our data, as the default +// representation has *no guarantees*. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], + + #[format(R32G32B32_SFLOAT)] + color: [f32; 3], +} + +pub struct Scene { + pipeline: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, +} + +impl Scene { + pub fn initialize(device: &Arc, swapchain: &Arc, memory_allocator: &Arc) -> Scene { + // The next step is to create the shaders. + // + // The raw shader creation API provided by the vulkano library is unsafe for various + // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL + // source - in the example below, the source is provided as a string input directly to the + // shader, but a path to a source file can be provided as well. Note that the user must + // specify the type of shader (e.g. "vertex", "fragment", etc.) using the `ty` option of + // the macro. + // + // The items generated by the `shader!` macro include a `load` function which loads the + // shader using an input logical device. The module also includes type definitions for + // layout structures defined in the shader source, for example uniforms and push constants. + // + // A more detailed overview of what the `shader!` macro generates can be found in the + // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/vertex.vert", + } + } + + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/vertex.frag", + } + } + + // Before we draw, we have to create what is called a **pipeline**. A pipeline describes + // how a GPU operation is to be performed. It is similar to an OpenGL program, but it also + // contains many settings for customization, all baked into a single object. For drawing, + // we create a **graphics** pipeline, but there are also other types of pipeline. + let pipeline = { + // First, we load the shaders that the pipeline will use: the vertex shader and the + // fragment shader. + // + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + + // Automatically generate a vertex input state from the vertex shader's input + // interface, that takes a single vertex buffer containing `Vertex` structs. + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + + // Make a list of the shader stages that the pipeline will have. + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + // We must now create a **pipeline layout** object, which describes the locations and + // types of descriptor sets and push constants used by the shaders in the pipeline. + // + // Multiple pipelines can share a common layout object, which is more efficient. The + // shaders in a pipeline must use a subset of the resources described in its pipeline + // layout, but the pipeline layout is allowed to contain resources that are not present + // in the shaders; they can be used by shaders in other pipelines that share the same + // layout. Thus, it is a good idea to design shaders so that many pipelines have common + // resource locations, which allows them to share pipeline layouts. + let layout = PipelineLayout::new( + device.clone(), + // Since we only have one pipeline in this example, and thus one pipeline layout, + // we automatically generate the creation info for it from the resources used in + // the shaders. In a real application, you would specify this information manually + // so that you can re-use one layout in multiple pipelines. + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(device.clone()) + .unwrap(), + ) + .unwrap(); + + // We describe the formats of attachment images where the colors, depth and/or stencil + // information will be written. The pipeline will only be usable with this particular + // configuration of the attachment images. + let subpass = PipelineRenderingCreateInfo { + // We specify a single color attachment that will be rendered to. When we begin + // rendering, we will specify a swapchain image to be used as this attachment, so + // here we set its format to be the same format as the swapchain. + color_attachment_formats: vec![Some(swapchain.image_format())], + ..Default::default() + }; + + // Finally, create the pipeline. + GraphicsPipeline::new( + device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + // How vertex data is read from the vertex buffers into the vertex shader. + vertex_input_state: Some(vertex_input_state), + // How vertices are arranged into primitive shapes. The default primitive shape + // is a triangle. + input_assembly_state: Some(InputAssemblyState::default()), + // How primitives are transformed and clipped to fit the framebuffer. We use a + // resizable viewport, set to draw over the entire window. + viewport_state: Some(ViewportState::default()), + // How polygons are culled and converted into a raster of pixels. The default + // value does not perform any culling. + rasterization_state: Some(RasterizationState::default()), + // How multiple fragment shader samples are converted to a single pixel value. + // The default value does not perform any multisampling. + multisample_state: Some(MultisampleState::default()), + // How pixel values are combined with the values already present in the + // framebuffer. The default value overwrites the old value with the new one, + // without any blending. + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + // Dynamic states allows us to specify parts of the pipeline settings when + // recording the command buffer, before we perform drawing. Here, we specify + // that the viewport should be dynamic. + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() + }; + + // We now create a buffer that will store the shape of our triangle. + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + color: [1.0, 0.0, 0.0], + }, + MyVertex { + position: [0.0, 0.5], + color: [0.0, 1.0, 0.0], + }, + MyVertex { + position: [0.25, -0.1], + color: [0.0, 0.0, 1.0], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + Self { + pipeline, + vertex_buffer, + } + } + + pub fn render(&self, builder: &mut AutoCommandBufferBuilder) { + builder.bind_pipeline_graphics(self.pipeline.clone()) + .unwrap() + .bind_vertex_buffers(0, self.vertex_buffer.clone()) + .unwrap(); + + // We add a draw command. + unsafe { builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0) }.unwrap(); + } +} \ No newline at end of file diff --git a/src/renderer/vulkan/mod.rs b/src/renderer/vulkan/mod.rs deleted file mode 100644 index a00ef58..0000000 --- a/src/renderer/vulkan/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -mod vk_render_context; -pub use vk_render_context::VkRenderContext; - -mod vk_instance; -pub use vk_instance::VkInstance; - -mod vk_surface; -pub use vk_surface::{SwapchainSupportDetails, VkSurface}; - -mod vk_physical_device; -pub use vk_physical_device::VkPhysicalDevice; - -mod vk_device; -pub use vk_device::VkDevice; - -mod vk_swapchain; -pub use vk_swapchain::VkSwapchain; - -mod vk_shader_module; -pub use vk_shader_module::VkShaderModule; - -mod vk_graphics_pipeline; -pub use vk_graphics_pipeline::VkGraphicsPipeline; - -mod vk_render_pass; -pub use vk_render_pass::VkRenderPass; - -mod vk_semaphore; -pub use vk_semaphore::VkSemaphore; - -mod vk_command_pool; -pub use vk_command_pool::VkCommandPool; - -mod vk_framebuffer; -pub use vk_framebuffer::VkFramebuffer; - -mod vk_fence; -pub use vk_fence::VkFence; - -mod utils; -mod vertex; -mod vk_buffer; -pub use vk_buffer::VkBuffer; - -pub use vertex::Vertex; diff --git a/src/renderer/vulkan/utils/layers.rs b/src/renderer/vulkan/utils/layers.rs deleted file mode 100644 index 9d901fb..0000000 --- a/src/renderer/vulkan/utils/layers.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::ffi::CString; - -pub enum LayersSelector<'a> { - Nothing, - SpecificLayers(Vec<&'a str>), - All, -} - -pub fn use_layers(entry: &ash::Entry, selector: LayersSelector) -> Vec { - let layers_available = get_layers_available(entry) - .iter() - .filter_map(|layer| { - layer - .layer_name_as_c_str() - .and_then(|layer_name| Ok(CString::from(layer_name))) - .ok() - }) - .collect::>(); - - match selector { - LayersSelector::Nothing => Vec::new(), - LayersSelector::SpecificLayers(layers) => select_layers(&layers_available, &layers), - LayersSelector::All => layers_available, - } -} - -fn get_layers_available(entry: &ash::Entry) -> Vec { - unsafe { - entry - .enumerate_instance_layer_properties() - .unwrap_or_default() - } -} - -fn select_layers(layers_available: &Vec, layers_to_select: &[&str]) -> Vec { - layers_to_select - .iter() - .filter_map(|layer_name| { - if layers_available.iter().any(|layer_available| { - layer_available - .to_str() - .and_then(|layer_available| Ok(layer_available.eq(*layer_name))) - .unwrap_or(false) - }) { - CString::new(*layer_name).ok() - } else { - None - } - }) - .collect::>() -} diff --git a/src/renderer/vulkan/utils/mod.rs b/src/renderer/vulkan/utils/mod.rs deleted file mode 100644 index 759c25e..0000000 --- a/src/renderer/vulkan/utils/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod layers; diff --git a/src/renderer/vulkan/vertex.rs b/src/renderer/vulkan/vertex.rs deleted file mode 100644 index 5d97213..0000000 --- a/src/renderer/vulkan/vertex.rs +++ /dev/null @@ -1,37 +0,0 @@ -use ash::vk; -use std::mem::offset_of; - -#[derive(Default)] -pub struct Vertex { - pub position: [f32; 2], - pub color: [f32; 3], -} - -impl Vertex { - pub fn new(position: [f32; 2], color: [f32; 3]) -> Self { - Self { position, color } - } - - pub fn get_binding_description() -> vk::VertexInputBindingDescription { - vk::VertexInputBindingDescription::default() - .binding(0) - .stride(size_of::() as u32) - .input_rate(vk::VertexInputRate::VERTEX) - } - - pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { - let position_attribute = vk::VertexInputAttributeDescription::default() - .binding(0) - .location(0) - .format(vk::Format::R32G32_SFLOAT) - .offset(offset_of!(Vertex, position) as u32); - - let color_attribute = vk::VertexInputAttributeDescription::default() - .binding(0) - .location(1) - .format(vk::Format::R32G32B32_SFLOAT) - .offset(offset_of!(Vertex, color) as u32); - - [position_attribute, color_attribute] - } -} \ No newline at end of file diff --git a/src/renderer/vulkan/vk_buffer.rs b/src/renderer/vulkan/vk_buffer.rs deleted file mode 100644 index b03dea4..0000000 --- a/src/renderer/vulkan/vk_buffer.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::sync::Arc; -use ash::prelude::VkResult; -use ash::vk; -use crate::renderer::vulkan::VkDevice; - -pub struct VkBuffer { - device: Arc, - - handle: vk::Buffer, -} - -impl VkBuffer { - pub fn new(device: &Arc, info: &vk::BufferCreateInfo) -> VkResult { - let buffer = unsafe { device.handle.create_buffer(info, None)? }; - - Ok(VkBuffer { device: Arc::clone(device), handle: buffer }) - } - - - pub fn mem_requirements(&self) -> vk::MemoryRequirements { - unsafe { self.device.handle.get_buffer_memory_requirements(self.handle) } - } -} - -impl Drop for VkBuffer { - fn drop(&mut self) { - unsafe { - self.device.handle.destroy_buffer(self.handle, None); - } - } -} \ No newline at end of file diff --git a/src/renderer/vulkan/vk_command_pool.rs b/src/renderer/vulkan/vk_command_pool.rs deleted file mode 100644 index c657e97..0000000 --- a/src/renderer/vulkan/vk_command_pool.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::VkDevice; -use ash::prelude::VkResult; -use ash::vk; -use std::sync::Arc; - -pub struct VkCommandPool { - device: Arc, - - pub handle: vk::CommandPool, -} - -impl VkCommandPool { - pub fn new(device: &Arc) -> VkResult { - let command_pool_info = - vk::CommandPoolCreateInfo::default() - .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) - .queue_family_index(device.queue_family_index); - let command_pool = unsafe { - device - .handle - .create_command_pool(&command_pool_info, None)? - }; - log::debug!("Command pool created ({command_pool:?})"); - - Ok(Self { - device: device.clone(), - handle: command_pool, - }) - } - - pub fn allocate_command_buffers_for_framebuffers(&self, framebuffers_count: u32) -> VkResult> { - let command_buffer_info = vk::CommandBufferAllocateInfo::default() - .command_pool(self.handle) - .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(framebuffers_count); - - let command_buffers = unsafe { - self.device - .handle - .allocate_command_buffers(&command_buffer_info)? - }; - - Ok(command_buffers) - } -} - -impl Drop for VkCommandPool { - fn drop(&mut self) { - unsafe { self.device.handle.destroy_command_pool(self.handle, None) }; - log::debug!("Command pool destroyed ({:?})", self.handle); - } -} diff --git a/src/renderer/vulkan/vk_device.rs b/src/renderer/vulkan/vk_device.rs deleted file mode 100644 index 9f14630..0000000 --- a/src/renderer/vulkan/vk_device.rs +++ /dev/null @@ -1,109 +0,0 @@ -use super::{VkInstance, VkPhysicalDevice}; -use ash::prelude::VkResult; -use ash::vk; -use std::sync::Arc; - -pub struct VkDevice { - instance: Arc, - physical_device: Arc, - - pub handle: ash::Device, - pub swapchain_loader: ash::khr::swapchain::Device, - pub queue_family_index: u32, - - // Arc not used because vk::Queue is destroyed with Device automatically - // so any references of vk::Queue must be destroyed with VkDevice - queues: Vec, -} - -impl VkDevice { - pub fn new_graphics_device( - instance: &Arc, - physical_device: &Arc, - queue_family_index: u32, - ) -> anyhow::Result { - let device_extension_names_raw = [ - ash::khr::swapchain::NAME.as_ptr(), - #[cfg(any(target_os = "macos", target_os = "ios"))] - ash::khr::portability_subset::NAME.as_ptr(), - ]; - let features = vk::PhysicalDeviceFeatures { - shader_clip_distance: 1, - ..Default::default() - }; - - let queues_priorities = [1.0]; - let queue_info = vk::DeviceQueueCreateInfo::default() - .queue_family_index(queue_family_index) - .queue_priorities(&queues_priorities); - - let device_create_info = vk::DeviceCreateInfo::default() - .queue_create_infos(std::slice::from_ref(&queue_info)) - .enabled_extension_names(&device_extension_names_raw) - .enabled_features(&features); - - let device = unsafe { - instance - .handle - .create_device(physical_device.handle, &device_create_info, None)? - }; - log::debug!("Device created ({:?})", device.handle()); - - let queues = queues_priorities - .iter() - .enumerate() - .map(|(index, _)| unsafe { device.get_device_queue(queue_family_index, index as u32) }) - .collect::>(); - - let swapchain_loader = ash::khr::swapchain::Device::new(&instance.handle, &device); - - Ok(Self { - instance: Arc::clone(instance), - physical_device: Arc::clone(&physical_device), - handle: device, - swapchain_loader, - queue_family_index, - queues, - }) - } - - pub fn get_device_queue(&self, queue_index: u32) -> Option<&vk::Queue> { - self.queues.get(queue_index as usize) - } - - pub fn create_command_pool( - &self, - info: &vk::CommandPoolCreateInfo, - ) -> VkResult { - let info = info.queue_family_index(self.queue_family_index); - - unsafe { self.handle.create_command_pool(&info, None) } - } - - pub fn allocate_command_buffers( - &self, - info: &vk::CommandBufferAllocateInfo, - ) -> VkResult> { - unsafe { self.handle.allocate_command_buffers(&info) } - } - - pub fn create_fence(&self, info: &vk::FenceCreateInfo) -> VkResult { - unsafe { self.handle.create_fence(&info, None) } - } - - pub fn create_semaphore( - &self, - info: &vk::SemaphoreCreateInfo, - ) -> VkResult { - unsafe { self.handle.create_semaphore(&info, None) } - } -} - -impl Drop for VkDevice { - fn drop(&mut self) { - unsafe { - self.handle.destroy_device(None); - log::debug!("Device destroyed ({:?})", self.handle.handle()); - } - } -} diff --git a/src/renderer/vulkan/vk_fence.rs b/src/renderer/vulkan/vk_fence.rs deleted file mode 100644 index 9fb71ff..0000000 --- a/src/renderer/vulkan/vk_fence.rs +++ /dev/null @@ -1,30 +0,0 @@ -use super::VkDevice; -use ash::vk; -use std::sync::Arc; - -pub struct VkFence { - device: Arc, - - pub handle: vk::Fence, -} - -impl VkFence { - pub fn new(device: &Arc) -> anyhow::Result { - let fence_info = vk::FenceCreateInfo::default() - .flags(vk::FenceCreateFlags::SIGNALED); - let fence = unsafe { device.handle.create_fence(&fence_info, None)? }; - log::debug!("Fence created ({fence:?})"); - - Ok(Self { - device: device.clone(), - handle: fence, - }) - } -} - -impl Drop for VkFence { - fn drop(&mut self) { - unsafe { self.device.handle.destroy_fence(self.handle, None) }; - log::debug!("Fence destroyed ({:?})", self.handle); - } -} \ No newline at end of file diff --git a/src/renderer/vulkan/vk_framebuffer.rs b/src/renderer/vulkan/vk_framebuffer.rs deleted file mode 100644 index 8486fb5..0000000 --- a/src/renderer/vulkan/vk_framebuffer.rs +++ /dev/null @@ -1,44 +0,0 @@ -use super::{VkDevice, VkRenderPass, VkSwapchain}; -use ash::vk; -use std::sync::Arc; - -pub struct VkFramebuffer { - device: Arc, - image_view: Arc, - render_pass: Arc, - - pub handle: vk::Framebuffer, -} - -impl VkFramebuffer { - pub fn from_swapchain_image_view( - device: &Arc, - render_pass: &Arc, - image_view: &Arc, - swapchain: &VkSwapchain, - ) -> anyhow::Result { - let attachments = [*image_view.as_ref()]; - let framebuffer_info = vk::FramebufferCreateInfo::default() - .render_pass(render_pass.handle) - .width(swapchain.surface_resolution.width) - .height(swapchain.surface_resolution.height) - .attachments(&attachments) - .layers(1); - - let framebuffer = unsafe { device.handle.create_framebuffer(&framebuffer_info, None)? }; - - Ok(Self { - device: device.clone(), - render_pass: render_pass.clone(), - image_view: image_view.clone(), - - handle: framebuffer, - }) - } -} - -impl Drop for VkFramebuffer { - fn drop(&mut self) { - unsafe { self.device.handle.destroy_framebuffer(self.handle, None) }; - } -} diff --git a/src/renderer/vulkan/vk_graphics_pipeline.rs b/src/renderer/vulkan/vk_graphics_pipeline.rs deleted file mode 100644 index a9af298..0000000 --- a/src/renderer/vulkan/vk_graphics_pipeline.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::{VkDevice, VkRenderPass, VkShaderModule, VkSwapchain}; -use ash::vk; -use std::ffi::CStr; -use std::sync::Arc; - -pub struct VkGraphicsPipeline { - device: Arc, - render_pass: Arc, - - pub pipeline_layout: vk::PipelineLayout, - pub pipeline: vk::Pipeline, - vertex_shader: VkShaderModule, - fragment_shader: VkShaderModule, -} - -impl VkGraphicsPipeline { - pub fn new( - device: &Arc, - render_pass: &Arc, - ) -> anyhow::Result { - let shader_entry_name = CStr::from_bytes_with_nul(b"main\0")?; - - let vert_shader_module = - VkShaderModule::from_spv_file(device, "res/shaders/main.vert.spv")?; - - let vert_shader_info = vk::PipelineShaderStageCreateInfo::default() - .module(vert_shader_module.handle) - .name(shader_entry_name) - .stage(vk::ShaderStageFlags::VERTEX); - - let frag_shader_module = - VkShaderModule::from_spv_file(device, "res/shaders/main.frag.spv")?; - - let frag_shader_info = vk::PipelineShaderStageCreateInfo::default() - .module(frag_shader_module.handle) - .name(shader_entry_name) - .stage(vk::ShaderStageFlags::FRAGMENT); - - let shader_stage_create_infos = [vert_shader_info, frag_shader_info]; - - let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::default(); - - let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() - .topology(vk::PrimitiveTopology::TRIANGLE_LIST); - - let viewport_state = vk::PipelineViewportStateCreateInfo::default() - .viewport_count(1) - .scissor_count(1); - - let rasterizer = vk::PipelineRasterizationStateCreateInfo::default() - .polygon_mode(vk::PolygonMode::FILL) - .cull_mode(vk::CullModeFlags::BACK) - .front_face(vk::FrontFace::CLOCKWISE) - .line_width(1.0); - - let multisampling = vk::PipelineMultisampleStateCreateInfo::default() - .rasterization_samples(vk::SampleCountFlags::TYPE_1) - .min_sample_shading(1.0); - - let color_blend_attachment = vk::PipelineColorBlendAttachmentState::default() - .color_write_mask(vk::ColorComponentFlags::RGBA); - - let attachments = [color_blend_attachment]; - let color_blending = - vk::PipelineColorBlendStateCreateInfo::default().attachments(&attachments); - - let dynamic_state = vk::PipelineDynamicStateCreateInfo::default() - .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]); - - let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default(); - let pipeline_layout = unsafe { - device - .handle - .create_pipeline_layout(&pipeline_layout_info, None)? - }; - log::debug!("Pipeline layout created ({pipeline_layout:?})"); - - let pipeline_info = vk::GraphicsPipelineCreateInfo::default() - .stages(&shader_stage_create_infos) - .vertex_input_state(&vertex_input_info) - .input_assembly_state(&input_assembly) - .viewport_state(&viewport_state) - .rasterization_state(&rasterizer) - .multisample_state(&multisampling) - .color_blend_state(&color_blending) - .dynamic_state(&dynamic_state) - .layout(pipeline_layout) - .render_pass(render_pass.handle); - let pipeline = unsafe { - device - .handle - .create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None) - .map_err(|(_, error)| error)?[0] - }; - log::debug!("Pipeline created ({pipeline_layout:?})"); - - Ok(Self { - device: device.clone(), - render_pass: render_pass.clone(), - pipeline_layout, - pipeline, - vertex_shader: vert_shader_module, - fragment_shader: frag_shader_module, - }) - } -} - -impl Drop for VkGraphicsPipeline { - fn drop(&mut self) { - unsafe { - self.device.handle.destroy_pipeline(self.pipeline, None); - log::debug!("Pipeline destroyed ({:?})", self.pipeline); - - self.device - .handle - .destroy_pipeline_layout(self.pipeline_layout, None); - log::debug!("Pipeline layout destroyed ({:?})", self.pipeline_layout); - } - } -} diff --git a/src/renderer/vulkan/vk_instance.rs b/src/renderer/vulkan/vk_instance.rs deleted file mode 100644 index 101dec2..0000000 --- a/src/renderer/vulkan/vk_instance.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::renderer::vulkan::{ - utils::layers::{use_layers, LayersSelector}, - VkPhysicalDevice, -}; -use ash::khr::surface; -use ash::{vk, Entry, Instance}; -use std::ffi::{c_char, CStr, CString}; -use std::sync::Arc; - -pub struct VkInstance { - pub entry: Entry, - pub handle: Instance, - pub surface_loader: surface::Instance, -} - -impl VkInstance { - pub fn new(required_extensions: &Vec<*const c_char>) -> Self { - let entry = Entry::linked(); - - log::debug!("Initializing Vulkan instance"); - - if log::log_enabled!(log::Level::Debug) { - let layer_properties = - unsafe { entry.enumerate_instance_layer_properties() }.unwrap_or_default(); - - for layer_property in layer_properties { - let layer_extensions = unsafe { - entry.enumerate_instance_extension_properties( - layer_property.layer_name_as_c_str().ok(), - ) - } - .unwrap_or_default(); - log::debug!("{layer_property:#?} {layer_extensions:#?}"); - } - } - - { - let required_extensions = required_extensions - .iter() - .map(|str| unsafe { CStr::from_ptr(*str) }) - .map(|cstr| cstr.to_string_lossy()) - .collect::>(); - log::debug!( - "Required instance extensions: {}", - required_extensions.join(", ") - ); - } - - // Layers - #[allow(unused)] - let mut layer_selector = LayersSelector::Nothing; - #[cfg(debug_assertions)] - { - layer_selector = LayersSelector::SpecificLayers(vec![ - "VK_LAYER_KHRONOS_validation", - "VK_LAYER_MANGOHUD_overlay_x86_64", - "VK_LAYER_NV_optimus", - ]); - } - let layers = use_layers(&entry, layer_selector); - - { - let layers = layers - .iter() - .map(|layer| layer.to_string_lossy()) - .collect::>(); - log::debug!("Selected debug layers : {}", layers.join(", ")) - } - - let layers_raw = layers.iter().map(|s| s.as_ptr()).collect::>(); - - // App Info - let app_name = CString::new("VulkanTriangle").unwrap(); - let appinfo = vk::ApplicationInfo::default() - .application_name(app_name.as_c_str()) - .application_version(0) - .engine_name(app_name.as_c_str()) - .engine_version(0) - .api_version(vk::make_api_version(0, 1, 0, 0)); - - let create_flags = if cfg!(any(target_os = "macos", target_os = "ios")) { - vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR - } else { - vk::InstanceCreateFlags::default() - }; - - // Instance Info - let create_info = vk::InstanceCreateInfo::default() - .application_info(&appinfo) - .enabled_layer_names(&layers_raw) - .enabled_extension_names(&required_extensions) - .flags(create_flags); - - let instance: Instance = unsafe { - entry - .create_instance(&create_info, None) - .expect("Instance creation error") - }; - - let surface_loader = surface::Instance::new(&entry, &instance); - - log::debug!("Vulkan instance created ({:?})", instance.handle()); - - Self { - entry, - handle: instance, - surface_loader, - } - } - - pub fn get_physical_devices(instance: &Arc) -> Vec { - let physical_devices = unsafe { instance.handle.enumerate_physical_devices() }; - physical_devices - .unwrap_or_default() - .iter() - .map(|physical_device| VkPhysicalDevice::new(&instance, *physical_device)) - .collect() - } -} - -impl Drop for VkInstance { - fn drop(&mut self) { - unsafe { - self.handle.destroy_instance(None); - } - log::debug!("Vulkan instance destroyed ({:?})", self.handle.handle()); - } -} diff --git a/src/renderer/vulkan/vk_physical_device.rs b/src/renderer/vulkan/vk_physical_device.rs deleted file mode 100644 index 3534f24..0000000 --- a/src/renderer/vulkan/vk_physical_device.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::sync::Arc; -use super::{VkInstance, VkSurface}; -use ash::vk; - -pub struct VkPhysicalDevice { - instance: Arc, - pub handle: vk::PhysicalDevice, - - pub properties: vk::PhysicalDeviceProperties, - pub features: vk::PhysicalDeviceFeatures, - pub queue_family_properties: Vec, -} - -impl VkPhysicalDevice { - pub fn new(instance: &Arc, physical_device: vk::PhysicalDevice) -> Self { - log::debug!("New physical device"); - let device_properties = unsafe { instance.handle.get_physical_device_properties(physical_device) }; - log::debug!("{device_properties:#?}"); - let device_features = unsafe { instance.handle.get_physical_device_features(physical_device) }; - log::debug!("{device_features:#?}"); - let device_queue_families = - unsafe { instance.handle.get_physical_device_queue_family_properties(physical_device) }; - log::debug!("{device_queue_families:#?}"); - - Self { - instance: Arc::clone(instance), - handle: physical_device, - properties: device_properties, - features: device_features, - queue_family_properties: device_queue_families, - } - } - - pub fn find_queue_family_by( - &self, - queue_flags: Option, - surface: Option<&VkSurface>, - ) -> Option<(u32, &vk::QueueFamilyProperties)> { - self.queue_family_properties.iter().enumerate().find_map( - |(index, queue_family_property)| { - let surface_check_passed = match surface { - Some(surface) => surface - .physical_device_queue_supported(self, index as u32) - .unwrap_or(false), - None => true, - }; - - let queue_flags_check_passed = match queue_flags { - Some(queue_flags) => queue_family_property.queue_flags.contains(queue_flags), - None => true, - }; - - if surface_check_passed && queue_flags_check_passed { - Some((index as u32, queue_family_property)) - } else { - None - } - }, - ) - } - - pub fn pick_physical_device_and_queue_by<'a>( - physical_devices: &'a Vec, - queue_flags: Option, - surface: Option<&VkSurface>, - ) -> Option<(&'a VkPhysicalDevice, u32, &'a vk::QueueFamilyProperties)> { - physical_devices.iter().find_map(|physical_device| { - physical_device - .find_queue_family_by(queue_flags, surface) - .and_then(|(queue_index, queue_family_properties)| { - Some((physical_device, queue_index, queue_family_properties)) - }) - }) - } - - pub fn priority(&self) -> usize { - match self.properties.device_type { - vk::PhysicalDeviceType::CPU => 1, - vk::PhysicalDeviceType::VIRTUAL_GPU => 2, - vk::PhysicalDeviceType::INTEGRATED_GPU => 3, - vk::PhysicalDeviceType::DISCRETE_GPU => 4, - _ => 0, - } - } -} diff --git a/src/renderer/vulkan/vk_render_context.rs b/src/renderer/vulkan/vk_render_context.rs deleted file mode 100644 index 68dd304..0000000 --- a/src/renderer/vulkan/vk_render_context.rs +++ /dev/null @@ -1,206 +0,0 @@ -use super::{ - VkCommandPool, VkDevice, VkFence, VkFramebuffer, VkGraphicsPipeline, VkInstance, VkPhysicalDevice, - VkRenderPass, VkSemaphore, VkSurface, VkSwapchain, -}; -use ash::vk; -use std::sync::Arc; -use crate::renderer::Renderable; - -pub struct VkRenderContext { - instance: Arc, - surface: Arc, - device: Arc, - - swapchain: Arc, - render_pass: Arc, - framebuffers: Vec>, - - command_pool: VkCommandPool, - command_buffers: Vec, - image_available_semaphore: VkSemaphore, - render_finished_semaphore: VkSemaphore, - in_flight_fence: VkFence, -} - -impl VkRenderContext { - pub fn init(window: &crate::display::Window) -> anyhow::Result { - let required_extensions = window.required_extensions()?; - - let instance = Arc::new(VkInstance::new(&required_extensions)); - let surface = Arc::new(VkSurface::new(&window, instance.clone())?); - - let mut physical_devices = VkInstance::get_physical_devices(&instance); - physical_devices.sort_by(|a, b| b.priority().cmp(&a.priority())); - - let (physical_device, queue_family_index, properties) = - VkPhysicalDevice::pick_physical_device_and_queue_by( - &physical_devices, - Some(vk::QueueFlags::GRAPHICS), - Some(&surface), - ) - .ok_or_else(|| anyhow::anyhow!("Unable to find physical device"))?; - log::debug!( - "Selected queue {properties:#?} for physical device {:?}", - physical_device.properties.device_name_as_c_str() - ); - - let device = Arc::new(VkDevice::new_graphics_device( - &instance, - &physical_device, - queue_family_index, - )?); - - let swapchain = Arc::new(VkSwapchain::new( - &window, - &surface, - &device, - &physical_device, - )?); - - let render_pass = Arc::new(VkRenderPass::new(&device, &swapchain)?); - - let framebuffers = swapchain - .create_framebuffers(&render_pass) - .ok_or_else(|| anyhow::anyhow!("Failed to get framebuffers"))?; - - let command_pool = VkCommandPool::new(&device)?; - - // Destroyed with command pool - let command_buffers = command_pool - .allocate_command_buffers_for_framebuffers(framebuffers.len() as u32)?; - - let image_available_semaphore = VkSemaphore::new(&device)?; - let render_finished_semaphore = VkSemaphore::new(&device)?; - let in_flight_fence = VkFence::new(&device)?; - - Ok(Self { - instance, - surface, - device, - - swapchain, - render_pass, - framebuffers, - - command_pool, - command_buffers, - - image_available_semaphore, - render_finished_semaphore, - in_flight_fence, - }) - } - - pub fn render(&mut self, scene: Option<&Box>) -> anyhow::Result<()> { - unsafe { self.device.handle.wait_for_fences(&[self.in_flight_fence.handle], true, u64::MAX)? }; - unsafe { self.device.handle.reset_fences(&[self.in_flight_fence.handle])? }; - - let (index, _) = self - .swapchain - .acquire_next_image(&self.image_available_semaphore)?; - - // if self.swapchain.is_dirty() { - // self.update_swapchain()? - // } - - let command_buffer = self.command_buffers[index as usize]; - unsafe { self.device.handle.reset_command_buffer(command_buffer, vk::CommandBufferResetFlags::default())? }; - - let render_area = vk::Rect2D::default().extent(self.swapchain.surface_resolution); - let clear_value = vk::ClearValue::default(); - let command_buffer_begin_info = vk::CommandBufferBeginInfo::default(); - unsafe { - self.device - .handle - .begin_command_buffer(command_buffer, &command_buffer_begin_info)? - }; - - let clear_values = [clear_value]; - let framebuffer = self.framebuffers[index as usize].as_ref(); - let render_pass_begin_info = vk::RenderPassBeginInfo::default() - .render_pass(self.render_pass.handle) - .framebuffer(framebuffer.handle) - .render_area(render_area) - .clear_values(&clear_values); - - unsafe { - self.device.handle.cmd_begin_render_pass( - command_buffer, - &render_pass_begin_info, - vk::SubpassContents::INLINE, - ); - }; - - if let Some(scene) = scene { - scene.render(&self.device, &self.swapchain, &command_buffer)?; - } - - unsafe { self.device.handle.cmd_end_render_pass(command_buffer) }; - - unsafe { self.device.handle.end_command_buffer(command_buffer)? }; - - let wait_semaphores = [self.image_available_semaphore.handle]; - let signal_semaphores = [self.render_finished_semaphore.handle]; - let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; - let command_buffers_to_submit = [command_buffer]; - let submit_info = vk::SubmitInfo::default() - .wait_semaphores(&wait_semaphores) - .wait_dst_stage_mask(&wait_stages) - .command_buffers(&command_buffers_to_submit) - .signal_semaphores(&signal_semaphores); - - let queue = self - .device - .get_device_queue(0) - .ok_or_else(|| anyhow::anyhow!("Failed to get a queue"))?; - - unsafe { - self.device - .handle - .queue_submit(*queue, &[submit_info], self.in_flight_fence.handle)? - }; - - let swapchains = [self.swapchain.handle.unwrap()]; - let indices = [index]; - let present_info = vk::PresentInfoKHR::default() - .wait_semaphores(&signal_semaphores) - .swapchains(&swapchains) - .image_indices(&indices); - - unsafe { self.device.swapchain_loader.queue_present(*queue, &present_info)? }; - - Ok(()) - } - - pub fn update_resolution(&mut self, width: u32, height: u32) -> anyhow::Result<()> { - match Arc::get_mut(&mut self.swapchain) { - Some(swapchain) => swapchain.update_resolution(width, height)?, - None => log::warn!("Impossible to get mutable swapchain"), - } - - Ok(()) - } - - pub fn exit(&self) { - unsafe { self.device.handle.device_wait_idle().unwrap() } - } - - pub fn init_scene(&self, scene: &mut Box) -> anyhow::Result<()> { - scene.init(&self.device, &self.render_pass) - } - - fn update_swapchain(&mut self) -> anyhow::Result<()> { - match Arc::get_mut(&mut self.swapchain) { - Some(swapchain) => { - swapchain.create_swapchain()?; - - self.framebuffers = self.swapchain - .create_framebuffers(&self.render_pass) - .ok_or_else(|| anyhow::anyhow!("Failed to get framebuffers"))?; - } - None => log::warn!("Impossible to get mutable swapchain"), - } - - Ok(()) - } -} diff --git a/src/renderer/vulkan/vk_render_pass.rs b/src/renderer/vulkan/vk_render_pass.rs deleted file mode 100644 index e9819fe..0000000 --- a/src/renderer/vulkan/vk_render_pass.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::{VkDevice, VkSwapchain}; -use ash::prelude::VkResult; -use ash::vk; -use std::sync::Arc; - -pub struct VkRenderPass { - device: Arc, - - pub handle: vk::RenderPass, -} - -impl VkRenderPass { - pub fn new(device: &Arc, swapchain: &Arc) -> VkResult { - let color_attachment = vk::AttachmentDescription::default() - .format(swapchain.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::PRESENT_SRC_KHR); - - let color_attachment_ref = vk::AttachmentReference::default() - .attachment(0) - .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); - - let color_attachments = [color_attachment_ref]; - let subpass = vk::SubpassDescription::default() - .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) - .color_attachments(&color_attachments); - - let attachments = [color_attachment]; - let subpasses = [subpass]; - let render_pass_info = vk::RenderPassCreateInfo::default() - .attachments(&attachments) - .subpasses(&subpasses); - - let render_pass = unsafe { device.handle.create_render_pass(&render_pass_info, None)? }; - log::debug!("Render pass created ({render_pass:?})"); - - Ok(Self { - device: device.clone(), - handle: render_pass, - }) - } -} - -impl Drop for VkRenderPass { - fn drop(&mut self) { - unsafe { - self.device.handle.destroy_render_pass(self.handle, None); - log::debug!("Render pass destroyed ({:?})", self.handle); - } - } -} diff --git a/src/renderer/vulkan/vk_semaphore.rs b/src/renderer/vulkan/vk_semaphore.rs deleted file mode 100644 index facaf4e..0000000 --- a/src/renderer/vulkan/vk_semaphore.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::VkDevice; -use ash::vk; -use std::sync::Arc; - -pub struct VkSemaphore { - device: Arc, - - pub handle: vk::Semaphore, -} - -impl VkSemaphore { - pub fn new(device: &Arc) -> anyhow::Result { - let semaphore_info = vk::SemaphoreCreateInfo::default(); - let semaphore = unsafe { device.handle.create_semaphore(&semaphore_info, None)? }; - log::debug!("Semaphore created ({semaphore:?})"); - - Ok(Self { - device: device.clone(), - handle: semaphore, - }) - } -} - -impl Drop for VkSemaphore { - fn drop(&mut self) { - unsafe { self.device.handle.destroy_semaphore(self.handle, None) }; - log::debug!("Semaphore destroyed ({:?})", self.handle); - } -} \ No newline at end of file diff --git a/src/renderer/vulkan/vk_shader_module.rs b/src/renderer/vulkan/vk_shader_module.rs deleted file mode 100644 index 96ddcc5..0000000 --- a/src/renderer/vulkan/vk_shader_module.rs +++ /dev/null @@ -1,42 +0,0 @@ -use super::VkDevice; -use ash::vk; -use std::path::Path; -use std::sync::Arc; - -pub struct VkShaderModule { - device: Arc, - - pub handle: vk::ShaderModule, -} - -impl VkShaderModule { - pub fn from_spv_file>(device: &Arc, path: P) -> anyhow::Result { - let mut file = std::fs::File::open(&path)?; - let frag_shader_str = ash::util::read_spv(&mut file)?; - - let shader_create_info = vk::ShaderModuleCreateInfo::default().code(&frag_shader_str); - let shader_module = unsafe { - device - .handle - .create_shader_module(&shader_create_info, None)? - }; - log::debug!( - "Shader module created ({shader_module:?}) from {:?}", - path.as_ref() - ); - - Ok(Self { - device: device.clone(), - handle: shader_module, - }) - } -} - -impl Drop for VkShaderModule { - fn drop(&mut self) { - unsafe { - self.device.handle.destroy_shader_module(self.handle, None); - log::debug!("Shader module destroyed ({:?})", self.handle); - } - } -} diff --git a/src/renderer/vulkan/vk_surface.rs b/src/renderer/vulkan/vk_surface.rs deleted file mode 100644 index 9aace7c..0000000 --- a/src/renderer/vulkan/vk_surface.rs +++ /dev/null @@ -1,94 +0,0 @@ -use super::{VkInstance, VkPhysicalDevice}; -use ash::prelude::VkResult; -use ash::vk; -use std::sync::Arc; -use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; - -pub struct SwapchainSupportDetails( - pub Vec, - pub vk::SurfaceCapabilitiesKHR, - pub Vec, -); - -pub struct VkSurface { - instance: Arc, - - pub handle: vk::SurfaceKHR, -} - -impl VkSurface { - pub fn new(window: &crate::display::Window, instance: Arc) -> anyhow::Result { - let window_handle = window - .handle() - .ok_or_else(|| anyhow::anyhow!("Window handle is not available."))?; - - let surface = unsafe { - ash_window::create_surface( - &instance.entry, - &instance.handle, - window_handle.display_handle()?.as_raw(), - window_handle.window_handle()?.as_raw(), - None, - )? - }; - - log::debug!("Surface created ({:?})", surface); - - Ok(Self { instance, handle: surface }) - } - - pub fn physical_device_queue_supported( - &self, - physical_device: &VkPhysicalDevice, - queue_index: u32, - ) -> VkResult { - unsafe { - self.instance - .surface_loader - .get_physical_device_surface_support( - physical_device.handle, - queue_index, - self.handle, - ) - } - } - - pub fn get_physical_device_swapchain_support_details( - &self, - physical_device: &VkPhysicalDevice, - ) -> VkResult { - unsafe { - let formats = self - .instance - .surface_loader - .get_physical_device_surface_formats(physical_device.handle, self.handle)?; - - let capabilities = self - .instance - .surface_loader - .get_physical_device_surface_capabilities(physical_device.handle, self.handle)?; - - let present_modes = self - .instance - .surface_loader - .get_physical_device_surface_present_modes(physical_device.handle, self.handle)?; - - Ok(SwapchainSupportDetails( - formats, - capabilities, - present_modes, - )) - } - } -} - -impl Drop for VkSurface { - fn drop(&mut self) { - unsafe { - self.instance - .surface_loader - .destroy_surface(self.handle, None); - } - log::debug!("Surface destroyed ({:?})", self.handle); - } -} diff --git a/src/renderer/vulkan/vk_swapchain.rs b/src/renderer/vulkan/vk_swapchain.rs deleted file mode 100644 index 758574a..0000000 --- a/src/renderer/vulkan/vk_swapchain.rs +++ /dev/null @@ -1,297 +0,0 @@ -use super::{SwapchainSupportDetails, VkDevice, VkFramebuffer, VkPhysicalDevice, VkRenderPass, VkSemaphore, VkSurface}; -use crate::display::Window; -use ash::prelude::VkResult; -use ash::vk; -use std::sync::Arc; - -pub struct VkSwapchain { - surface: Arc, - device: Arc, - - pub handle: Option, - swapchain_support_details: SwapchainSupportDetails, - - pub desired_image_count: u32, - pub surface_format: vk::SurfaceFormatKHR, - pub surface_resolution: vk::Extent2D, - pub new_requested_surface_resolution: Option, - pub present_mode: vk::PresentModeKHR, - pub pre_transform: vk::SurfaceTransformFlagsKHR, - - pub present_images: Option>, - pub present_image_views: Option>>, -} - -impl VkSwapchain { - pub fn new( - window: &Window, - surface: &Arc, - device: &Arc, - physical_device: &VkPhysicalDevice, - ) -> anyhow::Result { - log::debug!("Creating swapchain"); - - let window_size = window - .physical_size::() - .and_then(|size| { - Some(vk::Extent2D { - width: size.width, - height: size.height, - }) - }) - .ok_or_else(|| anyhow::anyhow!("Failed to get swapchain extent"))?; - log::debug!("Window size ({}x{})", window_size.width, window_size.height); - - let swapchain_support_details = - surface.get_physical_device_swapchain_support_details(physical_device)?; - let SwapchainSupportDetails(surface_formats, surface_capabilities, present_modes) = - &swapchain_support_details; - log::debug!("Supported surface formats by physical device: {surface_formats:#?}"); - log::debug!("Surface capabilities: {surface_capabilities:#?}"); - log::debug!("Present modes: {present_modes:#?}"); - - let surface_format = Self::choose_surface_format(surface_formats) - .ok_or_else(|| anyhow::anyhow!("No available surface formats"))?; - let desired_image_count = Self::choose_desired_image_count(surface_capabilities); - let swapchain_extent = Self::choose_swapchain_extent(window_size, surface_capabilities); - let pre_transform = Self::choose_pre_transform(surface_capabilities); - let present_mode = Self::choose_present_mode(present_modes); - - let mut swapchain = Self { - surface: surface.clone(), - device: device.clone(), - - handle: None, - new_requested_surface_resolution: None, - swapchain_support_details, - desired_image_count, - surface_format, - surface_resolution: swapchain_extent, - present_mode, - pre_transform, - present_images: None, - present_image_views: None, - }; - - swapchain.create_swapchain()?; - - Ok(swapchain) - } - - pub fn create_swapchain(&mut self) -> VkResult<()> { - if let Some(new_requested_surface_resolution) = self.new_requested_surface_resolution { - self.surface_resolution = new_requested_surface_resolution; - self.new_requested_surface_resolution = None; - } - - let mut swapchain_create_info = self.create_swapchain_info(&self.surface); - - if let Some(old_swapchain) = self.handle { - swapchain_create_info.old_swapchain = old_swapchain; - } - - let swapchain = unsafe { - self.device - .swapchain_loader - .create_swapchain(&swapchain_create_info, None)? - }; - - let present_images = unsafe { - self.device - .swapchain_loader - .get_swapchain_images(swapchain)? - }; - let present_images_view = present_images - .iter() - .map(|i| { - self.create_present_image_view(*i) - .expect("Failed to create image view") - }) - .map(|i| Arc::new(i)) - .collect::>(); - - if log::log_enabled!(log::Level::Debug) { - let label = match self.handle { - None => "Swapchain created", - Some(_) => "Swapchain updated", - }; - log::debug!("{label} ({swapchain:?}) : {swapchain_create_info:#?}"); - } - - self.handle = Some(swapchain); - self.present_image_views = Some(present_images_view); - self.present_images = Some(present_images); - - Ok(()) - } - - pub fn create_framebuffers( - &self, - render_pass: &Arc, - ) -> Option>> { - let present_image_views = self.present_image_views.as_ref()?; - - Some( - present_image_views - .iter() - .map(|image_view| { - VkFramebuffer::from_swapchain_image_view( - &self.device, - &render_pass, - &image_view, - &self, - ) - .unwrap() - }) - .map(|framebuffer| Arc::new(framebuffer)) - .collect::>(), - ) - } - - pub fn update_resolution(&mut self, width: u32, height: u32) -> VkResult<()> { - log::debug!("New resolution requested ({width}x{height})"); - - let chosen_extent = Self::choose_swapchain_extent( - vk::Extent2D { width, height }, - &self.swapchain_support_details.1, - ); - if chosen_extent.width != self.surface_resolution.width - || chosen_extent.height != self.surface_resolution.height - { - self.new_requested_surface_resolution = Some(chosen_extent); - log::debug!( - "New resolution submitted ({}x{})", - chosen_extent.width, - chosen_extent.height - ); - } else { - log::debug!("New resolution skipped ({width}x{height}) : Same resolution"); - } - - Ok(()) - } - - pub fn acquire_next_image(&self, semaphore: &VkSemaphore) -> VkResult<(u32, bool)> { - unsafe { - self.device.swapchain_loader.acquire_next_image( - self.handle.unwrap(), - u64::MAX, - semaphore.handle, - vk::Fence::null(), - ) - } - } - - pub fn is_dirty(&self) -> bool { - self.new_requested_surface_resolution.is_some() - } - - fn create_swapchain_info(&self, surface: &VkSurface) -> vk::SwapchainCreateInfoKHR { - vk::SwapchainCreateInfoKHR::default() - .surface(surface.handle) - .min_image_count(self.desired_image_count) - .image_color_space(self.surface_format.color_space) - .image_format(self.surface_format.format) - .image_extent(self.surface_resolution) - .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) - .image_sharing_mode(vk::SharingMode::EXCLUSIVE) - .pre_transform(self.pre_transform) - .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) - .present_mode(self.present_mode) - .clipped(true) - .image_array_layers(1) - } - - fn choose_swapchain_extent( - window_size: vk::Extent2D, - surface_capabilities: &vk::SurfaceCapabilitiesKHR, - ) -> vk::Extent2D { - vk::Extent2D { - width: window_size - .width - .max(surface_capabilities.min_image_extent.width) - .min(surface_capabilities.max_image_extent.width), - height: window_size - .height - .max(surface_capabilities.min_image_extent.height) - .min(surface_capabilities.max_image_extent.height), - } - } - - fn choose_surface_format( - surface_formats: &Vec, - ) -> Option { - surface_formats.first().and_then(|f| Some(*f)) - } - - fn choose_desired_image_count(surface_capabilities: &vk::SurfaceCapabilitiesKHR) -> u32 { - let mut desired_image_count = surface_capabilities.min_image_count + 1; - if surface_capabilities.max_image_count > 0 - && desired_image_count > surface_capabilities.max_image_count - { - desired_image_count = surface_capabilities.max_image_count; - } - desired_image_count - } - - fn choose_pre_transform( - surface_capabilities: &vk::SurfaceCapabilitiesKHR, - ) -> vk::SurfaceTransformFlagsKHR { - if surface_capabilities - .supported_transforms - .contains(vk::SurfaceTransformFlagsKHR::IDENTITY) - { - vk::SurfaceTransformFlagsKHR::IDENTITY - } else { - surface_capabilities.current_transform - } - } - - fn choose_present_mode(present_modes: &Vec) -> vk::PresentModeKHR { - present_modes - .iter() - .cloned() - .find(|&mode| mode == vk::PresentModeKHR::MAILBOX) - .unwrap_or(vk::PresentModeKHR::FIFO) - } - - fn create_present_image_view(&self, image: vk::Image) -> VkResult { - let create_view_info = vk::ImageViewCreateInfo::default() - .view_type(vk::ImageViewType::TYPE_2D) - .format(self.surface_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, - }) - .image(image); - - unsafe { - self.device - .handle - .create_image_view(&create_view_info, None) - } - } -} - -impl Drop for VkSwapchain { - fn drop(&mut self) { - if let Some(swapchain) = self.handle { - unsafe { - self.device - .swapchain_loader - .destroy_swapchain(swapchain, None); - } - self.handle = None; - log::debug!("Swapchain destroyed ({swapchain:?})"); - } - } -} diff --git a/src/scene/mod.rs b/src/scene/mod.rs deleted file mode 100644 index 46be2e9..0000000 --- a/src/scene/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod triangle; -mod vertex; - -pub use triangle::TriangleScene; diff --git a/src/scene/triangle.rs b/src/scene/triangle.rs deleted file mode 100644 index f699930..0000000 --- a/src/scene/triangle.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::renderer::vulkan::{VkDevice, VkGraphicsPipeline, VkRenderPass, VkSwapchain}; -use crate::renderer::Renderable; -use ash::vk; -use ash::vk::CommandBuffer; -use std::sync::Arc; - -pub struct TriangleScene { - pipeline: Option, -} - -impl TriangleScene { - pub fn new() -> Self { - Self { pipeline: None } - } -} - -impl Renderable for TriangleScene { - fn init(&mut self, device: &Arc, render_pass: &Arc) -> anyhow::Result<()> { - let pipeline = VkGraphicsPipeline::new(&device, &render_pass)?; - self.pipeline = Some(pipeline); - - Ok(()) - } - - fn render(&self, device: &VkDevice, swapchain: &VkSwapchain, command_buffer: &CommandBuffer) -> anyhow::Result<()> { - unsafe { - device.handle.cmd_bind_pipeline( - *command_buffer, - vk::PipelineBindPoint::GRAPHICS, - self.pipeline.as_ref().unwrap().pipeline, - ) - }; - - let viewport = vk::Viewport::default() - .width(swapchain.surface_resolution.width as f32) - .height(swapchain.surface_resolution.height as f32) - .max_depth(1.0); - - unsafe { device.handle.cmd_set_viewport(*command_buffer, 0, &[viewport]) } - - let scissor = swapchain.surface_resolution.into(); - - unsafe { device.handle.cmd_set_scissor(*command_buffer, 0, &[scissor]) } - - unsafe { device.handle.cmd_draw(*command_buffer, 3, 1, 0, 0) }; - - Ok(()) - } -} \ No newline at end of file diff --git a/src/scene/vertex.rs b/src/scene/vertex.rs deleted file mode 100644 index 0384d0c..0000000 --- a/src/scene/vertex.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::sync::Arc; -use ash::prelude::VkResult; -use ash::vk; -use crate::renderer::vulkan::{Vertex, VkBuffer, VkDevice}; - -#[derive(Default)] -struct VertexScene { - vertices: Vec, - - vertices_buffer: Option, -} - -impl VertexScene { - pub fn new() -> Self { - let vertices = vec![ - Vertex::new([0.0, -0.5], [1.0, 0.0, 0.0]), - Vertex::new([0.5, 0.5], [0.0, 1.0, 0.0]), - Vertex::new([-0.5, 0.5], [0.0, 0.0, 1.0]), - ]; - - Self { - vertices, - ..Default::default() - } - } - - - fn create_buffer(&mut self, device: &Arc) -> VkResult<()> { - let buffer_info = vk::BufferCreateInfo::default() - .usage(vk::BufferUsageFlags::VERTEX_BUFFER) - .sharing_mode(vk::SharingMode::EXCLUSIVE) - .size(self.vertices.len() as u64 * size_of::() as u64); - - let buffer = VkBuffer::new(device, &buffer_info)?; - self.vertices_buffer = Some(buffer); - - Ok(()) - } -} \ No newline at end of file From 9256da20944e3413dbc72bd9aeee08e4b9426af7 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 8 Dec 2024 18:20:36 +0100 Subject: [PATCH 02/91] Use rwh_06 with winit --- Cargo.lock | 14 +++----------- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7092bb5..d457b93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -662,8 +662,7 @@ dependencies = [ "log", "ndk-sys", "num_enum", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", + "raw-window-handle", "thiserror", ] @@ -1052,12 +1051,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -1472,7 +1465,7 @@ dependencies = [ "parking_lot", "proc-macro2", "quote", - "raw-window-handle 0.6.2", + "raw-window-handle", "raw-window-metal", "serde", "serde_json", @@ -1966,8 +1959,7 @@ dependencies = [ "orbclient", "percent-encoding", "pin-project", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", + "raw-window-handle", "redox_syscall 0.4.1", "rustix", "sctk-adwaita", diff --git a/Cargo.toml b/Cargo.toml index 3cf3338..9ad5563 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] anyhow = "1.0" -winit = { version = "0.30", features = ["rwh_05"] } +winit = { version = "0.30", features = ["rwh_06"] } vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } From e58a3573816e1af6a9637e126bc84e144f6d1630 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 8 Dec 2024 18:45:49 +0100 Subject: [PATCH 03/91] Add 4 triangle --- src/renderer/app.rs | 2 +- src/renderer/render_context.rs | 5 +-- src/renderer/scene.rs | 60 ++++++++++++++++++++++++++++++---- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/renderer/app.rs b/src/renderer/app.rs index 438f946..55c6a31 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -12,7 +12,7 @@ use vulkano::sync::GpuFuture; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; -use winit::window::{Window, WindowId}; +use winit::window::WindowId; use crate::renderer::render_context::RenderContext; use crate::renderer::{window_size_dependent_setup, Scene}; diff --git a/src/renderer/render_context.rs b/src/renderer/render_context.rs index cd2bc24..6ae9301 100644 --- a/src/renderer/render_context.rs +++ b/src/renderer/render_context.rs @@ -1,11 +1,8 @@ use std::sync::Arc; use vulkano::device::Device; -use vulkano::image::{Image, ImageUsage}; +use vulkano::image::ImageUsage; use vulkano::image::view::ImageView; -use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::pipeline::GraphicsPipeline; -use vulkano::render_pass::{Framebuffer, RenderPass}; use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use vulkano::sync; use vulkano::sync::GpuFuture; diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs index 174ba7e..f982963 100644 --- a/src/renderer/scene.rs +++ b/src/renderer/scene.rs @@ -164,17 +164,60 @@ impl Scene { // We now create a buffer that will store the shape of our triangle. let vertices = [ + // Triangle en haut à gauche MyVertex { - position: [-0.5, -0.25], - color: [1.0, 0.0, 0.0], + position: [-0.5, -0.75], // Coin supérieur gauche + color: [1.0, 0.0, 0.0], // Couleur rouge }, MyVertex { - position: [0.0, 0.5], - color: [0.0, 1.0, 0.0], + position: [-0.75, -0.25], // Coin supérieur + color: [0.0, 1.0, 0.0], // Couleur verte }, MyVertex { - position: [0.25, -0.1], - color: [0.0, 0.0, 1.0], + position: [-0.25, -0.25], // Coin supérieur droit + color: [0.0, 0.0, 1.0], // Couleur bleue + }, + + // Triangle en bas à gauche + MyVertex { + position: [-0.5, 0.25], // Coin inférieur gauche + color: [0.5, 0.5, 0.5], // Couleur gris + }, + MyVertex { + position: [-0.75, 0.75], // Coin inférieur + color: [0.2, 0.8, 0.2], // Couleur vert clair + }, + MyVertex { + position: [-0.25, 0.75], // Coin inférieur droit + color: [0.8, 0.2, 0.2], // Couleur rouge clair + }, + + // Triangle en haut à droite + MyVertex { + position: [0.5, -0.75], // Coin gauche supérieur + color: [1.0, 1.0, 0.0], // Couleur jaune + }, + MyVertex { + position: [0.25, -0.25], // Coin gauche + color: [0.0, 1.0, 1.0], // Couleur cyan + }, + MyVertex { + position: [0.75, -0.25], // Coin gauche inférieur + color: [1.0, 0.0, 1.0], // Couleur magenta + }, + + // Triangle en bas à droite + MyVertex { + position: [0.5, 0.25], // Coin droit supérieur + color: [0.1, 0.5, 0.8], // Couleur bleu clair + }, + MyVertex { + position: [0.25, 0.75], // Coin droit + color: [0.8, 0.6, 0.1], // Couleur or + }, + MyVertex { + position: [0.75, 0.75], // Coin droit inférieur + color: [0.3, 0.4, 0.6], // Couleur bleu foncé }, ]; let vertex_buffer = Buffer::from_iter( @@ -205,6 +248,9 @@ impl Scene { .unwrap(); // We add a draw command. - unsafe { builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0) }.unwrap(); + let vertex_count = self.vertex_buffer.len() as u32; + let instance_count = vertex_count / 3; + + unsafe { builder.draw(vertex_count, instance_count, 0, 0) }.unwrap(); } } \ No newline at end of file From 6743fe8fddf02cc09ec4aad33dd59070fdb91d03 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Dec 2024 14:03:40 +0100 Subject: [PATCH 04/91] Remove comment --- src/renderer/app.rs | 141 --------------------------------- src/renderer/render_context.rs | 55 +------------ src/renderer/scene.rs | 64 --------------- 3 files changed, 1 insertion(+), 259 deletions(-) diff --git a/src/renderer/app.rs b/src/renderer/app.rs index 55c6a31..fdf3ce2 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -30,16 +30,8 @@ impl App { pub fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - // The first step of any Vulkan program is to create an instance. - // - // When we create an instance, we have to pass a list of extensions that we want to enable. - // - // All the window-drawing functionalities are part of non-core extensions that we need to - // enable manually. To do so, we ask `Surface` for the list of extensions required to draw - // to a window. let required_extensions = Surface::required_extensions(event_loop).unwrap(); - // Now creating the instance. let instance = Instance::new( library, InstanceCreateInfo { @@ -57,69 +49,31 @@ impl App { ) .unwrap(); - // Choose device extensions that we're going to use. In order to present images to a - // surface, we need a `Swapchain`, which is provided by the `khr_swapchain` extension. let mut device_extensions = DeviceExtensions { khr_swapchain: true, ..DeviceExtensions::empty() }; - // We then choose which physical device to use. First, we enumerate all the available - // physical devices, then apply filters to narrow them down to those that can support our - // needs. let (physical_device, queue_family_index) = instance .enumerate_physical_devices() .unwrap() .filter(|p| { - // For this example, we require at least Vulkan 1.3, or a device that has the - // `khr_dynamic_rendering` extension available. p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering }) .filter(|p| { - // Some devices may not support the extensions or features that your application, - // or report properties and limits that are not sufficient for your application. - // These should be filtered out here. p.supported_extensions().contains(&device_extensions) }) .filter_map(|p| { - // For each physical device, we try to find a suitable queue family that will - // execute our draw commands. - // - // Devices can provide multiple queues to run commands in parallel (for example a - // draw queue and a compute queue), similar to CPU threads. This is something you - // have to have to manage manually in Vulkan. Queues of the same type belong to the - // same queue family. - // - // Here, we look for a single queue family that is suitable for our purposes. In a - // real-world application, you may want to use a separate dedicated transfer queue - // to handle data transfers in parallel with graphics operations. You may also need - // a separate queue for compute operations, if your application uses those. p.queue_family_properties() .iter() .enumerate() .position(|(i, q)| { - // We select a queue family that supports graphics operations. When drawing - // to a window surface, as we do in this example, we also need to check - // that queues in this queue family are capable of presenting images to the - // surface. q.queue_flags.intersects(QueueFlags::GRAPHICS) && p.presentation_support(i as u32, event_loop).unwrap() }) - // The code here searches for the first queue family that is suitable. If none - // is found, `None` is returned to `filter_map`, which disqualifies this - // physical device. .map(|i| (p, i as u32)) }) - // All the physical devices that pass the filters above are suitable for the - // application. However, not every device is equal, some are preferred over others. - // Now, we assign each physical device a score, and pick the device with the lowest - // ("best") score. - // - // In this example, we simply select the best-scoring device to use in the application. - // In a real-world setting, you may want to use the best-scoring device only as a - // "default" or "recommended" device, and let the user choose the device themself. .min_by_key(|(p, _)| { - // We assign a lower score to device types that are likely to be faster/better. match p.properties().device_type { PhysicalDeviceType::DiscreteGpu => 0, PhysicalDeviceType::IntegratedGpu => 1, @@ -131,68 +85,36 @@ impl App { }) .expect("no suitable physical device found"); - // Some little debug infos. println!( "Using device: {} (type: {:?})", physical_device.properties().device_name, physical_device.properties().device_type, ); - // If the selected device doesn't have Vulkan 1.3 available, then we need to enable the - // `khr_dynamic_rendering` extension manually. This extension became a core part of Vulkan - // in version 1.3 and later, so it's always available then and it does not need to be - // enabled. We can be sure that this extension will be available on the selected physical - // device, because we filtered out unsuitable devices in the device selection code above. if physical_device.api_version() < Version::V1_3 { device_extensions.khr_dynamic_rendering = true; } - // Now initializing the device. This is probably the most important object of Vulkan. - // - // An iterator of created queues is returned by the function alongside the device. let (device, mut queues) = Device::new( - // Which physical device to connect to. physical_device, DeviceCreateInfo { - // The list of queues that we are going to use. Here we only use one queue, from - // the previously chosen queue family. queue_create_infos: vec![QueueCreateInfo { queue_family_index, ..Default::default() }], - - // A list of optional features and extensions that our program needs to work - // correctly. Some parts of the Vulkan specs are optional and must be enabled - // manually at device creation. In this example the only things we are going to - // need are the `khr_swapchain` extension that allows us to draw to a window, and - // `khr_dynamic_rendering` if we don't have Vulkan 1.3 available. enabled_extensions: device_extensions, - - // In order to render with Vulkan 1.3's dynamic rendering, we need to enable it - // here. Otherwise, we are only allowed to render with a render pass object, as in - // the standard triangle example. The feature is required to be supported by the - // device if it supports Vulkan 1.3 and higher, or if the `khr_dynamic_rendering` - // extension is available, so we don't need to check for support. enabled_features: DeviceFeatures { dynamic_rendering: true, ..DeviceFeatures::empty() }, - ..Default::default() }, ) .unwrap(); - // Since we can request multiple queues, the `queues` variable is in fact an iterator. We - // only use one queue in this example, so we just retrieve the first and only element of - // the iterator. let queue = queues.next().unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - // Before we can start creating and recording command buffers, we need a way of allocating - // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command - // pools underneath and provides a safe interface for them. let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( device.clone(), Default::default(), @@ -245,21 +167,12 @@ impl ApplicationHandler for App { WindowEvent::RedrawRequested => { let window_size = rcx.window.inner_size(); - // Do not draw the frame when the screen size is zero. On Windows, this can occur - // when minimizing the application. if window_size.width == 0 || window_size.height == 0 { return; } - // It is important to call this function from time to time, otherwise resources - // will keep accumulating and you will eventually reach an out of memory error. - // Calling this function polls various fences in order to determine what the GPU - // has already processed, and frees the resources that are no longer needed. rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - // Whenever the window resizes we need to recreate everything dependent on the - // window size. In this example that includes the swapchain, the framebuffers and - // the dynamic state viewport. if rcx.recreate_swapchain { let (new_swapchain, new_images) = rcx .swapchain @@ -270,23 +183,11 @@ impl ApplicationHandler for App { .expect("failed to recreate swapchain"); rcx.swapchain = new_swapchain; - - // Now that we have new swapchain images, we must create new image views from - // them as well. rcx.attachment_image_views = window_size_dependent_setup(&new_images); - rcx.viewport.extent = window_size.into(); - rcx.recreate_swapchain = false; } - // Before we can draw on the output, we have to *acquire* an image from the - // swapchain. If no image is available (which happens if you submit draw commands - // too quickly), then the function will block. This operation returns the index of - // the image that we are allowed to draw upon. - // - // This function can block if no image is available. The parameter is an optional - // timeout after which the function call will return an error. let (image_index, suboptimal, acquire_future) = match acquire_next_image( rcx.swapchain.clone(), None, @@ -301,23 +202,10 @@ impl ApplicationHandler for App { Err(e) => panic!("failed to acquire next image: {e}"), }; - // `acquire_next_image` can be successful, but suboptimal. This means that the - // swapchain image will still work, but it may not display correctly. With some - // drivers this can be when the window resizes, but it may not cause the swapchain - // to become out of date. if suboptimal { rcx.recreate_swapchain = true; } - // In order to draw, we have to record a *command buffer*. The command buffer - // object holds the list of commands that are going to be executed. - // - // Recording a command buffer is an expensive operation (usually a few hundred - // microseconds), but it is known to be a hot path in the driver and is expected to - // be optimized. - // - // Note that we have to pass a queue family when we create the command buffer. The - // command buffer will only be executable on that given queue family. let mut builder = AutoCommandBufferBuilder::primary( self.command_buffer_allocator.clone(), self.queue.queue_family_index(), @@ -326,37 +214,18 @@ impl ApplicationHandler for App { .unwrap(); builder - // Before we can draw, we have to *enter a render pass*. We specify which - // attachments we are going to use for rendering here, which needs to match - // what was previously specified when creating the pipeline. .begin_rendering(RenderingInfo { - // As before, we specify one color attachment, but now we specify the image - // view to use as well as how it should be used. color_attachments: vec![Some(RenderingAttachmentInfo { - // `Clear` means that we ask the GPU to clear the content of this - // attachment at the start of rendering. load_op: AttachmentLoadOp::Clear, - // `Store` means that we ask the GPU to store the rendered output in - // the attachment image. We could also ask it to discard the result. store_op: AttachmentStoreOp::Store, - // The value to clear the attachment with. Here we clear it with a blue - // color. - // - // Only attachments that have `AttachmentLoadOp::Clear` are provided - // with clear values, any others should use `None` as the clear value. clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), ..RenderingAttachmentInfo::image_view( - // We specify image view corresponding to the currently acquired - // swapchain image, to use for this attachment. rcx.attachment_image_views[image_index as usize].clone(), ) })], ..Default::default() }) .unwrap() - // We are now inside the first subpass of the render pass. - // - // TODO: Document state setting and how it affects subsequent draw commands. .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap(); @@ -365,11 +234,9 @@ impl ApplicationHandler for App { } builder - // We leave the render pass. .end_rendering() .unwrap(); - // Finish recording the command buffer by calling `end`. let command_buffer = builder.build().unwrap(); let future = rcx @@ -379,14 +246,6 @@ impl ApplicationHandler for App { .join(acquire_future) .then_execute(self.queue.clone(), command_buffer) .unwrap() - // The color output is now expected to contain our triangle. But in order to - // show it on the screen, we have to *present* the image by calling - // `then_swapchain_present`. - // - // This function does not actually present the image immediately. Instead it - // submits a present command at the end of the queue. This means that it will - // only be presented once the GPU has finished executing the command buffer - // that draws the triangle. .then_swapchain_present( self.queue.clone(), SwapchainPresentInfo::swapchain_image_index( diff --git a/src/renderer/render_context.rs b/src/renderer/render_context.rs index 6ae9301..097479e 100644 --- a/src/renderer/render_context.rs +++ b/src/renderer/render_context.rs @@ -22,56 +22,26 @@ impl RenderContext { pub fn new(window: Arc, surface: Arc, device: &Arc) -> Self { let window_size = window.inner_size(); - - // Before we can draw on the surface, we have to create what is called a swapchain. - // Creating a swapchain allocates the color buffers that will contain the image that will - // ultimately be visible on the screen. These images are returned alongside the swapchain. let (swapchain, images) = { - // Querying the capabilities of the surface. When we create the swapchain we can only - // pass values that are allowed by the capabilities. let surface_capabilities = device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - // Choosing the internal format that the images will have. let (image_format, _) = device .physical_device() .surface_formats(&surface, Default::default()) .unwrap()[0]; - // Please take a look at the docs for the meaning of the parameters we didn't mention. Swapchain::new( device.clone(), surface, SwapchainCreateInfo { - // Some drivers report an `min_image_count` of 1, but fullscreen mode requires - // at least 2. Therefore we must ensure the count is at least 2, otherwise the - // program would crash when entering fullscreen mode on those drivers. + // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - - // The size of the window, only used to initially setup the swapchain. - // - // NOTE: - // On some drivers the swapchain extent is specified by - // `surface_capabilities.current_extent` and the swapchain size must use this - // extent. This extent is always the same as the window size. - // - // However, other drivers don't specify a value, i.e. - // `surface_capabilities.current_extent` is `None`. These drivers will allow - // anything, but the only sensible value is the window size. - // - // Both of these cases need the swapchain to use the window size, so we just - // use that. image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - - // The alpha mode indicates how the alpha value of the final image will behave. - // For example, you can choose whether the window will be opaque or - // transparent. composite_alpha: surface_capabilities .supported_composite_alpha .into_iter() @@ -84,38 +54,15 @@ impl RenderContext { .unwrap() }; - // When creating the swapchain, we only created plain images. To use them as an attachment - // for rendering, we must wrap then in an image view. - // - // Since we need to draw to multiple images, we are going to create a different image view - // for each image. let attachment_image_views = window_size_dependent_setup(&images); - // Dynamic viewports allow us to recreate just the viewport when the window is resized. - // Otherwise we would have to recreate the whole pipeline. let viewport = Viewport { offset: [0.0, 0.0], extent: window_size.into(), depth_range: 0.0..=1.0, }; - // In some situations, the swapchain will become invalid by itself. This includes for - // example when the window is resized (as the images of the swapchain will no longer match - // the window's) or, on Android, when the application went to the background and goes back - // to the foreground. - // - // In this situation, acquiring a swapchain image or presenting it will return an error. - // Rendering to an image of that swapchain will not produce any error, but may or may not - // work. To continue rendering, we need to recreate the swapchain by creating a new - // swapchain. Here, we remember that we need to do this for the next loop iteration. let recreate_swapchain = false; - - // In the loop below we are going to submit commands to the GPU. Submitting a command - // produces an object that implements the `GpuFuture` trait, which holds the resources for - // as long as they are in use by the GPU. - // - // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to - // avoid that, we store the submission of the previous frame here. let previous_frame_end = Some(sync::now(device.clone()).boxed()); Self { diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs index f982963..5b76973 100644 --- a/src/renderer/scene.rs +++ b/src/renderer/scene.rs @@ -34,21 +34,6 @@ pub struct Scene { impl Scene { pub fn initialize(device: &Arc, swapchain: &Arc, memory_allocator: &Arc) -> Scene { - // The next step is to create the shaders. - // - // The raw shader creation API provided by the vulkano library is unsafe for various - // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL - // source - in the example below, the source is provided as a string input directly to the - // shader, but a path to a source file can be provided as well. Note that the user must - // specify the type of shader (e.g. "vertex", "fragment", etc.) using the `ty` option of - // the macro. - // - // The items generated by the `shader!` macro include a `load` function which loads the - // shader using an input logical device. The module also includes type definitions for - // layout structures defined in the shader source, for example uniforms and push constants. - // - // A more detailed overview of what the `shader!` macro generates can be found in the - // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ mod vs { vulkano_shaders::shader! { ty: "vertex", @@ -63,16 +48,7 @@ impl Scene { } } - // Before we draw, we have to create what is called a **pipeline**. A pipeline describes - // how a GPU operation is to be performed. It is similar to an OpenGL program, but it also - // contains many settings for customization, all baked into a single object. For drawing, - // we create a **graphics** pipeline, but there are also other types of pipeline. let pipeline = { - // First, we load the shaders that the pipeline will use: the vertex shader and the - // fragment shader. - // - // A Vulkan shader can in theory contain multiple entry points, so we have to specify - // which one. let vs = vs::load(device.clone()) .unwrap() .entry_point("main") @@ -82,78 +58,40 @@ impl Scene { .entry_point("main") .unwrap(); - // Automatically generate a vertex input state from the vertex shader's input - // interface, that takes a single vertex buffer containing `Vertex` structs. let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - // Make a list of the shader stages that the pipeline will have. let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; - // We must now create a **pipeline layout** object, which describes the locations and - // types of descriptor sets and push constants used by the shaders in the pipeline. - // - // Multiple pipelines can share a common layout object, which is more efficient. The - // shaders in a pipeline must use a subset of the resources described in its pipeline - // layout, but the pipeline layout is allowed to contain resources that are not present - // in the shaders; they can be used by shaders in other pipelines that share the same - // layout. Thus, it is a good idea to design shaders so that many pipelines have common - // resource locations, which allows them to share pipeline layouts. let layout = PipelineLayout::new( device.clone(), - // Since we only have one pipeline in this example, and thus one pipeline layout, - // we automatically generate the creation info for it from the resources used in - // the shaders. In a real application, you would specify this information manually - // so that you can re-use one layout in multiple pipelines. PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) .into_pipeline_layout_create_info(device.clone()) .unwrap(), ) .unwrap(); - // We describe the formats of attachment images where the colors, depth and/or stencil - // information will be written. The pipeline will only be usable with this particular - // configuration of the attachment images. let subpass = PipelineRenderingCreateInfo { - // We specify a single color attachment that will be rendered to. When we begin - // rendering, we will specify a swapchain image to be used as this attachment, so - // here we set its format to be the same format as the swapchain. color_attachment_formats: vec![Some(swapchain.image_format())], ..Default::default() }; - // Finally, create the pipeline. GraphicsPipeline::new( device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), - // How vertex data is read from the vertex buffers into the vertex shader. vertex_input_state: Some(vertex_input_state), - // How vertices are arranged into primitive shapes. The default primitive shape - // is a triangle. input_assembly_state: Some(InputAssemblyState::default()), - // How primitives are transformed and clipped to fit the framebuffer. We use a - // resizable viewport, set to draw over the entire window. viewport_state: Some(ViewportState::default()), - // How polygons are culled and converted into a raster of pixels. The default - // value does not perform any culling. rasterization_state: Some(RasterizationState::default()), - // How multiple fragment shader samples are converted to a single pixel value. - // The default value does not perform any multisampling. multisample_state: Some(MultisampleState::default()), - // How pixel values are combined with the values already present in the - // framebuffer. The default value overwrites the old value with the new one, - // without any blending. color_blend_state: Some(ColorBlendState::with_attachment_states( subpass.color_attachment_formats.len() as u32, ColorBlendAttachmentState::default(), )), - // Dynamic states allows us to specify parts of the pipeline settings when - // recording the command buffer, before we perform drawing. Here, we specify - // that the viewport should be dynamic. dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), ..GraphicsPipelineCreateInfo::layout(layout) @@ -162,7 +100,6 @@ impl Scene { .unwrap() }; - // We now create a buffer that will store the shape of our triangle. let vertices = [ // Triangle en haut à gauche MyVertex { @@ -247,7 +184,6 @@ impl Scene { .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); - // We add a draw command. let vertex_count = self.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; From f65f45fc9aac6db09922994f7677bd2979053b75 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Dec 2024 19:30:36 +0100 Subject: [PATCH 05/91] Add configuration for vscode --- .vscode/extensions.json | 7 +++++++ .vscode/launch.json | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a6bd770 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "pinage404.rust-extension-pack", + "vadimcn.vscode-lldb", + "jnoortheen.nix-ide" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c0caf03 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'rust_vulkan_test'", + "cargo": { + "args": [ + "build", + "--bin=rust_vulkan_test", + "--package=rust_vulkan_test" + ], + "filter": { + "name": "rust_vulkan_test", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'rust_vulkan_test'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=rust_vulkan_test", + "--package=rust_vulkan_test" + ], + "filter": { + "name": "rust_vulkan_test", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file From 1169c76b41698a9cba263700f784fcb8b16302ba Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Dec 2024 20:16:38 +0100 Subject: [PATCH 06/91] Refactorisation de la gestion des pipelines --- src/renderer/mod.rs | 5 +- src/renderer/pipelines/mod.rs | 2 + src/renderer/pipelines/triangle_pipeline.rs | 85 +++++++++ src/renderer/scene.rs | 188 +++++--------------- src/renderer/vertex.rs | 18 ++ 5 files changed, 155 insertions(+), 143 deletions(-) create mode 100644 src/renderer/pipelines/mod.rs create mode 100644 src/renderer/pipelines/triangle_pipeline.rs create mode 100644 src/renderer/vertex.rs diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index e72d412..d056eb7 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,15 +1,18 @@ mod render_context; mod app; +mod vertex; +mod pipelines; pub use app::App; +pub use pipelines::create_triangle_pipeline; mod scene; pub use scene::Scene; +pub use vertex::Vertex2D; use std::sync::Arc; use vulkano::image::Image; use vulkano::image::view::ImageView; - /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup(images: &[Arc]) -> Vec> { images diff --git a/src/renderer/pipelines/mod.rs b/src/renderer/pipelines/mod.rs new file mode 100644 index 0000000..db588d1 --- /dev/null +++ b/src/renderer/pipelines/mod.rs @@ -0,0 +1,2 @@ +mod triangle_pipeline; +pub use triangle_pipeline::create_triangle_pipeline; \ No newline at end of file diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/renderer/pipelines/triangle_pipeline.rs new file mode 100644 index 0000000..e9de828 --- /dev/null +++ b/src/renderer/pipelines/triangle_pipeline.rs @@ -0,0 +1,85 @@ +use std::sync::Arc; +use vulkano::device::Device; +use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; +use vulkano::pipeline::{DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo}; +use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; +use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; +use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; +use vulkano::pipeline::graphics::multisample::MultisampleState; +use vulkano::pipeline::graphics::rasterization::RasterizationState; +use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; +use vulkano::pipeline::graphics::viewport::ViewportState; +use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; +use vulkano::swapchain::Swapchain; + +use crate::renderer::Vertex2D; + +mod shaders { + pub mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/vertex.vert", + } + } + + pub mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/vertex.frag", + } + } +} + +pub fn create_triangle_pipeline(device: &Arc, swapchain: &Arc) -> Arc { + let vs = shaders::vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = shaders::fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + + let vertex_input_state = Vertex2D::per_vertex() + .definition(&vs) + .unwrap(); + + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + let layout = PipelineLayout::new( + device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(device.clone()) + .unwrap(), + ) + .unwrap(); + + let subpass = PipelineRenderingCreateInfo { + color_attachment_formats: vec![Some(swapchain.image_format())], + ..Default::default() + }; + + GraphicsPipeline::new( + device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() +} diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs index 5b76973..f8fe8aa 100644 --- a/src/renderer/scene.rs +++ b/src/renderer/scene.rs @@ -1,163 +1,57 @@ use std::sync::Arc; -use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}; +use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; use vulkano::device::Device; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; -use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; -use vulkano::pipeline::{DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo}; -use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; -use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; -use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; -use vulkano::pipeline::graphics::multisample::MultisampleState; -use vulkano::pipeline::graphics::rasterization::RasterizationState; -use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; -use vulkano::pipeline::graphics::viewport::ViewportState; -use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; +use vulkano::pipeline::GraphicsPipeline; use vulkano::swapchain::Swapchain; -// We use `#[repr(C)]` here to force rustc to use a defined layout for our data, as the default -// representation has *no guarantees*. -#[derive(BufferContents, Vertex)] -#[repr(C)] -struct MyVertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - - #[format(R32G32B32_SFLOAT)] - color: [f32; 3], -} +use crate::renderer::{Vertex2D, create_triangle_pipeline}; pub struct Scene { pipeline: Arc, - vertex_buffer: Subbuffer<[MyVertex]>, + vertex_buffer: Subbuffer<[Vertex2D]>, } impl Scene { - pub fn initialize(device: &Arc, swapchain: &Arc, memory_allocator: &Arc) -> Scene { - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - path: r"res/shaders/vertex.vert", - } - } - - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - path: r"res/shaders/vertex.frag", - } - } - - let pipeline = { - let vs = vs::load(device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), - ) - .unwrap(); - - let subpass = PipelineRenderingCreateInfo { - color_attachment_formats: vec![Some(swapchain.image_format())], - ..Default::default() - }; - - GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.color_attachment_formats.len() as u32, - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - + fn create_vertex_buffer( + memory_allocator: &Arc, + ) -> Subbuffer<[Vertex2D]> { let vertices = [ // Triangle en haut à gauche - MyVertex { - position: [-0.5, -0.75], // Coin supérieur gauche - color: [1.0, 0.0, 0.0], // Couleur rouge - }, - MyVertex { - position: [-0.75, -0.25], // Coin supérieur - color: [0.0, 1.0, 0.0], // Couleur verte - }, - MyVertex { - position: [-0.25, -0.25], // Coin supérieur droit - color: [0.0, 0.0, 1.0], // Couleur bleue - }, + Vertex2D::new([-0.5, -0.75], // Coin supérieur gauche + [1.0, 0.0, 0.0]), // Couleur rouge + Vertex2D::new([-0.75, -0.25], // Coin supérieur + [0.0, 1.0, 0.0]), // Couleur verte + Vertex2D::new([-0.25, -0.25], // Coin supérieur droit + [0.0, 0.0, 1.0]), // Couleur bleue // Triangle en bas à gauche - MyVertex { - position: [-0.5, 0.25], // Coin inférieur gauche - color: [0.5, 0.5, 0.5], // Couleur gris - }, - MyVertex { - position: [-0.75, 0.75], // Coin inférieur - color: [0.2, 0.8, 0.2], // Couleur vert clair - }, - MyVertex { - position: [-0.25, 0.75], // Coin inférieur droit - color: [0.8, 0.2, 0.2], // Couleur rouge clair - }, + Vertex2D::new([-0.5, 0.25], // Coin inférieur gauche + [0.5, 0.5, 0.5]), // Couleur gris + Vertex2D::new([-0.75, 0.75], // Coin inférieur + [0.2, 0.8, 0.2]), // Couleur vert clair + Vertex2D::new([-0.25, 0.75], // Coin inférieur droit + [0.8, 0.2, 0.2]), // Couleur rouge clair // Triangle en haut à droite - MyVertex { - position: [0.5, -0.75], // Coin gauche supérieur - color: [1.0, 1.0, 0.0], // Couleur jaune - }, - MyVertex { - position: [0.25, -0.25], // Coin gauche - color: [0.0, 1.0, 1.0], // Couleur cyan - }, - MyVertex { - position: [0.75, -0.25], // Coin gauche inférieur - color: [1.0, 0.0, 1.0], // Couleur magenta - }, + Vertex2D::new([0.5, -0.75], // Coin gauche supérieur + [1.0, 1.0, 0.0]), // Couleur jaune + Vertex2D::new([0.25, -0.25], // Coin gauche + [0.0, 1.0, 1.0]), // Couleur cyan + Vertex2D::new([0.75, -0.25], // Coin gauche inférieur + [1.0, 0.0, 1.0]), // Couleur magenta // Triangle en bas à droite - MyVertex { - position: [0.5, 0.25], // Coin droit supérieur - color: [0.1, 0.5, 0.8], // Couleur bleu clair - }, - MyVertex { - position: [0.25, 0.75], // Coin droit - color: [0.8, 0.6, 0.1], // Couleur or - }, - MyVertex { - position: [0.75, 0.75], // Coin droit inférieur - color: [0.3, 0.4, 0.6], // Couleur bleu foncé - }, + Vertex2D::new([0.5, 0.25], // Coin droit supérieur + [0.1, 0.5, 0.8]), // Couleur bleu clair + Vertex2D::new([0.25, 0.75], // Coin droit + [0.8, 0.6, 0.1]), // Couleur or + Vertex2D::new([0.75, 0.75], // Coin droit inférieur + [0.3, 0.4, 0.6]), // Couleur bleu foncé ]; - let vertex_buffer = Buffer::from_iter( + + Buffer::from_iter( memory_allocator.clone(), BufferCreateInfo { usage: BufferUsage::VERTEX_BUFFER, @@ -170,16 +64,26 @@ impl Scene { }, vertices, ) - .unwrap(); + .unwrap() + } - Self { + pub fn initialize( + device: &Arc, + swapchain: &Arc, + memory_allocator: &Arc, + ) -> Scene { + let pipeline = create_triangle_pipeline(device, swapchain); + let vertex_buffer = Self::create_vertex_buffer(memory_allocator); + + Scene { pipeline, vertex_buffer, } } pub fn render(&self, builder: &mut AutoCommandBufferBuilder) { - builder.bind_pipeline_graphics(self.pipeline.clone()) + builder + .bind_pipeline_graphics(self.pipeline.clone()) .unwrap() .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); diff --git a/src/renderer/vertex.rs b/src/renderer/vertex.rs new file mode 100644 index 0000000..8eb4ff5 --- /dev/null +++ b/src/renderer/vertex.rs @@ -0,0 +1,18 @@ +use vulkano::buffer::BufferContents; +use vulkano::pipeline::graphics::vertex_input::Vertex; + +#[derive(BufferContents, Vertex)] +#[repr(C)] +pub struct Vertex2D { + #[format(R32G32_SFLOAT)] + pub position: [f32; 2], + + #[format(R32G32B32_SFLOAT)] + pub color: [f32; 3], +} + +impl Vertex2D { + pub fn new(position: [f32; 2], color: [f32; 3]) -> Self { + Self { position, color } + } +} From 11a5083513fa23c25d72da7ed3b29b2eae1989bc Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Dec 2024 20:41:04 +0100 Subject: [PATCH 07/91] Refactor vertex buffer creations --- src/renderer/app.rs | 60 +++++----- src/renderer/mod.rs | 6 +- src/renderer/pipelines/mod.rs | 2 +- src/renderer/pipelines/triangle_pipeline.rs | 21 ++-- src/renderer/render_context.rs | 6 +- src/renderer/scene.rs | 119 ++++++++++---------- src/renderer/vertex.rs | 26 ++++- 7 files changed, 132 insertions(+), 108 deletions(-) diff --git a/src/renderer/app.rs b/src/renderer/app.rs index fdf3ce2..784e6ce 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -1,20 +1,24 @@ +use crate::renderer::render_context::RenderContext; +use crate::renderer::{window_size_dependent_setup, Scene}; use std::sync::Arc; use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; -use vulkano::device::{Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags}; +use vulkano::command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, +}; use vulkano::device::physical::PhysicalDeviceType; +use vulkano::device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, +}; use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; use vulkano::memory::allocator::StandardMemoryAllocator; -use vulkano::swapchain::{acquire_next_image, Surface, SwapchainCreateInfo, SwapchainPresentInfo}; -use vulkano::{sync, Validated, Version, VulkanError, VulkanLibrary}; -use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; +use vulkano::swapchain::{acquire_next_image, Surface, SwapchainCreateInfo, SwapchainPresentInfo}; use vulkano::sync::GpuFuture; +use vulkano::{sync, Validated, Version, VulkanError, VulkanLibrary}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::window::WindowId; -use crate::renderer::render_context::RenderContext; -use crate::renderer::{window_size_dependent_setup, Scene}; pub struct App { instance: Arc, @@ -47,7 +51,7 @@ impl App { ..Default::default() }, ) - .unwrap(); + .unwrap(); let mut device_extensions = DeviceExtensions { khr_swapchain: true, @@ -60,9 +64,7 @@ impl App { .filter(|p| { p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering }) - .filter(|p| { - p.supported_extensions().contains(&device_extensions) - }) + .filter(|p| p.supported_extensions().contains(&device_extensions)) .filter_map(|p| { p.queue_family_properties() .iter() @@ -73,15 +75,13 @@ impl App { }) .map(|i| (p, i as u32)) }) - .min_by_key(|(p, _)| { - match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - } + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, }) .expect("no suitable physical device found"); @@ -110,7 +110,7 @@ impl App { ..Default::default() }, ) - .unwrap(); + .unwrap(); let queue = queues.next().unwrap(); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); @@ -141,16 +141,16 @@ impl ApplicationHandler for App { f64::from(600), )); - let window = Arc::new( - event_loop - .create_window(window_attributes) - .unwrap(), - ); + let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); self.rcx = Some(RenderContext::new(window, surface, &self.device)); - self.scene = Some(Scene::initialize(&self.device, &self.rcx.as_ref().unwrap().swapchain, &self.memory_allocator)); + self.scene = Some(Scene::initialize( + &self.device, + &self.rcx.as_ref().unwrap().swapchain, + &self.memory_allocator, + )); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { @@ -192,7 +192,7 @@ impl ApplicationHandler for App { rcx.swapchain.clone(), None, ) - .map_err(Validated::unwrap) + .map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { @@ -211,7 +211,7 @@ impl ApplicationHandler for App { self.queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, ) - .unwrap(); + .unwrap(); builder .begin_rendering(RenderingInfo { @@ -233,9 +233,7 @@ impl ApplicationHandler for App { scene.render(&mut builder); } - builder - .end_rendering() - .unwrap(); + builder.end_rendering().unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index d056eb7..21def04 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,7 +1,7 @@ -mod render_context; mod app; -mod vertex; mod pipelines; +mod render_context; +mod vertex; pub use app::App; pub use pipelines::create_triangle_pipeline; @@ -10,8 +10,8 @@ pub use scene::Scene; pub use vertex::Vertex2D; use std::sync::Arc; -use vulkano::image::Image; use vulkano::image::view::ImageView; +use vulkano::image::Image; /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup(images: &[Arc]) -> Vec> { diff --git a/src/renderer/pipelines/mod.rs b/src/renderer/pipelines/mod.rs index db588d1..670717a 100644 --- a/src/renderer/pipelines/mod.rs +++ b/src/renderer/pipelines/mod.rs @@ -1,2 +1,2 @@ mod triangle_pipeline; -pub use triangle_pipeline::create_triangle_pipeline; \ No newline at end of file +pub use triangle_pipeline::create_triangle_pipeline; diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/renderer/pipelines/triangle_pipeline.rs index e9de828..b5858e2 100644 --- a/src/renderer/pipelines/triangle_pipeline.rs +++ b/src/renderer/pipelines/triangle_pipeline.rs @@ -1,15 +1,17 @@ use std::sync::Arc; use vulkano::device::Device; -use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; -use vulkano::pipeline::{DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo}; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; -use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; use vulkano::pipeline::graphics::multisample::MultisampleState; use vulkano::pipeline::graphics::rasterization::RasterizationState; use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; +use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; use vulkano::pipeline::graphics::viewport::ViewportState; +use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; +use vulkano::pipeline::{ + DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, +}; use vulkano::swapchain::Swapchain; use crate::renderer::Vertex2D; @@ -30,7 +32,10 @@ mod shaders { } } -pub fn create_triangle_pipeline(device: &Arc, swapchain: &Arc) -> Arc { +pub fn create_triangle_pipeline( + device: &Arc, + swapchain: &Arc, +) -> Arc { let vs = shaders::vs::load(device.clone()) .unwrap() .entry_point("main") @@ -40,9 +45,7 @@ pub fn create_triangle_pipeline(device: &Arc, swapchain: &Arc .entry_point("main") .unwrap(); - let vertex_input_state = Vertex2D::per_vertex() - .definition(&vs) - .unwrap(); + let vertex_input_state = Vertex2D::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), @@ -55,7 +58,7 @@ pub fn create_triangle_pipeline(device: &Arc, swapchain: &Arc .into_pipeline_layout_create_info(device.clone()) .unwrap(), ) - .unwrap(); + .unwrap(); let subpass = PipelineRenderingCreateInfo { color_attachment_formats: vec![Some(swapchain.image_format())], @@ -81,5 +84,5 @@ pub fn create_triangle_pipeline(device: &Arc, swapchain: &Arc ..GraphicsPipelineCreateInfo::layout(layout) }, ) - .unwrap() + .unwrap() } diff --git a/src/renderer/render_context.rs b/src/renderer/render_context.rs index 097479e..2248be3 100644 --- a/src/renderer/render_context.rs +++ b/src/renderer/render_context.rs @@ -1,13 +1,13 @@ +use crate::renderer::window_size_dependent_setup; use std::sync::Arc; use vulkano::device::Device; -use vulkano::image::ImageUsage; use vulkano::image::view::ImageView; +use vulkano::image::ImageUsage; use vulkano::pipeline::graphics::viewport::Viewport; use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use vulkano::sync; use vulkano::sync::GpuFuture; use winit::window::Window; -use crate::renderer::window_size_dependent_setup; pub struct RenderContext { pub(super) window: Arc, @@ -51,7 +51,7 @@ impl RenderContext { ..Default::default() }, ) - .unwrap() + .unwrap() }; let attachment_image_views = window_size_dependent_setup(&images); diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs index f8fe8aa..e327f05 100644 --- a/src/renderer/scene.rs +++ b/src/renderer/scene.rs @@ -1,12 +1,67 @@ use std::sync::Arc; -use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; +use vulkano::buffer::Subbuffer; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; use vulkano::device::Device; -use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; +use vulkano::memory::allocator::StandardMemoryAllocator; use vulkano::pipeline::GraphicsPipeline; use vulkano::swapchain::Swapchain; -use crate::renderer::{Vertex2D, create_triangle_pipeline}; +use crate::renderer::{create_triangle_pipeline, Vertex2D}; + +const VERTICES: [Vertex2D; 12] = [ + // Triangle en haut à gauche + Vertex2D { + position: [-0.5, -0.75], + color: [1.0, 0.0, 0.0], + }, + Vertex2D { + position: [-0.75, -0.25], + color: [0.0, 1.0, 0.0], + }, + Vertex2D { + position: [-0.25, -0.25], + color: [0.0, 0.0, 1.0], + }, + // Triangle en bas à gauche + Vertex2D { + position: [-0.5, 0.25], + color: [0.5, 0.5, 0.5], + }, + Vertex2D { + position: [-0.75, 0.75], + color: [0.2, 0.8, 0.2], + }, + Vertex2D { + position: [-0.25, 0.75], + color: [0.8, 0.2, 0.2], + }, + // Triangle en haut à droite + Vertex2D { + position: [0.5, -0.75], + color: [1.0, 1.0, 0.0], + }, + Vertex2D { + position: [0.25, -0.25], + color: [0.0, 1.0, 1.0], + }, + Vertex2D { + position: [0.75, -0.25], + color: [1.0, 0.0, 1.0], + }, + // Triangle en bas à droite + Vertex2D { + position: [0.5, 0.25], + color: [0.1, 0.5, 0.8], + }, + Vertex2D { + position: [0.25, 0.75], + color: [0.8, 0.6, 0.1], + }, + Vertex2D { + position: [0.75, 0.75], + color: [0.3, 0.4, 0.6], + }, +]; pub struct Scene { pipeline: Arc, @@ -14,66 +69,14 @@ pub struct Scene { } impl Scene { - fn create_vertex_buffer( - memory_allocator: &Arc, - ) -> Subbuffer<[Vertex2D]> { - let vertices = [ - // Triangle en haut à gauche - Vertex2D::new([-0.5, -0.75], // Coin supérieur gauche - [1.0, 0.0, 0.0]), // Couleur rouge - Vertex2D::new([-0.75, -0.25], // Coin supérieur - [0.0, 1.0, 0.0]), // Couleur verte - Vertex2D::new([-0.25, -0.25], // Coin supérieur droit - [0.0, 0.0, 1.0]), // Couleur bleue - - // Triangle en bas à gauche - Vertex2D::new([-0.5, 0.25], // Coin inférieur gauche - [0.5, 0.5, 0.5]), // Couleur gris - Vertex2D::new([-0.75, 0.75], // Coin inférieur - [0.2, 0.8, 0.2]), // Couleur vert clair - Vertex2D::new([-0.25, 0.75], // Coin inférieur droit - [0.8, 0.2, 0.2]), // Couleur rouge clair - - // Triangle en haut à droite - Vertex2D::new([0.5, -0.75], // Coin gauche supérieur - [1.0, 1.0, 0.0]), // Couleur jaune - Vertex2D::new([0.25, -0.25], // Coin gauche - [0.0, 1.0, 1.0]), // Couleur cyan - Vertex2D::new([0.75, -0.25], // Coin gauche inférieur - [1.0, 0.0, 1.0]), // Couleur magenta - - // Triangle en bas à droite - Vertex2D::new([0.5, 0.25], // Coin droit supérieur - [0.1, 0.5, 0.8]), // Couleur bleu clair - Vertex2D::new([0.25, 0.75], // Coin droit - [0.8, 0.6, 0.1]), // Couleur or - Vertex2D::new([0.75, 0.75], // Coin droit inférieur - [0.3, 0.4, 0.6]), // Couleur bleu foncé - ]; - - Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap() - } - pub fn initialize( device: &Arc, swapchain: &Arc, memory_allocator: &Arc, ) -> Scene { let pipeline = create_triangle_pipeline(device, swapchain); - let vertex_buffer = Self::create_vertex_buffer(memory_allocator); + let vertex_buffer = + Vertex2D::create_buffer(Vec::from_iter(VERTICES), memory_allocator).unwrap(); Scene { pipeline, @@ -93,4 +96,4 @@ impl Scene { unsafe { builder.draw(vertex_count, instance_count, 0, 0) }.unwrap(); } -} \ No newline at end of file +} diff --git a/src/renderer/vertex.rs b/src/renderer/vertex.rs index 8eb4ff5..fc2ee21 100644 --- a/src/renderer/vertex.rs +++ b/src/renderer/vertex.rs @@ -1,5 +1,10 @@ -use vulkano::buffer::BufferContents; +use std::sync::Arc; +use vulkano::buffer::{ + AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, +}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::graphics::vertex_input::Vertex; +use vulkano::Validated; #[derive(BufferContents, Vertex)] #[repr(C)] @@ -12,7 +17,22 @@ pub struct Vertex2D { } impl Vertex2D { - pub fn new(position: [f32; 2], color: [f32; 3]) -> Self { - Self { position, color } + pub fn create_buffer( + vertices: Vec, + memory_allocator: &Arc, + ) -> Result, Validated> { + Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) } } From d9f70caec0f604ebf833a6adf93db3b2f5439745 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Dec 2024 20:43:10 +0100 Subject: [PATCH 08/91] vscode: Add format on save --- .vscode/settings.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..61e637e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.formatOnSave": true, + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + }, + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true +} From 7cbc78588896cdd4d7e98fee9a34dfc796587f21 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Dec 2024 21:07:47 +0100 Subject: [PATCH 09/91] Scene: manage error --- src/renderer/app.rs | 15 ++++--- src/renderer/pipelines/triangle_pipeline.rs | 46 +++++++++++---------- src/renderer/scene.rs | 34 ++++++++------- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/renderer/app.rs b/src/renderer/app.rs index 784e6ce..8651372 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -146,11 +146,14 @@ impl ApplicationHandler for App { let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); self.rcx = Some(RenderContext::new(window, surface, &self.device)); - self.scene = Some(Scene::initialize( - &self.device, - &self.rcx.as_ref().unwrap().swapchain, - &self.memory_allocator, - )); + self.scene = Some( + Scene::load( + &self.device, + &self.rcx.as_ref().unwrap().swapchain, + &self.memory_allocator, + ) + .unwrap(), + ); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { @@ -230,7 +233,7 @@ impl ApplicationHandler for App { .unwrap(); if let Some(scene) = self.scene.as_ref() { - scene.render(&mut builder); + scene.render(&mut builder).unwrap(); } builder.end_rendering().unwrap(); diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/renderer/pipelines/triangle_pipeline.rs index b5858e2..77d1092 100644 --- a/src/renderer/pipelines/triangle_pipeline.rs +++ b/src/renderer/pipelines/triangle_pipeline.rs @@ -1,3 +1,4 @@ +use std::error::Error; use std::sync::Arc; use vulkano::device::Device; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; @@ -12,6 +13,7 @@ use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; use vulkano::pipeline::{ DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, }; +use vulkano::shader::EntryPoint; use vulkano::swapchain::Swapchain; use crate::renderer::Vertex2D; @@ -35,37 +37,26 @@ mod shaders { pub fn create_triangle_pipeline( device: &Arc, swapchain: &Arc, -) -> Arc { - let vs = shaders::vs::load(device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = shaders::fs::load(device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - - let vertex_input_state = Vertex2D::per_vertex().definition(&vs).unwrap(); +) -> Result, Box> { + let (vs, fs) = load_shaders(device)?; + let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), - ) - .unwrap(); + let create_info = PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(device.clone())?; + + let layout = PipelineLayout::new(device.clone(), create_info)?; let subpass = PipelineRenderingCreateInfo { color_attachment_formats: vec![Some(swapchain.image_format())], ..Default::default() }; - GraphicsPipeline::new( + let pipeline = GraphicsPipeline::new( device.clone(), None, GraphicsPipelineCreateInfo { @@ -83,6 +74,19 @@ pub fn create_triangle_pipeline( subpass: Some(subpass.into()), ..GraphicsPipelineCreateInfo::layout(layout) }, - ) - .unwrap() + )?; + + Ok(pipeline) +} + +fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> { + let vs = shaders::vs::load(device.clone())? + .entry_point("main") + .ok_or(format!("Failed find main entry point of vertex shader"))?; + + let fs = shaders::fs::load(device.clone())? + .entry_point("main") + .ok_or(format!("Failed find main entry point of fragment shader"))?; + + Ok((vs, fs)) } diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs index e327f05..c820380 100644 --- a/src/renderer/scene.rs +++ b/src/renderer/scene.rs @@ -1,3 +1,4 @@ +use std::error::Error; use std::sync::Arc; use vulkano::buffer::Subbuffer; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; @@ -69,31 +70,34 @@ pub struct Scene { } impl Scene { - pub fn initialize( + pub fn load( device: &Arc, swapchain: &Arc, memory_allocator: &Arc, - ) -> Scene { - let pipeline = create_triangle_pipeline(device, swapchain); - let vertex_buffer = - Vertex2D::create_buffer(Vec::from_iter(VERTICES), memory_allocator).unwrap(); + ) -> Result> { + let pipeline = create_triangle_pipeline(device, swapchain)?; + let vertex_buffer = Vertex2D::create_buffer(Vec::from_iter(VERTICES), memory_allocator)?; - Scene { + Ok(Scene { pipeline, vertex_buffer, - } + }) } - pub fn render(&self, builder: &mut AutoCommandBufferBuilder) { - builder - .bind_pipeline_graphics(self.pipeline.clone()) - .unwrap() - .bind_vertex_buffers(0, self.vertex_buffer.clone()) - .unwrap(); - + pub fn render( + &self, + builder: &mut AutoCommandBufferBuilder, + ) -> Result<(), Box> { let vertex_count = self.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; - unsafe { builder.draw(vertex_count, instance_count, 0, 0) }.unwrap(); + unsafe { + builder + .bind_pipeline_graphics(self.pipeline.clone())? + .bind_vertex_buffers(0, self.vertex_buffer.clone())? + .draw(vertex_count, instance_count, 0, 0)?; + } + + Ok(()) } } From 2b9c3ab25a7ac1b99c3f60714d01c20d6403cd59 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Dec 2024 21:08:28 +0100 Subject: [PATCH 10/91] vscode: Organize imports --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 61e637e..875f10e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "always", + }, "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer", }, From 864c558db70e94cc79bb00fbe32a2b590e0abbf5 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 17 Dec 2024 19:01:30 +0100 Subject: [PATCH 11/91] Update deps --- Cargo.lock | 143 ++++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d457b93..cbd0e43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arrayref" @@ -190,9 +190,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" dependencies = [ "bytemuck_derive", ] @@ -210,9 +210,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "calloop" @@ -242,9 +242,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.1" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" dependencies = [ "jobserver", "libc", @@ -345,18 +345,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -428,14 +428,20 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.5.0" @@ -497,9 +503,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -521,9 +527,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -574,24 +580,25 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.164" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -605,7 +612,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] @@ -957,7 +964,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -1026,9 +1033,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1079,9 +1086,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -1138,15 +1145,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1191,18 +1198,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -1311,9 +1318,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1394,9 +1401,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-core", @@ -1404,21 +1411,21 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" [[package]] name = "ttf-parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" @@ -1450,12 +1457,12 @@ dependencies = [ [[package]] name = "vulkano" version = "0.34.0" -source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#23606f05825adf5212f104ead9e95f9d325db1aa" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#ea30f65280360b1e1bc907cd42d4eb355fed3c9d" dependencies = [ - "ahash", "ash", "bytemuck", "crossbeam-queue", + "foldhash", "half", "heck", "indexmap", @@ -1481,7 +1488,7 @@ dependencies = [ [[package]] name = "vulkano-macros" version = "0.34.0" -source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#23606f05825adf5212f104ead9e95f9d325db1aa" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#ea30f65280360b1e1bc907cd42d4eb355fed3c9d" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1492,9 +1499,9 @@ dependencies = [ [[package]] name = "vulkano-shaders" version = "0.34.0" -source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#23606f05825adf5212f104ead9e95f9d325db1aa" +source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#ea30f65280360b1e1bc907cd42d4eb355fed3c9d" dependencies = [ - "ahash", + "foldhash", "heck", "proc-macro2", "quote", @@ -1521,9 +1528,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -1532,13 +1539,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -1547,21 +1553,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1569,9 +1576,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -1582,9 +1589,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wayland-backend" @@ -1697,9 +1704,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", From ec6e0c28bee6178537599e50f289261abf7dc64f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 17 Dec 2024 23:41:33 +0100 Subject: [PATCH 12/91] Begin work on uniform data --- res/shaders/vertex.vert | 2 +- src/renderer/pipelines/triangle_pipeline.rs | 31 +++++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index 3adb319..ec483f7 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1 +1 @@ -#version 450 layout (location = 0) in vec2 position; layout (location = 1) in vec3 color; layout (location = 0) out vec3 fragColor; void main() { gl_Position = vec4(position, 0.0, 1.0); fragColor = color; } \ No newline at end of file +#version 450 layout (location = 0) in vec2 position; layout (location = 1) in vec3 color; layout (location = 0) out vec3 fragColor; layout (set = 0, binding = 0) uniform MVP_Data { mat4 model; mat4 view; mat4 projection; } uniforms; void main() { gl_Position = vec4(position, 0.0, 1.0); fragColor = color; } \ No newline at end of file diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/renderer/pipelines/triangle_pipeline.rs index 77d1092..1423b8f 100644 --- a/src/renderer/pipelines/triangle_pipeline.rs +++ b/src/renderer/pipelines/triangle_pipeline.rs @@ -1,5 +1,9 @@ +use std::collections::BTreeMap; use std::error::Error; use std::sync::Arc; +use vulkano::descriptor_set::layout::{ + DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, +}; use vulkano::device::Device; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; @@ -9,11 +13,11 @@ use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; use vulkano::pipeline::graphics::viewport::ViewportState; use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; -use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; +use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; use vulkano::pipeline::{ DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, }; -use vulkano::shader::EntryPoint; +use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; use crate::renderer::Vertex2D; @@ -46,8 +50,23 @@ pub fn create_triangle_pipeline( PipelineShaderStageCreateInfo::new(fs), ]; - let create_info = PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone())?; + let mut bindings = BTreeMap::::new(); + let mut descriptor_set_layout_binding = + DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBufferDynamic); + descriptor_set_layout_binding.stages = ShaderStages::VERTEX; + bindings.insert(0, descriptor_set_layout_binding); + + let descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings, + ..Default::default() + }; + + let create_info = PipelineDescriptorSetLayoutCreateInfo { + set_layouts: vec![descriptor_set_layout], + flags: PipelineLayoutCreateFlags::default(), + push_constant_ranges: vec![], + } + .into_pipeline_layout_create_info(device.clone())?; let layout = PipelineLayout::new(device.clone(), create_info)?; @@ -82,11 +101,11 @@ pub fn create_triangle_pipeline( fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> { let vs = shaders::vs::load(device.clone())? .entry_point("main") - .ok_or(format!("Failed find main entry point of vertex shader"))?; + .ok_or("Failed find main entry point of vertex shader".to_string())?; let fs = shaders::fs::load(device.clone())? .entry_point("main") - .ok_or(format!("Failed find main entry point of fragment shader"))?; + .ok_or("Failed find main entry point of fragment shader".to_string())?; Ok((vs, fs)) } From 784e5b90beb3f462aec722f081166ea120d251e1 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 18 Dec 2024 21:47:11 +0100 Subject: [PATCH 13/91] Add first working rotation --- Cargo.lock | 7 ++ Cargo.toml | 3 + res/shaders/vertex.vert | 2 +- src/renderer/mod.rs | 1 - src/renderer/pipelines/mod.rs | 3 +- src/renderer/pipelines/triangle_pipeline.rs | 4 +- src/renderer/scene.rs | 84 ++++++++++++++++++++- 7 files changed, 94 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbd0e43..78fdb0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,6 +490,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "glam" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677" + [[package]] name = "half" version = "2.4.1" @@ -1137,6 +1143,7 @@ version = "0.1.0" dependencies = [ "anyhow", "env_logger", + "glam", "log", "vulkano", "vulkano-shaders", diff --git a/Cargo.toml b/Cargo.toml index 9ad5563..62dde55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ winit = { version = "0.30", features = ["rwh_06"] } vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } +# Math +glam = { version = "0.29" } + # Log and tracing log = "0.4" env_logger = "0.11.5" diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index ec483f7..65b0acf 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1 +1 @@ -#version 450 layout (location = 0) in vec2 position; layout (location = 1) in vec3 color; layout (location = 0) out vec3 fragColor; layout (set = 0, binding = 0) uniform MVP_Data { mat4 model; mat4 view; mat4 projection; } uniforms; void main() { gl_Position = vec4(position, 0.0, 1.0); fragColor = color; } \ No newline at end of file +#version 450 layout (location = 0) in vec2 position; layout (location = 1) in vec3 color; layout (location = 0) out vec3 fragColor; layout (set = 0, binding = 0) uniform MVPData { mat4 world; mat4 view; mat4 projection; } uniforms; void main() { mat4 worldview = uniforms.view * uniforms.world; gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0); fragColor = color; } \ No newline at end of file diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 21def04..01187d5 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -3,7 +3,6 @@ mod pipelines; mod render_context; mod vertex; pub use app::App; -pub use pipelines::create_triangle_pipeline; mod scene; pub use scene::Scene; diff --git a/src/renderer/pipelines/mod.rs b/src/renderer/pipelines/mod.rs index 670717a..e5f30a7 100644 --- a/src/renderer/pipelines/mod.rs +++ b/src/renderer/pipelines/mod.rs @@ -1,2 +1 @@ -mod triangle_pipeline; -pub use triangle_pipeline::create_triangle_pipeline; +pub mod triangle_pipeline; diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/renderer/pipelines/triangle_pipeline.rs index 1423b8f..f6001f7 100644 --- a/src/renderer/pipelines/triangle_pipeline.rs +++ b/src/renderer/pipelines/triangle_pipeline.rs @@ -22,7 +22,7 @@ use vulkano::swapchain::Swapchain; use crate::renderer::Vertex2D; -mod shaders { +pub mod shaders { pub mod vs { vulkano_shaders::shader! { ty: "vertex", @@ -52,7 +52,7 @@ pub fn create_triangle_pipeline( let mut bindings = BTreeMap::::new(); let mut descriptor_set_layout_binding = - DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBufferDynamic); + DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer); descriptor_set_layout_binding.stages = ShaderStages::VERTEX; bindings.insert(0, descriptor_set_layout_binding); diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs index c820380..266a7a8 100644 --- a/src/renderer/scene.rs +++ b/src/renderer/scene.rs @@ -1,13 +1,19 @@ +use crate::renderer::pipelines::triangle_pipeline::shaders::vs; +use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; -use vulkano::buffer::Subbuffer; +use std::time::Instant; +use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; +use vulkano::buffer::{BufferUsage, Subbuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; +use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; +use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::device::Device; -use vulkano::memory::allocator::StandardMemoryAllocator; -use vulkano::pipeline::GraphicsPipeline; +use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; +use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::swapchain::Swapchain; -use crate::renderer::{create_triangle_pipeline, Vertex2D}; +use crate::renderer::{pipelines::triangle_pipeline::create_triangle_pipeline, Vertex2D}; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche @@ -67,6 +73,11 @@ const VERTICES: [Vertex2D; 12] = [ pub struct Scene { pipeline: Arc, vertex_buffer: Subbuffer<[Vertex2D]>, + + uniform_buffer_allocator: SubbufferAllocator, + rotation_start: Instant, + swapchain: Arc, + descriptor_set_allocator: Arc, } impl Scene { @@ -78,9 +89,28 @@ impl Scene { let pipeline = create_triangle_pipeline(device, swapchain)?; let vertex_buffer = Vertex2D::create_buffer(Vec::from_iter(VERTICES), memory_allocator)?; + let uniform_buffer_allocator = SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::UNIFORM_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + Ok(Scene { pipeline, vertex_buffer, + uniform_buffer_allocator, + rotation_start: Instant::now(), + swapchain: swapchain.clone(), + descriptor_set_allocator, }) } @@ -91,13 +121,59 @@ impl Scene { let vertex_count = self.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; + let uniform_buffer = self.get_uniform_buffer(); + let layout = &self.pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + layout.clone(), + [WriteDescriptorSet::buffer(0, uniform_buffer)], + [], + ) + .unwrap(); + unsafe { builder .bind_pipeline_graphics(self.pipeline.clone())? + .bind_descriptor_sets( + PipelineBindPoint::Graphics, + self.pipeline.layout().clone(), + 0, + descriptor_set, + )? .bind_vertex_buffers(0, self.vertex_buffer.clone())? .draw(vertex_count, instance_count, 0, 0)?; } Ok(()) } + + fn get_uniform_buffer(&self) -> Subbuffer { + let elapsed = self.rotation_start.elapsed(); + let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; + let rotation = Mat3::from_rotation_y(rotation as f32); + + // NOTE: This teapot was meant for OpenGL where the origin is at the lower left + // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. + let aspect_ratio = + self.swapchain.image_extent()[0] as f32 / self.swapchain.image_extent()[1] as f32; + + let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0); + let view = Mat4::look_at_rh( + Vec3::new(0.3, 0.3, 1.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, -1.0, 0.0), + ); + let scale = Mat4::from_scale(Vec3::splat(1.0)); + + let uniform_data = vs::MVPData { + world: Mat4::from_mat3(rotation).to_cols_array_2d(), + view: (view * scale).to_cols_array_2d(), + projection: proj.to_cols_array_2d(), + }; + + let buffer = self.uniform_buffer_allocator.allocate_sized().unwrap(); + *buffer.write().unwrap() = uniform_data; + + buffer + } } From f7a3d883c64669e03369eec58e36f6c5fc305fb5 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 19 Dec 2024 21:44:49 +0100 Subject: [PATCH 14/91] Move allocator to App --- src/renderer/app.rs | 206 +++++++++++++++++---------------- src/renderer/mod.rs | 12 -- src/renderer/render_context.rs | 31 ++++- src/renderer/scene.rs | 54 +++------ 4 files changed, 150 insertions(+), 153 deletions(-) diff --git a/src/renderer/app.rs b/src/renderer/app.rs index 8651372..9788aa2 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -1,18 +1,21 @@ use crate::renderer::render_context::RenderContext; -use crate::renderer::{window_size_dependent_setup, Scene}; +use crate::renderer::Scene; use std::sync::Arc; +use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; +use vulkano::buffer::BufferUsage; use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, }; +use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; use vulkano::device::physical::PhysicalDeviceType; use vulkano::device::{ Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, }; use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; -use vulkano::memory::allocator::StandardMemoryAllocator; +use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{acquire_next_image, Surface, SwapchainCreateInfo, SwapchainPresentInfo}; +use vulkano::swapchain::{acquire_next_image, Surface, SwapchainPresentInfo}; use vulkano::sync::GpuFuture; use vulkano::{sync, Validated, Version, VulkanError, VulkanLibrary}; use winit::application::ApplicationHandler; @@ -21,12 +24,16 @@ use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::window::WindowId; pub struct App { - instance: Arc, - device: Arc, - queue: Arc, - memory_allocator: Arc, - command_buffer_allocator: Arc, - rcx: Option, + pub instance: Arc, + pub device: Arc, + pub queue: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub uniform_buffer_allocator: SubbufferAllocator, + pub descriptor_set_allocator: Arc, + + pub rcx: Option, scene: Option, } @@ -120,12 +127,29 @@ impl App { Default::default(), )); + let uniform_buffer_allocator = SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::UNIFORM_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + Self { instance, device, queue, memory_allocator, command_buffer_allocator, + uniform_buffer_allocator, + descriptor_set_allocator, rcx: None, scene: None, } @@ -146,68 +170,49 @@ impl ApplicationHandler for App { let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); self.rcx = Some(RenderContext::new(window, surface, &self.device)); - self.scene = Some( - Scene::load( - &self.device, - &self.rcx.as_ref().unwrap().swapchain, - &self.memory_allocator, - ) - .unwrap(), - ); + self.scene = Some(Scene::load(&self).unwrap()); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - let rcx = self.rcx.as_mut().unwrap(); - match event { WindowEvent::CloseRequested => { log::debug!("The close button was pressed; stopping"); event_loop.exit(); } WindowEvent::Resized(_) => { + let rcx = self.rcx.as_mut().unwrap(); rcx.recreate_swapchain = true; } WindowEvent::RedrawRequested => { - let window_size = rcx.window.inner_size(); + let (image_index, acquire_future) = { + let rcx = self.rcx.as_mut().unwrap(); + let window_size = rcx.window.inner_size(); - if window_size.width == 0 || window_size.height == 0 { - return; - } - - rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - - if rcx.recreate_swapchain { - let (new_swapchain, new_images) = rcx - .swapchain - .recreate(SwapchainCreateInfo { - image_extent: window_size.into(), - ..rcx.swapchain.create_info() - }) - .expect("failed to recreate swapchain"); - - rcx.swapchain = new_swapchain; - rcx.attachment_image_views = window_size_dependent_setup(&new_images); - rcx.viewport.extent = window_size.into(); - rcx.recreate_swapchain = false; - } - - let (image_index, suboptimal, acquire_future) = match acquire_next_image( - rcx.swapchain.clone(), - None, - ) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; + if window_size.width == 0 || window_size.height == 0 { return; } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - if suboptimal { - rcx.recreate_swapchain = true; - } + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.update_swapchain().unwrap(); + + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(rcx.swapchain.clone(), None) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + rcx.recreate_swapchain = true; + } + + (image_index, acquire_future) + }; let mut builder = AutoCommandBufferBuilder::primary( self.command_buffer_allocator.clone(), @@ -216,57 +221,64 @@ impl ApplicationHandler for App { ) .unwrap(); - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - rcx.attachment_image_views[image_index as usize].clone(), - ) - })], - ..Default::default() - }) - .unwrap() - .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) - .unwrap(); + { + let rcx = self.rcx.as_ref().unwrap(); + builder + .begin_rendering(RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + ..RenderingAttachmentInfo::image_view( + rcx.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) + .unwrap(); + } if let Some(scene) = self.scene.as_ref() { - scene.render(&mut builder).unwrap(); + scene.render(&self, &mut builder).unwrap(); } builder.end_rendering().unwrap(); let command_buffer = builder.build().unwrap(); - let future = rcx - .previous_frame_end - .take() - .unwrap() - .join(acquire_future) - .then_execute(self.queue.clone(), command_buffer) - .unwrap() - .then_swapchain_present( - self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index( - rcx.swapchain.clone(), - image_index, - ), - ) - .then_signal_fence_and_flush(); + { + let rcx = self.rcx.as_mut().unwrap(); - match future.map_err(Validated::unwrap) { - Ok(future) => { - rcx.previous_frame_end = Some(future.boxed()); - } - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - } - Err(e) => { - println!("failed to flush future: {e}"); - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + let future = rcx + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present( + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + rcx.previous_frame_end = Some(future.boxed()); + } + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + } + Err(e) => { + println!("failed to flush future: {e}"); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + } } } } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 01187d5..bbcff9a 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -7,15 +7,3 @@ pub use app::App; mod scene; pub use scene::Scene; pub use vertex::Vertex2D; - -use std::sync::Arc; -use vulkano::image::view::ImageView; -use vulkano::image::Image; - -/// This function is called once during initialization, then again whenever the window is resized. -fn window_size_dependent_setup(images: &[Arc]) -> Vec> { - images - .iter() - .map(|image| ImageView::new_default(image.clone()).unwrap()) - .collect::>() -} diff --git a/src/renderer/render_context.rs b/src/renderer/render_context.rs index 2248be3..dd7e840 100644 --- a/src/renderer/render_context.rs +++ b/src/renderer/render_context.rs @@ -1,12 +1,11 @@ -use crate::renderer::window_size_dependent_setup; use std::sync::Arc; use vulkano::device::Device; use vulkano::image::view::ImageView; -use vulkano::image::ImageUsage; +use vulkano::image::{Image, ImageUsage}; use vulkano::pipeline::graphics::viewport::Viewport; use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; -use vulkano::sync; use vulkano::sync::GpuFuture; +use vulkano::{sync, Validated, VulkanError}; use winit::window::Window; pub struct RenderContext { @@ -74,4 +73,30 @@ impl RenderContext { previous_frame_end, } } + + pub fn update_swapchain(&mut self) -> Result<(), Validated> { + if !self.recreate_swapchain { + return Ok(()); + } + + let window_size = self.window.inner_size(); + let (new_swapchain, new_images) = self.swapchain.recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..self.swapchain.create_info() + })?; + + self.swapchain = new_swapchain; + self.attachment_image_views = window_size_dependent_setup(&new_images); + self.viewport.extent = window_size.into(); + self.recreate_swapchain = false; + + Ok(()) + } +} + +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() } diff --git a/src/renderer/scene.rs b/src/renderer/scene.rs index 266a7a8..e3c6b58 100644 --- a/src/renderer/scene.rs +++ b/src/renderer/scene.rs @@ -3,17 +3,12 @@ use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; use std::time::Instant; -use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; -use vulkano::buffer::{BufferUsage, Subbuffer}; +use vulkano::buffer::Subbuffer; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; -use vulkano::device::Device; -use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use vulkano::swapchain::Swapchain; -use crate::renderer::{pipelines::triangle_pipeline::create_triangle_pipeline, Vertex2D}; +use crate::renderer::{pipelines::triangle_pipeline::create_triangle_pipeline, App, Vertex2D}; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche @@ -74,57 +69,34 @@ pub struct Scene { pipeline: Arc, vertex_buffer: Subbuffer<[Vertex2D]>, - uniform_buffer_allocator: SubbufferAllocator, rotation_start: Instant, - swapchain: Arc, - descriptor_set_allocator: Arc, } impl Scene { - pub fn load( - device: &Arc, - swapchain: &Arc, - memory_allocator: &Arc, - ) -> Result> { - let pipeline = create_triangle_pipeline(device, swapchain)?; - let vertex_buffer = Vertex2D::create_buffer(Vec::from_iter(VERTICES), memory_allocator)?; - - let uniform_buffer_allocator = SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); + pub fn load(app: &App) -> Result> { + let pipeline = create_triangle_pipeline(&app.device, &app.rcx.as_ref().unwrap().swapchain)?; + let vertex_buffer = + Vertex2D::create_buffer(Vec::from_iter(VERTICES), &app.memory_allocator)?; Ok(Scene { pipeline, vertex_buffer, - uniform_buffer_allocator, rotation_start: Instant::now(), - swapchain: swapchain.clone(), - descriptor_set_allocator, }) } pub fn render( &self, + app: &App, builder: &mut AutoCommandBufferBuilder, ) -> Result<(), Box> { let vertex_count = self.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; - let uniform_buffer = self.get_uniform_buffer(); + let uniform_buffer = self.get_uniform_buffer(app); let layout = &self.pipeline.layout().set_layouts()[0]; let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), + app.descriptor_set_allocator.clone(), layout.clone(), [WriteDescriptorSet::buffer(0, uniform_buffer)], [], @@ -147,15 +119,15 @@ impl Scene { Ok(()) } - fn get_uniform_buffer(&self) -> Subbuffer { + fn get_uniform_buffer(&self, app: &App) -> Subbuffer { + let swapchain = &app.rcx.as_ref().unwrap().swapchain; let elapsed = self.rotation_start.elapsed(); let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; let rotation = Mat3::from_rotation_y(rotation as f32); // NOTE: This teapot was meant for OpenGL where the origin is at the lower left // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. - let aspect_ratio = - self.swapchain.image_extent()[0] as f32 / self.swapchain.image_extent()[1] as f32; + let aspect_ratio = swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0); let view = Mat4::look_at_rh( @@ -171,7 +143,7 @@ impl Scene { projection: proj.to_cols_array_2d(), }; - let buffer = self.uniform_buffer_allocator.allocate_sized().unwrap(); + let buffer = app.uniform_buffer_allocator.allocate_sized().unwrap(); *buffer.write().unwrap() = uniform_data; buffer From 3208f62b4a727e6bf4380b6e386ad3926092998f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 22 Dec 2024 23:35:39 +0100 Subject: [PATCH 15/91] Fix flake.nix for nixos --- .envrc | 2 +- flake.nix | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.envrc b/.envrc index 2f8e1d3..3550a30 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use flake --impure +use flake diff --git a/flake.nix b/flake.nix index 624590d..8386939 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,13 @@ cargo = rust; }); + nativeBuildInputs = with pkgs; [ + (rust.override { extensions = ["rust-src" "rust-analyzer"]; }) + pkg-config + cmake + python312 + ]; + libs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ libxkbcommon wayland libGL ]) ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isDarwin (with pkgs; [ darwin.apple_sdk.frameworks.SystemConfiguration ]); @@ -37,10 +44,15 @@ { devShells = { default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - (rust.override { extensions = ["rust-src" "rust-analyzer"]; }) - pkg-config - ]; + inherit nativeBuildInputs; + + buildInputs = libs; + + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (with pkgs; [ libxkbcommon wayland libGL ]); + }; + + with_compatibility = pkgs.mkShell { + inherit nativeBuildInputs; buildInputs = libs ++ [ pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanIntel From 7ed254f1839cfd2eb7a0970518dc9285a98257bb Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 8 Jan 2025 23:12:09 +0100 Subject: [PATCH 16/91] Fix NixOS not running --- .envrc | 2 +- flake.nix | 41 +++++++++++++++++++---------------------- src/renderer/app.rs | 2 -- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/.envrc b/.envrc index 3550a30..2f8e1d3 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use flake +use flake --impure diff --git a/flake.nix b/flake.nix index 8386939..687120b 100644 --- a/flake.nix +++ b/flake.nix @@ -30,36 +30,34 @@ cargo = rust; }); + buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers ] + ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ libxkbcommon wayland libGL ]) + ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isDarwin (with pkgs; [ darwin.apple_sdk.frameworks.SystemConfiguration ]); + nativeBuildInputs = with pkgs; [ - (rust.override { extensions = ["rust-src" "rust-analyzer"]; }) pkg-config cmake python312 ]; - libs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers ] - ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ libxkbcommon wayland libGL ]) - ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isDarwin (with pkgs; [ darwin.apple_sdk.frameworks.SystemConfiguration ]); + mkRustVulkanShell = { nixGLSupport ? true }: pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + (rust.override { extensions = ["rust-src" "rust-analyzer"]; }) + ] ++ nativeBuildInputs; + + buildInputs = buildInputs + ++ pkgs.lib.optionals nixGLSupport [ pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanIntel ]; + + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d"; + }; in { devShells = { - default = pkgs.mkShell { - inherit nativeBuildInputs; + default = mkRustVulkanShell {}; - buildInputs = libs; - - LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (with pkgs; [ libxkbcommon wayland libGL ]); - }; - - with_compatibility = pkgs.mkShell { - inherit nativeBuildInputs; - - buildInputs = libs ++ [ - pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanIntel - ]; - - LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (with pkgs; [ libxkbcommon wayland libGL ]); - }; + # Crash with error: cannot coerce null to a string: null + nixos = mkRustVulkanShell { nixGLSupport = false; }; }; packages = { @@ -69,8 +67,7 @@ src = self; - nativeBuildInputs = with pkgs; [ pkg-config shaderc ]; - buildInputs = libs; + inherit nativeBuildInputs buildInputs; cargoLock = { lockFile = ./Cargo.lock; diff --git a/src/renderer/app.rs b/src/renderer/app.rs index 9788aa2..0c562fd 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -52,8 +52,6 @@ impl App { enabled_extensions: required_extensions, enabled_layers: vec![ String::from("VK_LAYER_KHRONOS_validation"), - String::from("VK_LAYER_MANGOHUD_overlay_x86_64"), - String::from("VK_LAYER_NV_optimus"), ], ..Default::default() }, From 34ec2894df4b5da47f337efaedf962dbe83a7f8c Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 9 Jan 2025 15:33:33 +0100 Subject: [PATCH 17/91] Use nixGl auto detection fork to fix NixOS (open source NVIDIA) --- flake.lock | 11 ++++++----- flake.nix | 29 +++++++++++++---------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/flake.lock b/flake.lock index 9ff2d00..2a584fa 100644 --- a/flake.lock +++ b/flake.lock @@ -28,15 +28,16 @@ ] }, "locked": { - "lastModified": 1713543440, - "narHash": "sha256-lnzZQYG0+EXl/6NkGpyIz+FEOc/DSEG57AP1VsdeNrM=", - "owner": "nix-community", + "lastModified": 1735283791, + "narHash": "sha256-JlT4VFs8aVlW+l151HZIZumfFsccZXcO/k5WpbYF09Y=", + "owner": "phirsch", "repo": "nixGL", - "rev": "310f8e49a149e4c9ea52f1adf70cdc768ec53f8a", + "rev": "ea8baea3b9d854bf9cf5c834a805c50948dd2603", "type": "github" }, "original": { - "owner": "nix-community", + "owner": "phirsch", + "ref": "fix-versionMatch", "repo": "nixGL", "type": "github" } diff --git a/flake.nix b/flake.nix index 687120b..1b38c42 100644 --- a/flake.nix +++ b/flake.nix @@ -9,7 +9,8 @@ inputs.nixpkgs.follows = "nixpkgs"; }; nixgl = { - url = "github:nix-community/nixGL"; + # Revert this to community version when https://github.com/nix-community/nixGL/pull/187 is merged + url = "github:phirsch/nixGL/fix-versionMatch"; inputs.nixpkgs.follows = "nixpkgs"; inputs.flake-utils.follows = "flake-utils"; }; @@ -39,25 +40,21 @@ cmake python312 ]; - - mkRustVulkanShell = { nixGLSupport ? true }: pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - (rust.override { extensions = ["rust-src" "rust-analyzer"]; }) - ] ++ nativeBuildInputs; - - buildInputs = buildInputs - ++ pkgs.lib.optionals nixGLSupport [ pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanIntel ]; - - LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; - VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d"; - }; in { devShells = { - default = mkRustVulkanShell {}; + default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + (rust.override { extensions = [ "rust-src" "rust-analyzer" ]; }) + ] ++ nativeBuildInputs; - # Crash with error: cannot coerce null to a string: null - nixos = mkRustVulkanShell { nixGLSupport = false; }; + buildInputs = buildInputs + ++ [ pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanMesa ]; + + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d"; + RUST_LOG = "info,rust_vulkan_test=trace"; + }; }; packages = { From 7d6b6ea3709827f4cdeb8aa005309b5504a6e407 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 19 Feb 2025 12:48:36 +0100 Subject: [PATCH 18/91] Update vulkan crates --- Cargo.lock | 202 ++++++++++++++++++++++++++++------------------------- Cargo.toml | 4 +- 2 files changed, 110 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78fdb0b..4e01f8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,7 +47,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.6.0", + "bitflags 2.8.0", "cc", "cesu8", "jni", @@ -108,19 +108,20 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arrayref" @@ -169,9 +170,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "block2" @@ -190,18 +191,18 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", @@ -220,7 +221,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "log", "polling", "rustix", @@ -242,9 +243,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.4" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -399,9 +400,9 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -409,9 +410,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -438,9 +439,9 @@ dependencies = [ [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -586,9 +587,9 @@ dependencies = [ [[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", @@ -596,9 +597,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -616,16 +617,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall 0.5.8", ] [[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" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lock_api" @@ -639,9 +640,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "memchr" @@ -670,7 +671,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "jni-sys", "log", "ndk-sys", @@ -747,7 +748,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "libc", "objc2", @@ -763,7 +764,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -787,7 +788,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -829,7 +830,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "dispatch", "libc", @@ -854,7 +855,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -866,7 +867,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -889,7 +890,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-cloud-kit", @@ -921,7 +922,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -983,18 +984,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", @@ -1003,9 +1004,9 @@ 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" @@ -1039,9 +1040,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1057,9 +1058,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1096,7 +1097,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -1152,17 +1153,23 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.59.0", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" version = "1.0.18" @@ -1205,18 +1212,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -1225,9 +1232,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -1289,7 +1296,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -1325,9 +1332,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -1463,8 +1470,9 @@ dependencies = [ [[package]] name = "vulkano" -version = "0.34.0" -source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#ea30f65280360b1e1bc907cd42d4eb355fed3c9d" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08840c2b51759a6f88f26f5ea378bc8b5c199a5b4760ddda292304be087249c4" dependencies = [ "ash", "bytemuck", @@ -1494,8 +1502,9 @@ dependencies = [ [[package]] name = "vulkano-macros" -version = "0.34.0" -source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#ea30f65280360b1e1bc907cd42d4eb355fed3c9d" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc929c42c9336fd082079ac3ea30126e4a0dfe36fd2e2b3581303f7d140d20f" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1505,8 +1514,9 @@ dependencies = [ [[package]] name = "vulkano-shaders" -version = "0.34.0" -source = "git+https://github.com/vulkano-rs/vulkano.git?branch=master#ea30f65280360b1e1bc907cd42d4eb355fed3c9d" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf501461be7cef2893c0e62c50945add9763cc482051d29053f6157089d5ea9" dependencies = [ "foldhash", "heck", @@ -1535,20 +1545,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", @@ -1560,9 +1571,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", @@ -1573,9 +1584,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", @@ -1583,9 +1594,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", @@ -1596,9 +1607,12 @@ 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" @@ -1620,7 +1634,7 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "rustix", "wayland-backend", "wayland-scanner", @@ -1632,7 +1646,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cursor-icon", "wayland-backend", ] @@ -1654,7 +1668,7 @@ version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -1666,7 +1680,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1679,7 +1693,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1711,9 +1725,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" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -1945,14 +1959,14 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.5" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" +checksum = "f5d74280aabb958072864bff6cfbcf9025cf8bfacdde5e32b5e12920ef703b0f" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "bytemuck", "calloop", @@ -1997,9 +2011,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -2048,7 +2062,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "dlib", "log", "once_cell", @@ -2063,9 +2077,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" [[package]] name = "xmlparser" diff --git a/Cargo.toml b/Cargo.toml index 62dde55..e1de2ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ publish = false anyhow = "1.0" winit = { version = "0.30", features = ["rwh_06"] } -vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } -vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" } +vulkano = "0.35" +vulkano-shaders = "0.35" # Math glam = { version = "0.29" } From 011b4c27c1b6eefd5e4dcba8d38cb404d87d9530 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 19 Feb 2025 12:57:02 +0100 Subject: [PATCH 19/91] Update rust version to 1.84.1 --- flake.lock | 18 +++++++++--------- rust-toolchain.toml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/flake.lock b/flake.lock index 2a584fa..6484a8a 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -44,11 +44,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1730831018, - "narHash": "sha256-2S0HwIFRxYp+afuoFORcZA9TjryAf512GmE0MTfEOPU=", + "lastModified": 1739863612, + "narHash": "sha256-UbtgxplOhFcyjBcNbTVO8+HUHAl/WXFDOb6LvqShiZo=", "owner": "nixos", "repo": "nixpkgs", - "rev": "8c4dc69b9732f6bbe826b5fbb32184987520ff26", + "rev": "632f04521e847173c54fa72973ec6c39a371211c", "type": "github" }, "original": { @@ -73,11 +73,11 @@ ] }, "locked": { - "lastModified": 1730860036, - "narHash": "sha256-u0sfA4B65Q9cRO3xpIkQ4nldB8isfdIb3rWtsnRZ+Iw=", + "lastModified": 1739932111, + "narHash": "sha256-WkayjH0vuGw0hx2gmjTUGFRvMKpM17gKcpL/U8EUUw0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "b8eb3aeb21629cbe14968a5e3b1cbaefb0d1b260", + "rev": "75b2271c5c087d830684cd5462d4410219acc367", "type": "github" }, "original": { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2e2b8c8..fcb78ec 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.82.0" +channel = "1.84.1" From 752f5ab2b1626b1def0812a2cfd45ba6dcf02cb8 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 14 Mar 2025 13:08:24 +0100 Subject: [PATCH 20/91] Update rust version to 1.85.0 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fcb78ec..c1bc0a6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.84.1" +channel = "1.85.0" From e267be066ad4c505d3a97b67c3b8f713844d737a Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 14 Mar 2025 13:08:54 +0100 Subject: [PATCH 21/91] Update cargo lock --- Cargo.lock | 416 ++++++++++++++++++++++++++++++++--------------------- flake.lock | 12 +- 2 files changed, 258 insertions(+), 170 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e01f8e..38d2f44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -47,7 +47,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.8.0", + "bitflags 2.9.0", "cc", "cesu8", "jni", @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "arrayref" @@ -170,9 +170,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block2" @@ -180,20 +180,20 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2", + "objc2 0.5.2", ] [[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", ] @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "calloop" @@ -221,7 +221,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "log", "polling", "rustix", @@ -243,9 +243,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.9" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", @@ -272,9 +272,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cmake" -version = "0.1.52" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] @@ -361,9 +361,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "cursor-icon" @@ -410,22 +410,22 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] [[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" @@ -499,9 +499,9 @@ checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677" [[package]] name = "half" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" dependencies = [ "bytemuck", "cfg-if", @@ -526,17 +526,11 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[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", @@ -550,9 +544,33 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "jni" @@ -597,9 +615,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libloading" @@ -617,9 +635,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.10", ] [[package]] @@ -640,9 +658,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" @@ -671,7 +689,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "jni-sys", "log", "ndk-sys", @@ -742,20 +760,29 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-app-kit" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", "libc", - "objc2", + "objc2 0.5.2", "objc2-core-data", "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", ] [[package]] @@ -764,11 +791,11 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -778,8 +805,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -788,10 +815,20 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", ] [[package]] @@ -801,9 +838,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ "block2", - "objc2", - "objc2-foundation", - "objc2-metal", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", ] [[package]] @@ -813,16 +850,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ "block2", - "objc2", + "objc2 0.5.2", "objc2-contacts", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] name = "objc2-encode" -version = "4.0.3" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" @@ -830,11 +867,22 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", "dispatch", "libc", - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", ] [[package]] @@ -844,9 +892,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ "block2", - "objc2", + "objc2 0.5.2", "objc2-app-kit", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -855,10 +903,21 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-metal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c41bc8b0e50ea7a5304a56f25e0066f526e99641b46fd7b9ad4421dd35bff6" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", ] [[package]] @@ -867,11 +926,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", - "objc2", - "objc2-foundation", - "objc2-metal", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "objc2-metal 0.3.0", ] [[package]] @@ -880,8 +952,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -890,16 +962,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", - "objc2", + "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-image", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", @@ -912,8 +984,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -922,18 +994,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[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" @@ -971,7 +1043,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.10", "smallvec", "windows-targets 0.52.6", ] @@ -984,18 +1056,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.8" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.8" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -1010,9 +1082,9 @@ 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" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polling" @@ -1030,37 +1102,52 @@ dependencies = [ ] [[package]] -name = "proc-macro-crate" -version = "3.2.0" +name = "portable-atomic" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" 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", ] @@ -1073,13 +1160,14 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "raw-window-metal" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2000e45d7daa9b6d946e88dfa1d7ae330424a81918a6545741821c989eb80a9" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" dependencies = [ - "objc2", - "objc2-foundation", - "objc2-quartz-core", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "objc2-quartz-core 0.3.0", ] [[package]] @@ -1093,11 +1181,11 @@ 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.8.0", + "bitflags 2.9.0", ] [[package]] @@ -1153,11 +1241,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -1166,15 +1254,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -1212,18 +1300,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -1232,9 +1320,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -1280,15 +1368,15 @@ dependencies = [ [[package]] name = "slabbin" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd33b7a607dbd960b5e78bb4740d1f86e84250eb03a12960ee1482c2a256063" +checksum = "9db491c0d4152a069911a0fbdaca959691bf0b9d7110d98a7ed1c8e59b79ab30" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "smithay-client-toolkit" @@ -1296,7 +1384,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -1332,9 +1420,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -1404,9 +1492,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[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", "toml_datetime", @@ -1437,9 +1525,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" @@ -1616,9 +1704,9 @@ dependencies = [ [[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", @@ -1630,11 +1718,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.8.0", + "bitflags 2.9.0", "rustix", "wayland-backend", "wayland-scanner", @@ -1646,16 +1734,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.8.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", @@ -1664,11 +1752,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.5" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -1676,11 +1764,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" +checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1689,11 +1777,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" +checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1702,9 +1790,9 @@ dependencies = [ [[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", @@ -1713,9 +1801,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", @@ -1959,14 +2047,14 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.8" +version = "0.30.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d74280aabb958072864bff6cfbcf9025cf8bfacdde5e32b5e12920ef703b0f" +checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.8.0", + "bitflags 2.9.0", "block2", "bytemuck", "calloop", @@ -1980,9 +2068,9 @@ dependencies = [ "libc", "memmap2", "ndk", - "objc2", + "objc2 0.5.2", "objc2-app-kit", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", "percent-encoding", @@ -2011,9 +2099,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.24" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] @@ -2062,7 +2150,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "dlib", "log", "once_cell", diff --git a/flake.lock b/flake.lock index 6484a8a..6b2b086 100644 --- a/flake.lock +++ b/flake.lock @@ -44,11 +44,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1739863612, - "narHash": "sha256-UbtgxplOhFcyjBcNbTVO8+HUHAl/WXFDOb6LvqShiZo=", + "lastModified": 1741865919, + "narHash": "sha256-4thdbnP6dlbdq+qZWTsm4ffAwoS8Tiq1YResB+RP6WE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "632f04521e847173c54fa72973ec6c39a371211c", + "rev": "573c650e8a14b2faa0041645ab18aed7e60f0c9a", "type": "github" }, "original": { @@ -73,11 +73,11 @@ ] }, "locked": { - "lastModified": 1739932111, - "narHash": "sha256-WkayjH0vuGw0hx2gmjTUGFRvMKpM17gKcpL/U8EUUw0=", + "lastModified": 1741919466, + "narHash": "sha256-xto0Oq+WItjDZvafo6ZVI4TZa6/2oxJHb0tkcSGVUJI=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "75b2271c5c087d830684cd5462d4410219acc367", + "rev": "8b137260b776525542d47d3a4dbac753df478a42", "type": "github" }, "original": { From 6b6534df763f4ebb12b2904e5f214b974ba051e1 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 14 Mar 2025 17:03:59 +0100 Subject: [PATCH 22/91] Add apecs + update glam --- Cargo.lock | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 5 +- 2 files changed, 244 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38d2f44..e69aec8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,12 +117,48 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "any_vec" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6ac04794a7749710e3c7f3c93222e3d04692993b69876d69393efd2565401a" + [[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "apecs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df7760d4baebb17003dcf99134d8e3a63f487e146d58911f0bcd27afb185d1c" +dependencies = [ + "any_vec", + "apecs-derive", + "async-channel", + "itertools", + "log", + "moongraph", + "parking_lot", + "rayon", + "smallvec", + "snafu 0.8.5", +] + +[[package]] +name = "apecs-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0f3ddfd31fd5276fb8039b75dc4d284c21213757a969e480c6ef8fde494f3b" +dependencies = [ + "moongraph-macros-syntax", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -150,6 +186,17 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -183,6 +230,17 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "broomdog" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ec65645d8167b03c07e049f114a878a11ab889f20c071d6f7b30bf88fbe5af" +dependencies = [ + "log", + "rustc-hash", + "snafu 0.8.5", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -206,7 +264,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -344,6 +402,25 @@ dependencies = [ "libc", ] +[[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-queue" version = "0.3.12" @@ -371,6 +448,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +[[package]] +name = "dagga" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cf0d7dcd307c9c5d81277737c35d1faf08af9e2cb262966a01c91021686b68" +dependencies = [ + "log", + "rustc-hash", + "snafu 0.7.5", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -386,6 +474,12 @@ dependencies = [ "libloading", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "downcast-rs" version = "1.2.1" @@ -398,6 +492,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "env_filter" version = "0.1.3" @@ -437,6 +537,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "foldhash" version = "0.1.4" @@ -461,7 +567,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -470,6 +576,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + [[package]] name = "gethostname" version = "0.4.3" @@ -493,9 +605,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.29.2" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677" +checksum = "17fcdf9683c406c2fc4d124afd29c0d595e22210d633cbdb8695ba9935ab1dc6" [[package]] name = "half" @@ -542,6 +654,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -569,7 +690,7 @@ checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -683,6 +804,30 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "moongraph" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5a4b09eb96a84205062b48ec5469c8c35c128167e838aa73dc620c4411af598" +dependencies = [ + "broomdog", + "dagga", + "log", + "rayon", + "snafu 0.8.5", +] + +[[package]] +name = "moongraph-macros-syntax" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08112087acc92cc28fb5d8f7bda1307123ecc9a275ed4835f1c03f1a8dd02c1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "ndk" version = "0.9.0" @@ -741,7 +886,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -1071,7 +1216,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -1170,6 +1315,26 @@ dependencies = [ "objc2-quartz-core 0.3.0", ] +[[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.4.1" @@ -1231,6 +1396,7 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", + "apecs", "env_logger", "glam", "log", @@ -1239,6 +1405,12 @@ dependencies = [ "winit", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.44" @@ -1315,7 +1487,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -1412,12 +1584,66 @@ dependencies = [ "serde", ] +[[package]] +name = "snafu" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +dependencies = [ + "doc-comment", + "snafu-derive 0.7.5", +] + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive 0.8.5", +] + +[[package]] +name = "snafu-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.100" @@ -1446,7 +1672,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -1597,7 +1823,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] [[package]] @@ -1611,7 +1837,7 @@ dependencies = [ "proc-macro2", "quote", "shaderc", - "syn", + "syn 2.0.100", "vulkano", ] @@ -1653,7 +1879,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -1688,7 +1914,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2192,5 +2418,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.100", ] diff --git a/Cargo.toml b/Cargo.toml index e1de2ee..11b5243 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,10 @@ vulkano = "0.35" vulkano-shaders = "0.35" # Math -glam = { version = "0.29" } +glam = { version = "0.30" } + +# ECS +apecs = "0.8" # Log and tracing log = "0.4" From 1d0ef32f60f349599fb3c27543d558a072735e44 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 21 Mar 2025 17:13:27 +0100 Subject: [PATCH 23/91] Try add renderdoc vulkan layer --- flake.nix | 5 +++-- src/renderer/app.rs | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 1b38c42..981454a 100644 --- a/flake.nix +++ b/flake.nix @@ -31,7 +31,7 @@ cargo = rust; }); - buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers ] + buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ libxkbcommon wayland libGL ]) ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isDarwin (with pkgs; [ darwin.apple_sdk.frameworks.SystemConfiguration ]); @@ -45,6 +45,7 @@ devShells = { default = pkgs.mkShell { nativeBuildInputs = with pkgs; [ + renderdoc (rust.override { extensions = [ "rust-src" "rust-analyzer" ]; }) ] ++ nativeBuildInputs; @@ -52,7 +53,7 @@ ++ [ pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanMesa ]; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; - VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d"; + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; RUST_LOG = "info,rust_vulkan_test=trace"; }; }; diff --git a/src/renderer/app.rs b/src/renderer/app.rs index 0c562fd..3287f7d 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -41,6 +41,10 @@ impl App { pub fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); + for layer in library.layer_properties().unwrap() { + log::debug!("Available layer: {}", layer.name()); + } + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( @@ -52,6 +56,7 @@ impl App { enabled_extensions: required_extensions, enabled_layers: vec![ String::from("VK_LAYER_KHRONOS_validation"), + String::from("VK_LAYER_RENDERDOC_Capture"), ], ..Default::default() }, @@ -90,7 +95,7 @@ impl App { }) .expect("no suitable physical device found"); - println!( + log::debug!( "Using device: {} (type: {:?})", physical_device.properties().device_name, physical_device.properties().device_type, @@ -100,6 +105,8 @@ impl App { device_extensions.khr_dynamic_rendering = true; } + log::debug!("Using device extensions: {:#?}", device_extensions); + let (device, mut queues) = Device::new( physical_device, DeviceCreateInfo { From 30721773c890f58fe65095a437ff12d9fa53585b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sat, 22 Mar 2025 21:51:21 +0100 Subject: [PATCH 24/91] First renderdoc run without wayland --- Cargo.toml | 2 +- flake.lock | 12 ++++++------ flake.nix | 40 ++++++++++++++++++++++++++-------------- rust-toolchain.toml | 2 +- src/renderer/app.rs | 5 +---- 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11b5243..ba7eb9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rust_vulkan_test" version = "0.1.0" -edition = "2021" +edition = "2024" authors = ["Florian RICHER "] publish = false diff --git a/flake.lock b/flake.lock index 6b2b086..2853bb7 100644 --- a/flake.lock +++ b/flake.lock @@ -44,11 +44,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1741865919, - "narHash": "sha256-4thdbnP6dlbdq+qZWTsm4ffAwoS8Tiq1YResB+RP6WE=", + "lastModified": 1742546557, + "narHash": "sha256-QyhimDBaDBtMfRc7kyL28vo+HTwXRPq3hz+BgSJDotw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "573c650e8a14b2faa0041645ab18aed7e60f0c9a", + "rev": "bfa9810ff7104a17555ab68ebdeafb6705f129b1", "type": "github" }, "original": { @@ -73,11 +73,11 @@ ] }, "locked": { - "lastModified": 1741919466, - "narHash": "sha256-xto0Oq+WItjDZvafo6ZVI4TZa6/2oxJHb0tkcSGVUJI=", + "lastModified": 1742524367, + "narHash": "sha256-KzTwk/5ETJavJZYV1DEWdCx05M4duFCxCpRbQSKWpng=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8b137260b776525542d47d3a4dbac753df478a42", + "rev": "70bf752d176b2ce07417e346d85486acea9040ef", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 981454a..e1a71d6 100644 --- a/flake.nix +++ b/flake.nix @@ -31,8 +31,17 @@ cargo = rust; }); + renderdoc = pkgs.renderdoc.overrideAttrs(oldAttrs: { + cmakeFlags = oldAttrs.cmakeFlags ++ [ + (pkgs.lib.cmakeBool "ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND" true) + ]; + }); + buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] - ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ libxkbcommon wayland libGL ]) + ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ + libxkbcommon wayland libGL # Wayland + xorg.libX11 xorg.libXcursor xorg.libXi xorg.libxcb xorg.libxshmfence # Xorg + ]) ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isDarwin (with pkgs; [ darwin.apple_sdk.frameworks.SystemConfiguration ]); nativeBuildInputs = with pkgs; [ @@ -40,22 +49,25 @@ cmake python312 ]; + + mkCustomShell = { packages ? [] }: pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + renderdoc + (rust.override { extensions = [ "rust-src" "rust-analyzer" ]; }) + ] ++ nativeBuildInputs; + + buildInputs = buildInputs + ++ packages; + + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${renderdoc}/share/vulkan/implicit_layer.d"; + RUST_LOG = "info,rust_vulkan_test=trace"; + }; in { devShells = { - default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - renderdoc - (rust.override { extensions = [ "rust-src" "rust-analyzer" ]; }) - ] ++ nativeBuildInputs; - - buildInputs = buildInputs - ++ [ pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanMesa ]; - - LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; - VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; - RUST_LOG = "info,rust_vulkan_test=trace"; - }; + default = mkCustomShell { packages = [ pkgs.nixgl.auto.nixVulkanNvidia pkgs.nixgl.nixVulkanMesa ]; }; + nixos = mkCustomShell { }; }; packages = { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c1bc0a6..00822fd 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.85.0" +channel = "1.85.1" diff --git a/src/renderer/app.rs b/src/renderer/app.rs index 3287f7d..f791616 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -54,10 +54,7 @@ impl App { // (e.g. MoltenVK) flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, enabled_extensions: required_extensions, - enabled_layers: vec![ - String::from("VK_LAYER_KHRONOS_validation"), - String::from("VK_LAYER_RENDERDOC_Capture"), - ], + enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], ..Default::default() }, ) From 1c24a05200761c44b2997282d83fb632ec9f3c72 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 25 Mar 2025 12:45:30 +0100 Subject: [PATCH 25/91] Update flake.nix --- flake.nix | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index e1a71d6..565976b 100644 --- a/flake.nix +++ b/flake.nix @@ -31,7 +31,7 @@ cargo = rust; }); - renderdoc = pkgs.renderdoc.overrideAttrs(oldAttrs: { + renderdoc = pkgs.renderdoc.overrideAttrs (oldAttrs: { cmakeFlags = oldAttrs.cmakeFlags ++ [ (pkgs.lib.cmakeBool "ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND" true) ]; @@ -39,9 +39,17 @@ buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ - libxkbcommon wayland libGL # Wayland - xorg.libX11 xorg.libXcursor xorg.libXi xorg.libxcb xorg.libxshmfence # Xorg - ]) + # Wayland + libxkbcommon + wayland + libGL + # Xorg + xorg.libX11 + xorg.libXcursor + xorg.libXi + xorg.libxcb + xorg.libxshmfence + ]) ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isDarwin (with pkgs; [ darwin.apple_sdk.frameworks.SystemConfiguration ]); nativeBuildInputs = with pkgs; [ @@ -50,8 +58,8 @@ python312 ]; - mkCustomShell = { packages ? [] }: pkgs.mkShell { - nativeBuildInputs = with pkgs; [ + mkCustomShell = { packages ? [ ] }: pkgs.mkShell { + nativeBuildInputs = [ renderdoc (rust.override { extensions = [ "rust-src" "rust-analyzer" ]; }) ] ++ nativeBuildInputs; From 63f16975b9d210a15d42bcb7ef856d0a051a9f1d Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 25 Mar 2025 13:45:34 +0100 Subject: [PATCH 26/91] Begin rework vulkano model structure --- src/main.rs | 1 + src/vulkano/context.rs | 12 ++++++++++++ src/vulkano/mod.rs | 3 +++ src/vulkano/queues.rs | 8 ++++++++ src/vulkano/renderer.rs | 0 5 files changed, 24 insertions(+) create mode 100644 src/vulkano/context.rs create mode 100644 src/vulkano/mod.rs create mode 100644 src/vulkano/queues.rs create mode 100644 src/vulkano/renderer.rs diff --git a/src/main.rs b/src/main.rs index 6c01c40..4c3f414 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; mod renderer; +mod vulkano; fn main() -> Result<(), impl Error> { env_logger::init(); diff --git a/src/vulkano/context.rs b/src/vulkano/context.rs new file mode 100644 index 0000000..b4b5753 --- /dev/null +++ b/src/vulkano/context.rs @@ -0,0 +1,12 @@ +use crate::vulkano::queues::Queues; +use std::sync::Arc; +use vulkano::device::Device; +use vulkano::instance::Instance; +use vulkano::memory::allocator::StandardMemoryAllocator; + +struct Context { + instance: Arc, + device: Arc, + queues: Queues, + memory_allocator: Arc, +} diff --git a/src/vulkano/mod.rs b/src/vulkano/mod.rs new file mode 100644 index 0000000..3d6d8f1 --- /dev/null +++ b/src/vulkano/mod.rs @@ -0,0 +1,3 @@ +pub mod context; +pub mod queues; +mod renderer; diff --git a/src/vulkano/queues.rs b/src/vulkano/queues.rs new file mode 100644 index 0000000..c602a2a --- /dev/null +++ b/src/vulkano/queues.rs @@ -0,0 +1,8 @@ +use std::sync::Arc; +use vulkano::device::Queue; + +pub struct Queues { + graphics_queue: Arc, + compute_queue: Arc, + transfer_queue: Option>, +} diff --git a/src/vulkano/renderer.rs b/src/vulkano/renderer.rs new file mode 100644 index 0000000..e69de29 From 62275f20d9953cacd179b98990341530625c9e8f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 1 Apr 2025 20:58:31 +0200 Subject: [PATCH 27/91] Revert begin rework vulkan --- src/main.rs | 1 - src/vulkano/context.rs | 12 ------------ src/vulkano/mod.rs | 3 --- src/vulkano/queues.rs | 8 -------- src/vulkano/renderer.rs | 0 5 files changed, 24 deletions(-) delete mode 100644 src/vulkano/context.rs delete mode 100644 src/vulkano/mod.rs delete mode 100644 src/vulkano/queues.rs delete mode 100644 src/vulkano/renderer.rs diff --git a/src/main.rs b/src/main.rs index 4c3f414..6c01c40 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; mod renderer; -mod vulkano; fn main() -> Result<(), impl Error> { env_logger::init(); diff --git a/src/vulkano/context.rs b/src/vulkano/context.rs deleted file mode 100644 index b4b5753..0000000 --- a/src/vulkano/context.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::vulkano::queues::Queues; -use std::sync::Arc; -use vulkano::device::Device; -use vulkano::instance::Instance; -use vulkano::memory::allocator::StandardMemoryAllocator; - -struct Context { - instance: Arc, - device: Arc, - queues: Queues, - memory_allocator: Arc, -} diff --git a/src/vulkano/mod.rs b/src/vulkano/mod.rs deleted file mode 100644 index 3d6d8f1..0000000 --- a/src/vulkano/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod context; -pub mod queues; -mod renderer; diff --git a/src/vulkano/queues.rs b/src/vulkano/queues.rs deleted file mode 100644 index c602a2a..0000000 --- a/src/vulkano/queues.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::sync::Arc; -use vulkano::device::Queue; - -pub struct Queues { - graphics_queue: Arc, - compute_queue: Arc, - transfer_queue: Option>, -} diff --git a/src/vulkano/renderer.rs b/src/vulkano/renderer.rs deleted file mode 100644 index e69de29..0000000 From 6bc3dbd53df570995c9145bd4daaa9773e56d76f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 1 Apr 2025 23:49:40 +0200 Subject: [PATCH 28/91] Migrate to bevy_ecs for more up to date ECS crates --- Cargo.lock | 550 ++++++++++++++++++++++++++++++++--------------------- Cargo.toml | 2 +- 2 files changed, 331 insertions(+), 221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e69aec8..e35561c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "const-random", "getrandom", "once_cell", "version_check", @@ -40,6 +41,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -117,48 +124,12 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "any_vec" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6ac04794a7749710e3c7f3c93222e3d04692993b69876d69393efd2565401a" - [[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" -[[package]] -name = "apecs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6df7760d4baebb17003dcf99134d8e3a63f487e146d58911f0bcd27afb185d1c" -dependencies = [ - "any_vec", - "apecs-derive", - "async-channel", - "itertools", - "log", - "moongraph", - "parking_lot", - "rayon", - "smallvec", - "snafu 0.8.5", -] - -[[package]] -name = "apecs-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0f3ddfd31fd5276fb8039b75dc4d284c21213757a969e480c6ef8fde494f3b" -dependencies = [ - "moongraph-macros-syntax", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "arrayref" version = "0.3.9" @@ -187,16 +158,35 @@ dependencies = [ ] [[package]] -name = "async-channel" -version = "1.9.0" +name = "assert_type_match" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", + "proc-macro2", + "quote", + "syn", ] +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -209,6 +199,127 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bevy_ecs" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1597106cc01e62e6217ccb662e0748b2ce330893f27c7dc17bac33e0bb99bca9" +dependencies = [ + "bevy_ecs_macros", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bitflags 2.9.0", + "concurrent-queue", + "derive_more", + "disqualified", + "fixedbitset 0.5.7", + "nonmax", + "petgraph", + "smallvec", +] + +[[package]] +name = "bevy_ecs_macros" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f453adf07712b39826bc5845e5b0887ce03204ee8359bbe6b40a9afda60564a1" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_macro_utils" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb6ded1ddc124ea214f6a2140e47a78d1fe79b0638dad39419cdeef2e1133f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_ptr" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89fe0b0b919146939481a3a7c38864face2c6d0fd2c73ab3d430dc693ecd9b11" + +[[package]] +name = "bevy_reflect" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ddbca0a39e88eff2c301dc794ee9d73a53f4b08d47b2c9b5a6aac182fae6217" +dependencies = [ + "assert_type_match", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "derive_more", + "disqualified", + "downcast-rs", + "erased-serde", + "serde", + "smallvec", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d62affb769db17d34ad0b75ff27eca94867e2acc8ea350c5eca97d102bd98709" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_tasks" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028630ddc355563bd567df1076db3515858aa26715ddf7467d2086f9b40e5ab1" +dependencies = [ + "async-executor", + "futures-channel", + "futures-lite", + "pin-project", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_utils" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63c2174d43a0de99f863c98a472370047a2bfa7d1e5cec8d9d647fb500905d9d" +dependencies = [ + "ahash", + "bevy_utils_proc_macros", + "getrandom", + "hashbrown 0.14.5", + "thread_local", + "tracing", + "web-time", +] + +[[package]] +name = "bevy_utils_proc_macros" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94847541f6dd2e28f54a9c2b0e857da5f2631e2201ebc25ce68781cdcb721391" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -230,17 +341,6 @@ dependencies = [ "objc2 0.5.2", ] -[[package]] -name = "broomdog" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ec65645d8167b03c07e049f114a878a11ab889f20c071d6f7b30bf88fbe5af" -dependencies = [ - "log", - "rustc-hash", - "snafu 0.8.5", -] - [[package]] name = "bumpalo" version = "3.17.0" @@ -264,7 +364,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -362,6 +462,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -402,25 +522,6 @@ dependencies = [ "libc", ] -[[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-queue" version = "0.3.12" @@ -449,14 +550,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] -name = "dagga" -version = "0.2.1" +name = "derive_more" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf0d7dcd307c9c5d81277737c35d1faf08af9e2cb262966a01c91021686b68" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "log", - "rustc-hash", - "snafu 0.7.5", + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] @@ -465,6 +576,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "disqualified" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd" + [[package]] name = "dlib" version = "0.5.2" @@ -474,12 +591,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "downcast-rs" version = "1.2.1" @@ -492,12 +603,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "env_filter" version = "0.1.3" @@ -527,6 +632,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.10" @@ -538,10 +653,22 @@ dependencies = [ ] [[package]] -name = "event-listener" -version = "2.5.3" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "foldhash" @@ -567,7 +694,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -576,12 +703,40 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -599,8 +754,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -620,6 +777,17 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -645,7 +813,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", ] [[package]] @@ -654,15 +822,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -690,7 +849,7 @@ checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -804,30 +963,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "moongraph" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a4b09eb96a84205062b48ec5469c8c35c128167e838aa73dc620c4411af598" -dependencies = [ - "broomdog", - "dagga", - "log", - "rayon", - "snafu 0.8.5", -] - -[[package]] -name = "moongraph-macros-syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08112087acc92cc28fb5d8f7bda1307123ecc9a275ed4835f1c03f1a8dd02c1d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ndk" version = "0.9.0" @@ -868,6 +1003,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + [[package]] name = "num_enum" version = "0.7.3" @@ -886,7 +1027,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1170,6 +1311,12 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1199,6 +1346,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -1216,7 +1373,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1315,26 +1472,6 @@ dependencies = [ "objc2-quartz-core 0.3.0", ] -[[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.4.1" @@ -1396,7 +1533,7 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", - "apecs", + "bevy_ecs", "env_logger", "glam", "log", @@ -1405,12 +1542,6 @@ dependencies = [ "winit", ] -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustix" version = "0.38.44" @@ -1487,7 +1618,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1584,66 +1715,12 @@ dependencies = [ "serde", ] -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive 0.7.5", -] - -[[package]] -name = "snafu" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" -dependencies = [ - "snafu-derive 0.8.5", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "snafu-derive" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.100" @@ -1672,7 +1749,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1685,6 +1762,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -1742,6 +1828,9 @@ name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] [[package]] name = "ttf-parser" @@ -1749,6 +1838,12 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -1761,12 +1856,27 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +dependencies = [ + "getrandom", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1823,7 +1933,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1837,7 +1947,7 @@ dependencies = [ "proc-macro2", "quote", "shaderc", - "syn 2.0.100", + "syn", "vulkano", ] @@ -1879,7 +1989,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn", "wasm-bindgen-shared", ] @@ -1914,7 +2024,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2418,5 +2528,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index ba7eb9d..a6ece63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ vulkano-shaders = "0.35" glam = { version = "0.30" } # ECS -apecs = "0.8" +bevy_ecs = "0.15.3" # Log and tracing log = "0.4" From f32db721011cf5ddb12f6e444b916fa62d8eaf0a Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 3 Apr 2025 18:38:17 +0200 Subject: [PATCH 29/91] rename renderer to vulkan --- src/main.rs | 4 ++-- src/{renderer => vulkan}/app.rs | 10 +++++----- src/{renderer => vulkan}/mod.rs | 0 src/{renderer => vulkan}/pipelines/mod.rs | 0 .../pipelines/triangle_pipeline.rs | 4 ++-- src/{renderer => vulkan}/render_context.rs | 0 src/{renderer => vulkan}/scene.rs | 4 ++-- src/{renderer => vulkan}/vertex.rs | 0 8 files changed, 11 insertions(+), 11 deletions(-) rename src/{renderer => vulkan}/app.rs (98%) rename src/{renderer => vulkan}/mod.rs (100%) rename src/{renderer => vulkan}/pipelines/mod.rs (100%) rename src/{renderer => vulkan}/pipelines/triangle_pipeline.rs (99%) rename src/{renderer => vulkan}/render_context.rs (100%) rename src/{renderer => vulkan}/scene.rs (96%) rename src/{renderer => vulkan}/vertex.rs (100%) diff --git a/src/main.rs b/src/main.rs index 6c01c40..e0ab6d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; -mod renderer; +mod vulkan; fn main() -> Result<(), impl Error> { env_logger::init(); @@ -9,7 +9,7 @@ fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let mut app = renderer::App::new(&event_loop); + let mut app = vulkan::App::new(&event_loop); event_loop.run_app(&mut app) } diff --git a/src/renderer/app.rs b/src/vulkan/app.rs similarity index 98% rename from src/renderer/app.rs rename to src/vulkan/app.rs index f791616..8a0397e 100644 --- a/src/renderer/app.rs +++ b/src/vulkan/app.rs @@ -1,8 +1,8 @@ -use crate::renderer::render_context::RenderContext; -use crate::renderer::Scene; +use crate::vulkan::Scene; +use crate::vulkan::render_context::RenderContext; use std::sync::Arc; -use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; use vulkano::buffer::BufferUsage; +use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, @@ -15,9 +15,9 @@ use vulkano::device::{ use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{acquire_next_image, Surface, SwapchainPresentInfo}; +use vulkano::swapchain::{Surface, SwapchainPresentInfo, acquire_next_image}; use vulkano::sync::GpuFuture; -use vulkano::{sync, Validated, Version, VulkanError, VulkanLibrary}; +use vulkano::{Validated, Version, VulkanError, VulkanLibrary, sync}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; diff --git a/src/renderer/mod.rs b/src/vulkan/mod.rs similarity index 100% rename from src/renderer/mod.rs rename to src/vulkan/mod.rs diff --git a/src/renderer/pipelines/mod.rs b/src/vulkan/pipelines/mod.rs similarity index 100% rename from src/renderer/pipelines/mod.rs rename to src/vulkan/pipelines/mod.rs diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/vulkan/pipelines/triangle_pipeline.rs similarity index 99% rename from src/renderer/pipelines/triangle_pipeline.rs rename to src/vulkan/pipelines/triangle_pipeline.rs index f6001f7..0c1554d 100644 --- a/src/renderer/pipelines/triangle_pipeline.rs +++ b/src/vulkan/pipelines/triangle_pipeline.rs @@ -5,6 +5,7 @@ use vulkano::descriptor_set::layout::{ DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, }; use vulkano::device::Device; +use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; use vulkano::pipeline::graphics::multisample::MultisampleState; @@ -12,7 +13,6 @@ use vulkano::pipeline::graphics::rasterization::RasterizationState; use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; use vulkano::pipeline::graphics::viewport::ViewportState; -use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; use vulkano::pipeline::{ DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, @@ -20,7 +20,7 @@ use vulkano::pipeline::{ use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; -use crate::renderer::Vertex2D; +use crate::vulkan::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/renderer/render_context.rs b/src/vulkan/render_context.rs similarity index 100% rename from src/renderer/render_context.rs rename to src/vulkan/render_context.rs diff --git a/src/renderer/scene.rs b/src/vulkan/scene.rs similarity index 96% rename from src/renderer/scene.rs rename to src/vulkan/scene.rs index e3c6b58..83863e2 100644 --- a/src/renderer/scene.rs +++ b/src/vulkan/scene.rs @@ -1,4 +1,4 @@ -use crate::renderer::pipelines::triangle_pipeline::shaders::vs; +use crate::vulkan::pipelines::triangle_pipeline::shaders::vs; use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; @@ -8,7 +8,7 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use crate::renderer::{pipelines::triangle_pipeline::create_triangle_pipeline, App, Vertex2D}; +use crate::vulkan::{App, Vertex2D, pipelines::triangle_pipeline::create_triangle_pipeline}; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche diff --git a/src/renderer/vertex.rs b/src/vulkan/vertex.rs similarity index 100% rename from src/renderer/vertex.rs rename to src/vulkan/vertex.rs From 15c273b93d35b0f0cdc7fc3fac987c159f7362c8 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 3 Apr 2025 19:59:10 +0200 Subject: [PATCH 30/91] Split app, window render context and vulkan context --- src/core/app.rs | 178 +++++++++++ src/core/mod.rs | 1 + src/main.rs | 6 +- src/vulkan/app.rs | 295 ------------------ src/vulkan/mod.rs | 13 +- src/vulkan/pipelines/triangle_pipeline.rs | 2 +- src/vulkan/scene.rs | 35 ++- src/vulkan/vulkan_context.rs | 203 ++++++++++++ ...er_context.rs => window_render_context.rs} | 18 +- 9 files changed, 426 insertions(+), 325 deletions(-) create mode 100644 src/core/app.rs create mode 100644 src/core/mod.rs delete mode 100644 src/vulkan/app.rs create mode 100644 src/vulkan/vulkan_context.rs rename src/vulkan/{render_context.rs => window_render_context.rs} (89%) diff --git a/src/core/app.rs b/src/core/app.rs new file mode 100644 index 0000000..93a7d56 --- /dev/null +++ b/src/core/app.rs @@ -0,0 +1,178 @@ +use crate::vulkan::scene::Scene; +use crate::vulkan::vulkan_context::VulkanContext; +use crate::vulkan::window_render_context::WindowRenderContext; +use std::sync::Arc; +use vulkano::command_buffer::{RenderingAttachmentInfo, RenderingInfo}; +use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; +use vulkano::swapchain::{SwapchainPresentInfo, acquire_next_image}; +use vulkano::sync::GpuFuture; +use vulkano::{Validated, VulkanError, sync}; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::ActiveEventLoop; +use winit::window::WindowId; + +pub struct App { + vulkan_context: VulkanContext, + window_render_context: Option, + scene: Option, +} + +impl From for App { + fn from(vulkan_context: VulkanContext) -> Self { + Self { + vulkan_context, + window_render_context: None, + scene: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_attributes = winit::window::Window::default_attributes() + .with_title("Rust ASH Test") + .with_inner_size(winit::dpi::PhysicalSize::new( + f64::from(800), + f64::from(600), + )); + + let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); + + let surface = self.vulkan_context.create_surface(window.clone()); + + self.window_render_context = Some(WindowRenderContext::new( + window, + surface, + &self.vulkan_context.device, + )); + self.scene = Some( + Scene::load( + &self.vulkan_context, + self.window_render_context.as_ref().unwrap(), + ) + .unwrap(), + ); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + WindowEvent::Resized(_) => { + let rcx = self.window_render_context.as_mut().unwrap(); + rcx.recreate_swapchain = true; + } + WindowEvent::RedrawRequested => { + let (image_index, acquire_future) = { + let rcx = self.window_render_context.as_mut().unwrap(); + let window_size = rcx.window.inner_size(); + + if window_size.width == 0 || window_size.height == 0 { + return; + } + + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.update_swapchain().unwrap(); + + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(rcx.swapchain.clone(), None) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + rcx.recreate_swapchain = true; + } + + (image_index, acquire_future) + }; + + let mut builder = self.vulkan_context.create_render_builder(); + + { + let rcx = self.window_render_context.as_ref().unwrap(); + builder + .begin_rendering(RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + ..RenderingAttachmentInfo::image_view( + rcx.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) + .unwrap(); + } + + if let Some(scene) = self.scene.as_ref() { + scene + .render( + &self.vulkan_context, + &self.window_render_context.as_ref().unwrap(), + &mut builder, + ) + .unwrap(); + } + + builder.end_rendering().unwrap(); + + let command_buffer = builder.build().unwrap(); + + { + let rcx = self.window_render_context.as_mut().unwrap(); + + let future = rcx + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.vulkan_context.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present( + self.vulkan_context.graphics_queue.clone(), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + rcx.previous_frame_end = Some(future.boxed()); + } + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + rcx.previous_frame_end = + Some(sync::now(self.vulkan_context.device.clone()).boxed()); + } + Err(e) => { + println!("failed to flush future: {e}"); + rcx.previous_frame_end = + Some(sync::now(self.vulkan_context.device.clone()).boxed()); + } + } + } + } + _ => {} + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.window_render_context.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..309be62 --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1 @@ +pub mod app; diff --git a/src/main.rs b/src/main.rs index e0ab6d4..54a1427 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; -mod vulkan; +pub mod core; +pub mod vulkan; fn main() -> Result<(), impl Error> { env_logger::init(); @@ -9,7 +10,8 @@ fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let mut app = vulkan::App::new(&event_loop); + let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); + let mut app = core::app::App::from(vulkan_context); event_loop.run_app(&mut app) } diff --git a/src/vulkan/app.rs b/src/vulkan/app.rs deleted file mode 100644 index 8a0397e..0000000 --- a/src/vulkan/app.rs +++ /dev/null @@ -1,295 +0,0 @@ -use crate::vulkan::Scene; -use crate::vulkan::render_context::RenderContext; -use std::sync::Arc; -use vulkano::buffer::BufferUsage; -use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; -use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; -use vulkano::command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, -}; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; -use vulkano::device::physical::PhysicalDeviceType; -use vulkano::device::{ - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, -}; -use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; -use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; -use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{Surface, SwapchainPresentInfo, acquire_next_image}; -use vulkano::sync::GpuFuture; -use vulkano::{Validated, Version, VulkanError, VulkanLibrary, sync}; -use winit::application::ApplicationHandler; -use winit::event::WindowEvent; -use winit::event_loop::{ActiveEventLoop, EventLoop}; -use winit::window::WindowId; - -pub struct App { - pub instance: Arc, - pub device: Arc, - pub queue: Arc, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub uniform_buffer_allocator: SubbufferAllocator, - pub descriptor_set_allocator: Arc, - - pub rcx: Option, - scene: Option, -} - -impl App { - pub fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - - for layer in library.layer_properties().unwrap() { - log::debug!("Available layer: {}", layer.name()); - } - - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - - let instance = Instance::new( - library, - InstanceCreateInfo { - // Enable enumerating devices that use non-conformant Vulkan implementations. - // (e.g. MoltenVK) - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], - ..Default::default() - }, - ) - .unwrap(); - - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .expect("no suitable physical device found"); - - log::debug!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - if physical_device.api_version() < Version::V1_3 { - device_extensions.khr_dynamic_rendering = true; - } - - log::debug!("Using device extensions: {:#?}", device_extensions); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, - ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let uniform_buffer_allocator = SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - - Self { - instance, - device, - queue, - memory_allocator, - command_buffer_allocator, - uniform_buffer_allocator, - descriptor_set_allocator, - rcx: None, - scene: None, - } - } -} - -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window_attributes = winit::window::Window::default_attributes() - .with_title("Rust ASH Test") - .with_inner_size(winit::dpi::PhysicalSize::new( - f64::from(800), - f64::from(600), - )); - - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - - self.rcx = Some(RenderContext::new(window, surface, &self.device)); - self.scene = Some(Scene::load(&self).unwrap()); - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => { - log::debug!("The close button was pressed; stopping"); - event_loop.exit(); - } - WindowEvent::Resized(_) => { - let rcx = self.rcx.as_mut().unwrap(); - rcx.recreate_swapchain = true; - } - WindowEvent::RedrawRequested => { - let (image_index, acquire_future) = { - let rcx = self.rcx.as_mut().unwrap(); - let window_size = rcx.window.inner_size(); - - if window_size.width == 0 || window_size.height == 0 { - return; - } - - rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - rcx.update_swapchain().unwrap(); - - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - if suboptimal { - rcx.recreate_swapchain = true; - } - - (image_index, acquire_future) - }; - - let mut builder = AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); - - { - let rcx = self.rcx.as_ref().unwrap(); - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - rcx.attachment_image_views[image_index as usize].clone(), - ) - })], - ..Default::default() - }) - .unwrap() - .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) - .unwrap(); - } - - if let Some(scene) = self.scene.as_ref() { - scene.render(&self, &mut builder).unwrap(); - } - - builder.end_rendering().unwrap(); - - let command_buffer = builder.build().unwrap(); - - { - let rcx = self.rcx.as_mut().unwrap(); - - let future = rcx - .previous_frame_end - .take() - .unwrap() - .join(acquire_future) - .then_execute(self.queue.clone(), command_buffer) - .unwrap() - .then_swapchain_present( - self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index( - rcx.swapchain.clone(), - image_index, - ), - ) - .then_signal_fence_and_flush(); - - match future.map_err(Validated::unwrap) { - Ok(future) => { - rcx.previous_frame_end = Some(future.boxed()); - } - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - } - Err(e) => { - println!("failed to flush future: {e}"); - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - } - } - } - } - _ => {} - } - } - - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let rcx = self.rcx.as_mut().unwrap(); - rcx.window.request_redraw(); - } -} diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index bbcff9a..56d8c07 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,9 +1,6 @@ -mod app; -mod pipelines; -mod render_context; -mod vertex; -pub use app::App; +pub mod pipelines; +pub mod vertex; +pub mod vulkan_context; +pub mod window_render_context; -mod scene; -pub use scene::Scene; -pub use vertex::Vertex2D; +pub mod scene; diff --git a/src/vulkan/pipelines/triangle_pipeline.rs b/src/vulkan/pipelines/triangle_pipeline.rs index 0c1554d..bb07389 100644 --- a/src/vulkan/pipelines/triangle_pipeline.rs +++ b/src/vulkan/pipelines/triangle_pipeline.rs @@ -20,7 +20,7 @@ use vulkano::pipeline::{ use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; -use crate::vulkan::Vertex2D; +use crate::vulkan::vertex::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/vulkan/scene.rs b/src/vulkan/scene.rs index 83863e2..1f77e5a 100644 --- a/src/vulkan/scene.rs +++ b/src/vulkan/scene.rs @@ -8,7 +8,10 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use crate::vulkan::{App, Vertex2D, pipelines::triangle_pipeline::create_triangle_pipeline}; +use crate::vulkan::{pipelines::triangle_pipeline::create_triangle_pipeline, vertex::Vertex2D}; + +use super::vulkan_context::VulkanContext; +use super::window_render_context::WindowRenderContext; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche @@ -73,10 +76,14 @@ pub struct Scene { } impl Scene { - pub fn load(app: &App) -> Result> { - let pipeline = create_triangle_pipeline(&app.device, &app.rcx.as_ref().unwrap().swapchain)?; + pub fn load( + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, + ) -> Result> { + let pipeline = + create_triangle_pipeline(&vulkan_context.device, &window_render_context.swapchain)?; let vertex_buffer = - Vertex2D::create_buffer(Vec::from_iter(VERTICES), &app.memory_allocator)?; + Vertex2D::create_buffer(Vec::from_iter(VERTICES), &vulkan_context.memory_allocator)?; Ok(Scene { pipeline, @@ -87,16 +94,17 @@ impl Scene { pub fn render( &self, - app: &App, + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, builder: &mut AutoCommandBufferBuilder, ) -> Result<(), Box> { let vertex_count = self.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; - let uniform_buffer = self.get_uniform_buffer(app); + let uniform_buffer = self.get_uniform_buffer(vulkan_context, window_render_context); let layout = &self.pipeline.layout().set_layouts()[0]; let descriptor_set = DescriptorSet::new( - app.descriptor_set_allocator.clone(), + vulkan_context.descriptor_set_allocator.clone(), layout.clone(), [WriteDescriptorSet::buffer(0, uniform_buffer)], [], @@ -119,8 +127,12 @@ impl Scene { Ok(()) } - fn get_uniform_buffer(&self, app: &App) -> Subbuffer { - let swapchain = &app.rcx.as_ref().unwrap().swapchain; + fn get_uniform_buffer( + &self, + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, + ) -> Subbuffer { + let swapchain = &window_render_context.swapchain; let elapsed = self.rotation_start.elapsed(); let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; let rotation = Mat3::from_rotation_y(rotation as f32); @@ -143,7 +155,10 @@ impl Scene { projection: proj.to_cols_array_2d(), }; - let buffer = app.uniform_buffer_allocator.allocate_sized().unwrap(); + let buffer = vulkan_context + .uniform_buffer_allocator + .allocate_sized() + .unwrap(); *buffer.write().unwrap() = uniform_data; buffer diff --git a/src/vulkan/vulkan_context.rs b/src/vulkan/vulkan_context.rs new file mode 100644 index 0000000..ef2c6f7 --- /dev/null +++ b/src/vulkan/vulkan_context.rs @@ -0,0 +1,203 @@ +use std::{any::Any, sync::Arc}; + +use vulkano::{ + Version, VulkanLibrary, + buffer::{ + BufferUsage, + allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, + }, + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + allocator::StandardCommandBufferAllocator, + }, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, + QueueFlags, physical::PhysicalDeviceType, + }, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}, + swapchain::Surface, +}; +use winit::{ + event_loop::EventLoop, + raw_window_handle::{HasDisplayHandle, HasWindowHandle}, +}; + +pub struct VulkanContext { + instance: Arc, + pub device: Arc, + pub graphics_queue: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub uniform_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +impl From<&EventLoop<()>> for VulkanContext { + fn from(event_loop: &EventLoop<()>) -> Self { + let library = load_library(); + + let enabled_extensions = Surface::required_extensions(event_loop).unwrap(); + + let instance = create_instance(library.clone(), enabled_extensions); + + let (device, mut queues) = pick_graphics_device(&instance, event_loop); + let graphics_queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let uniform_buffer_allocator = Arc::new(SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::UNIFORM_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + )); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + + Self { + instance, + device, + graphics_queue, + memory_allocator, + command_buffer_allocator, + uniform_buffer_allocator, + descriptor_set_allocator, + } + } +} + +impl VulkanContext { + pub fn create_surface( + &self, + window: Arc, + ) -> Arc { + Surface::from_window(self.instance.clone(), window).unwrap() + } + + pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { + AutoCommandBufferBuilder::primary( + self.command_buffer_allocator.clone(), + self.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap() + } +} + +fn load_library() -> Arc { + let library = VulkanLibrary::new().unwrap(); + + log::debug!("Available layer:"); + for layer in library.layer_properties().unwrap() { + log::debug!( + "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", + layer.name(), + layer.description(), + layer.implementation_version(), + layer.vulkan_version() + ); + } + + library +} + +fn create_instance( + library: Arc, + required_extensions: InstanceExtensions, +) -> Arc { + Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + ..Default::default() + }, + ) + .unwrap() +} + +fn pick_graphics_device( + instance: &Arc, + event_loop: &EventLoop<()>, +) -> ( + Arc, + impl ExactSizeIterator> + use<>, +) { + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .expect("no suitable physical device found"); + + log::debug!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + log::debug!("Using device extensions: {:#?}", device_extensions); + + Device::new( + physical_device, + DeviceCreateInfo { + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + ..Default::default() + }, + ) + .unwrap() +} diff --git a/src/vulkan/render_context.rs b/src/vulkan/window_render_context.rs similarity index 89% rename from src/vulkan/render_context.rs rename to src/vulkan/window_render_context.rs index dd7e840..54120d0 100644 --- a/src/vulkan/render_context.rs +++ b/src/vulkan/window_render_context.rs @@ -5,19 +5,19 @@ use vulkano::image::{Image, ImageUsage}; use vulkano::pipeline::graphics::viewport::Viewport; use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use vulkano::sync::GpuFuture; -use vulkano::{sync, Validated, VulkanError}; +use vulkano::{Validated, VulkanError, sync}; use winit::window::Window; -pub struct RenderContext { - pub(super) window: Arc, - pub(super) swapchain: Arc, - pub(super) attachment_image_views: Vec>, - pub(super) viewport: Viewport, - pub(super) recreate_swapchain: bool, - pub(super) previous_frame_end: Option>, +pub struct WindowRenderContext { + pub window: Arc, + pub swapchain: Arc, + pub attachment_image_views: Vec>, + pub viewport: Viewport, + pub recreate_swapchain: bool, + pub previous_frame_end: Option>, } -impl RenderContext { +impl WindowRenderContext { pub fn new(window: Arc, surface: Arc, device: &Arc) -> Self { let window_size = window.inner_size(); From 2fbf4e6ce2ff41a2234fda87da49439a35f9191b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 3 Apr 2025 21:10:08 +0200 Subject: [PATCH 31/91] Split pick_graphics_device --- src/vulkan/vulkan_context.rs | 81 +++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/vulkan/vulkan_context.rs b/src/vulkan/vulkan_context.rs index ef2c6f7..ea1b29b 100644 --- a/src/vulkan/vulkan_context.rs +++ b/src/vulkan/vulkan_context.rs @@ -13,7 +13,8 @@ use vulkano::{ descriptor_set::allocator::StandardDescriptorSetAllocator, device::{ Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, - QueueFlags, physical::PhysicalDeviceType, + QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}, @@ -133,6 +134,53 @@ fn create_instance( .unwrap() } +fn find_physical_device_queue_family_indexes( + physical_device: &Arc, + event_loop: &EventLoop<()>, +) -> Option { + let mut graphic_queue_family_index = None; + + for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { + if queue_family_property + .queue_flags + .intersects(QueueFlags::GRAPHICS) + && physical_device + .presentation_support(i as u32, event_loop) + .unwrap() + { + graphic_queue_family_index = Some(i as u32); + } + } + + graphic_queue_family_index +} + +fn pick_physical_device_and_queue_family_indexes( + instance: &Arc, + event_loop: &EventLoop<()>, + device_extensions: &DeviceExtensions, +) -> Option<(Arc, u32)> { + instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| p.supported_extensions().contains(device_extensions)) + .filter_map(|p| { + find_physical_device_queue_family_indexes(&p, event_loop) + .and_then(|indexes| Some((p, indexes))) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) +} + fn pick_graphics_device( instance: &Arc, event_loop: &EventLoop<()>, @@ -145,32 +193,9 @@ fn pick_graphics_device( ..DeviceExtensions::empty() }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .expect("no suitable physical device found"); + let (physical_device, graphics_family_index) = + pick_physical_device_and_queue_family_indexes(instance, event_loop, &device_extensions) + .unwrap(); log::debug!( "Using device: {} (type: {:?})", @@ -188,7 +213,7 @@ fn pick_graphics_device( physical_device, DeviceCreateInfo { queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + queue_family_index: graphics_family_index, ..Default::default() }], enabled_extensions: device_extensions, From 852d72d716710ac0e9c30298d26371c7d98bdbf5 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 4 Apr 2025 13:38:27 +0200 Subject: [PATCH 32/91] Begin move mesh + Vertex and Camera into core --- src/core/camera/mod.rs | 19 +++++++++++++++++++ src/core/mod.rs | 3 +++ src/core/render/mesh.rs | 14 ++++++++++++++ src/core/render/mod.rs | 2 ++ src/{vulkan => core/render}/vertex.rs | 0 src/core/scene.rs | 21 +++++++++++++++++++++ src/game/mod.rs | 1 + src/main.rs | 1 + src/vulkan/mod.rs | 1 - src/vulkan/pipelines/triangle_pipeline.rs | 2 +- src/vulkan/scene.rs | 3 ++- 11 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/core/camera/mod.rs create mode 100644 src/core/render/mesh.rs create mode 100644 src/core/render/mod.rs rename src/{vulkan => core/render}/vertex.rs (100%) create mode 100644 src/core/scene.rs create mode 100644 src/game/mod.rs diff --git a/src/core/camera/mod.rs b/src/core/camera/mod.rs new file mode 100644 index 0000000..f9b6925 --- /dev/null +++ b/src/core/camera/mod.rs @@ -0,0 +1,19 @@ +use bevy_ecs::component::Component; +use glam::{Mat4, Quat, Vec3}; + +pub trait Camera: Into + Component {} + +#[derive(Component)] +pub struct Camera3D { + pub projection: Mat4, + pub position: Vec3, + pub rotation: Quat, +} + +impl Into for Camera3D { + fn into(self) -> Mat4 { + Mat4::from_rotation_translation(self.rotation, self.position) * self.projection + } +} + +impl Camera for Camera3D {} diff --git a/src/core/mod.rs b/src/core/mod.rs index 309be62..f058532 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1 +1,4 @@ pub mod app; +pub mod camera; +pub mod render; +pub mod scene; diff --git a/src/core/render/mesh.rs b/src/core/render/mesh.rs new file mode 100644 index 0000000..ca5ec0f --- /dev/null +++ b/src/core/render/mesh.rs @@ -0,0 +1,14 @@ +use bevy_ecs::component::Component; + +use super::vertex::Vertex2D; + +#[derive(Component)] +pub struct Mesh2D { + pub vertices: Vec, +} + +impl Mesh2D { + pub fn new(vertices: Vec) -> Self { + Self { vertices } + } +} diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs new file mode 100644 index 0000000..6f29814 --- /dev/null +++ b/src/core/render/mod.rs @@ -0,0 +1,2 @@ +pub mod mesh; +pub mod vertex; diff --git a/src/vulkan/vertex.rs b/src/core/render/vertex.rs similarity index 100% rename from src/vulkan/vertex.rs rename to src/core/render/vertex.rs diff --git a/src/core/scene.rs b/src/core/scene.rs new file mode 100644 index 0000000..a424da3 --- /dev/null +++ b/src/core/scene.rs @@ -0,0 +1,21 @@ +use bevy_ecs::world::World; + +pub struct Scene { + world: World, +} + +impl Scene { + pub fn new() -> Self { + Self { + world: World::new(), + } + } + + pub fn world(&self) -> &World { + &self.world + } + + pub fn world_mut(&mut self) -> &mut World { + &mut self.world + } +} diff --git a/src/game/mod.rs b/src/game/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/game/mod.rs @@ -0,0 +1 @@ + diff --git a/src/main.rs b/src/main.rs index 54a1427..2b80274 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; pub mod core; +pub mod game; pub mod vulkan; fn main() -> Result<(), impl Error> { diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 56d8c07..dc3d731 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,5 +1,4 @@ pub mod pipelines; -pub mod vertex; pub mod vulkan_context; pub mod window_render_context; diff --git a/src/vulkan/pipelines/triangle_pipeline.rs b/src/vulkan/pipelines/triangle_pipeline.rs index bb07389..e573747 100644 --- a/src/vulkan/pipelines/triangle_pipeline.rs +++ b/src/vulkan/pipelines/triangle_pipeline.rs @@ -20,7 +20,7 @@ use vulkano::pipeline::{ use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; -use crate::vulkan::vertex::Vertex2D; +use crate::core::render::vertex::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/vulkan/scene.rs b/src/vulkan/scene.rs index 1f77e5a..4d1de95 100644 --- a/src/vulkan/scene.rs +++ b/src/vulkan/scene.rs @@ -8,7 +8,8 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use crate::vulkan::{pipelines::triangle_pipeline::create_triangle_pipeline, vertex::Vertex2D}; +use crate::core::render::vertex::Vertex2D; +use crate::vulkan::pipelines::triangle_pipeline::create_triangle_pipeline; use super::vulkan_context::VulkanContext; use super::window_render_context::WindowRenderContext; From 9664ea754ca4a0cda52a76603391699e9ce9fad7 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 7 Apr 2025 13:11:19 +0200 Subject: [PATCH 33/91] flake: Fix missing libstdc++ --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 565976b..4f7cd2e 100644 --- a/flake.nix +++ b/flake.nix @@ -39,6 +39,8 @@ buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ + stdenv.cc.cc.lib + # Wayland libxkbcommon wayland From 1d333b633b19e0691a760e504df4cc17185bf392 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 7 Apr 2025 17:03:00 +0200 Subject: [PATCH 34/91] Push lunch break code --- src/core/render/material/mod.rs | 10 ++++++++ src/core/render/material/triangle.rs | 34 ++++++++++++++++++++++++++++ src/core/render/mod.rs | 1 + 3 files changed, 45 insertions(+) create mode 100644 src/core/render/material/mod.rs create mode 100644 src/core/render/material/triangle.rs diff --git a/src/core/render/material/mod.rs b/src/core/render/material/mod.rs new file mode 100644 index 0000000..8cccde2 --- /dev/null +++ b/src/core/render/material/mod.rs @@ -0,0 +1,10 @@ +pub mod triangle; + +use vulkano::command_buffer::CommandBuffer; + +use crate::vulkan::vulkan_context::VulkanContext; + +pub trait Material { + fn load(&self, vulkan_context: &VulkanContext); + fn bind(&self, command_buffer: &mut CommandBuffer); +} diff --git a/src/core/render/material/triangle.rs b/src/core/render/material/triangle.rs new file mode 100644 index 0000000..c3bafaa --- /dev/null +++ b/src/core/render/material/triangle.rs @@ -0,0 +1,34 @@ +use glam::Vec4; +use vulkano::command_buffer::CommandBuffer; + +use super::Material; + +pub mod shaders { + pub mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/vertex.vert", + } + } + + pub mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/vertex.frag", + } + } +} + +pub struct TriangleMaterial { + pub colors: [Vec4; 3], +} + +impl Material for TriangleMaterial { + fn bind(&self, command_buffer: &mut CommandBuffer) { + todo!() + } + + fn load(&self, vulkan_context: &crate::vulkan::vulkan_context::VulkanContext) { + todo!() + } +} diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index 6f29814..5d003a8 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,2 +1,3 @@ +pub mod material; pub mod mesh; pub mod vertex; From b361965033337551f488e39f4bd81b945abf4754 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 7 Apr 2025 22:51:49 +0200 Subject: [PATCH 35/91] Update --- src/core/render/material.rs | 7 ++++++ src/core/render/material/mod.rs | 10 -------- src/core/render/material/triangle.rs | 34 ---------------------------- src/core/render/vertex.rs | 2 +- src/vulkan/scene.rs | 24 +++++++++++++------- src/vulkan/vulkan_context.rs | 18 +-------------- 6 files changed, 25 insertions(+), 70 deletions(-) create mode 100644 src/core/render/material.rs delete mode 100644 src/core/render/material/mod.rs delete mode 100644 src/core/render/material/triangle.rs diff --git a/src/core/render/material.rs b/src/core/render/material.rs new file mode 100644 index 0000000..539d4bc --- /dev/null +++ b/src/core/render/material.rs @@ -0,0 +1,7 @@ +use std::sync::Arc; + +use bevy_ecs::component::Component; +use vulkano::pipeline::GraphicsPipeline; + +#[derive(Component)] +pub struct Material(pub Arc); diff --git a/src/core/render/material/mod.rs b/src/core/render/material/mod.rs deleted file mode 100644 index 8cccde2..0000000 --- a/src/core/render/material/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod triangle; - -use vulkano::command_buffer::CommandBuffer; - -use crate::vulkan::vulkan_context::VulkanContext; - -pub trait Material { - fn load(&self, vulkan_context: &VulkanContext); - fn bind(&self, command_buffer: &mut CommandBuffer); -} diff --git a/src/core/render/material/triangle.rs b/src/core/render/material/triangle.rs deleted file mode 100644 index c3bafaa..0000000 --- a/src/core/render/material/triangle.rs +++ /dev/null @@ -1,34 +0,0 @@ -use glam::Vec4; -use vulkano::command_buffer::CommandBuffer; - -use super::Material; - -pub mod shaders { - pub mod vs { - vulkano_shaders::shader! { - ty: "vertex", - path: r"res/shaders/vertex.vert", - } - } - - pub mod fs { - vulkano_shaders::shader! { - ty: "fragment", - path: r"res/shaders/vertex.frag", - } - } -} - -pub struct TriangleMaterial { - pub colors: [Vec4; 3], -} - -impl Material for TriangleMaterial { - fn bind(&self, command_buffer: &mut CommandBuffer) { - todo!() - } - - fn load(&self, vulkan_context: &crate::vulkan::vulkan_context::VulkanContext) { - todo!() - } -} diff --git a/src/core/render/vertex.rs b/src/core/render/vertex.rs index fc2ee21..9bf133e 100644 --- a/src/core/render/vertex.rs +++ b/src/core/render/vertex.rs @@ -1,10 +1,10 @@ use std::sync::Arc; +use vulkano::Validated; use vulkano::buffer::{ AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, }; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::graphics::vertex_input::Vertex; -use vulkano::Validated; #[derive(BufferContents, Vertex)] #[repr(C)] diff --git a/src/vulkan/scene.rs b/src/vulkan/scene.rs index 4d1de95..22986ff 100644 --- a/src/vulkan/scene.rs +++ b/src/vulkan/scene.rs @@ -3,9 +3,10 @@ use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; use std::time::Instant; -use vulkano::buffer::Subbuffer; +use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use crate::core::render::vertex::Vertex2D; @@ -156,12 +157,19 @@ impl Scene { projection: proj.to_cols_array_2d(), }; - let buffer = vulkan_context - .uniform_buffer_allocator - .allocate_sized() - .unwrap(); - *buffer.write().unwrap() = uniform_data; - - buffer + Buffer::from_data( + vulkan_context.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::UNIFORM_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + uniform_data, + ) + .unwrap() } } diff --git a/src/vulkan/vulkan_context.rs b/src/vulkan/vulkan_context.rs index ea1b29b..bad47de 100644 --- a/src/vulkan/vulkan_context.rs +++ b/src/vulkan/vulkan_context.rs @@ -2,10 +2,6 @@ use std::{any::Any, sync::Arc}; use vulkano::{ Version, VulkanLibrary, - buffer::{ - BufferUsage, - allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, - }, command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, allocator::StandardCommandBufferAllocator, @@ -17,7 +13,7 @@ use vulkano::{ physical::{PhysicalDevice, PhysicalDeviceType}, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, - memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}, + memory::allocator::StandardMemoryAllocator, swapchain::Surface, }; use winit::{ @@ -32,7 +28,6 @@ pub struct VulkanContext { pub memory_allocator: Arc, pub command_buffer_allocator: Arc, - pub uniform_buffer_allocator: Arc, pub descriptor_set_allocator: Arc, } @@ -54,16 +49,6 @@ impl From<&EventLoop<()>> for VulkanContext { Default::default(), )); - let uniform_buffer_allocator = Arc::new(SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - )); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( device.clone(), Default::default(), @@ -75,7 +60,6 @@ impl From<&EventLoop<()>> for VulkanContext { graphics_queue, memory_allocator, command_buffer_allocator, - uniform_buffer_allocator, descriptor_set_allocator, } } From f4694157ab53d3223089ad0032964dd67ca500da Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 16:35:21 +0200 Subject: [PATCH 36/91] Update --- src/core/app/app.rs | 54 +++++++++++++++++++++++++++++++ src/core/app/mod.rs | 4 +++ src/core/app/plugin.rs | 5 +++ src/core/mod.rs | 2 ++ src/core/{app.rs => old_app.rs} | 0 src/core/window/mod.rs | 24 ++++++++++++++ src/core/window/window_handler.rs | 40 +++++++++++++++++++++++ src/main.rs | 17 ++++++++-- 8 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 src/core/app/app.rs create mode 100644 src/core/app/mod.rs create mode 100644 src/core/app/plugin.rs rename src/core/{app.rs => old_app.rs} (100%) create mode 100644 src/core/window/mod.rs create mode 100644 src/core/window/window_handler.rs diff --git a/src/core/app/app.rs b/src/core/app/app.rs new file mode 100644 index 0000000..12b4be7 --- /dev/null +++ b/src/core/app/app.rs @@ -0,0 +1,54 @@ +use std::error::Error; + +use bevy_ecs::{schedule::Schedules, world::World}; + +pub enum AppExit { + Success, + Error(Box), +} + +pub type RunnerFn = Box AppExit>; + +pub struct App { + world: World, + runner: Option, +} + +impl Default for App { + fn default() -> Self { + let mut world = World::new(); + world.init_resource::(); + + Self { + world, + runner: None, + } + } +} + +impl App { + pub fn world_mut(&mut self) -> &mut World { + &mut self.world + } + + pub fn world(&self) -> &World { + &self.world + } + + pub fn run(&mut self) -> Result<(), Box> { + match self.runner.take() { + Some(runner) => match runner() { + AppExit::Success => Ok(()), + AppExit::Error(e) => Err(e), + }, + None => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "runner is not set", + ))), + } + } + + pub fn set_runner(&mut self, runner: RunnerFn) { + self.runner = Some(runner); + } +} diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs new file mode 100644 index 0000000..f7320aa --- /dev/null +++ b/src/core/app/mod.rs @@ -0,0 +1,4 @@ +mod app; +pub mod plugin; + +pub use app::App; diff --git a/src/core/app/plugin.rs b/src/core/app/plugin.rs new file mode 100644 index 0000000..987c540 --- /dev/null +++ b/src/core/app/plugin.rs @@ -0,0 +1,5 @@ +use super::app::App; + +pub trait Plugin { + fn build(&self, app: &mut App); +} diff --git a/src/core/mod.rs b/src/core/mod.rs index f058532..07427ac 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,6 @@ pub mod app; pub mod camera; +pub mod old_app; pub mod render; pub mod scene; +pub mod window; diff --git a/src/core/app.rs b/src/core/old_app.rs similarity index 100% rename from src/core/app.rs rename to src/core/old_app.rs diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs new file mode 100644 index 0000000..55f6a70 --- /dev/null +++ b/src/core/window/mod.rs @@ -0,0 +1,24 @@ +pub mod window_handler; + +use super::app::{App, plugin::Plugin}; +use window_handler::WindowHandler; +use winit::event_loop::EventLoop; +use winit::window::WindowAttributes; + +pub struct WindowPlugin { + window_attributes: WindowAttributes, + event_loop: EventLoop<()>, +} + +impl Plugin for WindowPlugin { + fn build(&self, app: &mut App) { + let world = app.world_mut(); + world.insert_resource(WindowHandler::new(self.window_attributes.clone())); + + let window_handler = world.get_resource_mut::().unwrap(); + + // app.set_runner(Box::new(move || { + // self.event_loop.run_app(&mut window_handler); + // })); + } +} diff --git a/src/core/window/window_handler.rs b/src/core/window/window_handler.rs new file mode 100644 index 0000000..2b87d35 --- /dev/null +++ b/src/core/window/window_handler.rs @@ -0,0 +1,40 @@ +use bevy_ecs::system::Resource; +use winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::ActiveEventLoop, + window::{Window, WindowAttributes, WindowId}, +}; + +#[derive(Resource)] +pub struct WindowHandler { + window_attributes: WindowAttributes, + window: Option>, +} + +impl WindowHandler { + pub fn new(window_attributes: WindowAttributes) -> Self { + Self { + window_attributes, + window: None, + } + } +} +impl ApplicationHandler for WindowHandler { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = event_loop + .create_window(self.window_attributes.clone()) + .unwrap(); + self.window = Some(Box::new(window)); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + _ => {} + } + } +} diff --git a/src/main.rs b/src/main.rs index 2b80274..f09236f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,25 @@ pub mod core; pub mod game; pub mod vulkan; -fn main() -> Result<(), impl Error> { +fn main() -> Result<(), Box> { env_logger::init(); + run_old_app() +} + +fn run_new_app() -> Result<(), Box> { + let mut app = core::app::App::default(); + app.run() +} + +fn run_old_app() -> Result<(), Box> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); - let mut app = core::app::App::from(vulkan_context); + let mut app = core::old_app::App::from(vulkan_context); - event_loop.run_app(&mut app) + event_loop.run_app(&mut app).map_err(Box::new)?; + + Ok(()) } From df99ef3a3f1641f4f753bb67de76f99c288532fc Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 16:49:07 +0200 Subject: [PATCH 37/91] Add AppError --- Cargo.lock | 33 +++++++++++++++++++++++++++------ Cargo.toml | 1 + src/core/app/app.rs | 21 +++++++++++++-------- src/main.rs | 14 ++++++-------- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e35561c..da7454f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -384,7 +384,7 @@ dependencies = [ "polling", "rustix", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -863,7 +863,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -975,7 +975,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1537,6 +1537,7 @@ dependencies = [ "env_logger", "glam", "log", + "thiserror 2.0.12", "vulkano", "vulkano-shaders", "winit", @@ -1695,7 +1696,7 @@ dependencies = [ "log", "memmap2", "rustix", - "thiserror", + "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -1738,7 +1739,16 @@ 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]] @@ -1752,6 +1762,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" diff --git a/Cargo.toml b/Cargo.toml index a6ece63..a84f681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] anyhow = "1.0" +thiserror = "2.0" winit = { version = "0.30", features = ["rwh_06"] } vulkano = "0.35" diff --git a/src/core/app/app.rs b/src/core/app/app.rs index 12b4be7..21ebd28 100644 --- a/src/core/app/app.rs +++ b/src/core/app/app.rs @@ -7,7 +7,15 @@ pub enum AppExit { Error(Box), } -pub type RunnerFn = Box AppExit>; +pub type RunnerFn = Box AppExit>; + +#[derive(Debug, thiserror::Error)] +pub enum AppError { + #[error("Runner is not set")] + RunnerNotSet, + #[error("Runner returned an error : {0}")] + RunnerError(Box), +} pub struct App { world: World, @@ -35,16 +43,13 @@ impl App { &self.world } - pub fn run(&mut self) -> Result<(), Box> { + pub fn run(mut self) -> Result<(), AppError> { match self.runner.take() { - Some(runner) => match runner() { + Some(runner) => match runner(self) { AppExit::Success => Ok(()), - AppExit::Error(e) => Err(e), + AppExit::Error(e) => Err(AppError::RunnerError(e)), }, - None => Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "runner is not set", - ))), + None => Err(AppError::RunnerNotSet), } } diff --git a/src/main.rs b/src/main.rs index f09236f..9412fad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,25 +5,23 @@ pub mod core; pub mod game; pub mod vulkan; -fn main() -> Result<(), Box> { +fn main() -> Result<(), impl Error> { env_logger::init(); - run_old_app() + run_new_app() } -fn run_new_app() -> Result<(), Box> { - let mut app = core::app::App::default(); +fn run_new_app() -> Result<(), impl Error> { + let app = core::app::App::default(); app.run() } -fn run_old_app() -> Result<(), Box> { +fn run_old_app() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); let mut app = core::old_app::App::from(vulkan_context); - event_loop.run_app(&mut app).map_err(Box::new)?; - - Ok(()) + event_loop.run_app(&mut app) } From 4f6216635f7fe88577f6e05b624ceb6a19589a5b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 18:06:18 +0200 Subject: [PATCH 38/91] Update --- src/core/app/mod.rs | 4 +- src/core/app/plugin.rs | 5 --- src/core/mod.rs | 2 - src/core/scene.rs | 21 --------- src/core/window/config.rs | 17 +++++++ src/core/window/mod.rs | 37 ++++++++------- src/core/window/state.rs | 45 +++++++++++++++++++ src/core/window/window_handler.rs | 40 ----------------- src/game/mod.rs | 13 ++++++ src/main.rs | 12 ++--- src/{core/old_app.rs => old_app/app.rs} | 6 +-- src/{vulkan => old_app}/mod.rs | 4 +- src/{vulkan => old_app}/pipelines/mod.rs | 0 .../pipelines/triangle_pipeline.rs | 0 src/{vulkan => old_app}/scene.rs | 4 +- src/{vulkan => old_app}/vulkan_context.rs | 0 .../window_render_context.rs | 0 17 files changed, 110 insertions(+), 100 deletions(-) delete mode 100644 src/core/app/plugin.rs delete mode 100644 src/core/scene.rs create mode 100644 src/core/window/config.rs create mode 100644 src/core/window/state.rs delete mode 100644 src/core/window/window_handler.rs rename src/{core/old_app.rs => old_app/app.rs} (97%) rename src/{vulkan => old_app}/mod.rs (87%) rename src/{vulkan => old_app}/pipelines/mod.rs (100%) rename src/{vulkan => old_app}/pipelines/triangle_pipeline.rs (100%) rename src/{vulkan => old_app}/scene.rs (97%) rename src/{vulkan => old_app}/vulkan_context.rs (100%) rename src/{vulkan => old_app}/window_render_context.rs (100%) diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs index f7320aa..c6c8a20 100644 --- a/src/core/app/mod.rs +++ b/src/core/app/mod.rs @@ -1,4 +1,2 @@ mod app; -pub mod plugin; - -pub use app::App; +pub use app::*; diff --git a/src/core/app/plugin.rs b/src/core/app/plugin.rs deleted file mode 100644 index 987c540..0000000 --- a/src/core/app/plugin.rs +++ /dev/null @@ -1,5 +0,0 @@ -use super::app::App; - -pub trait Plugin { - fn build(&self, app: &mut App); -} diff --git a/src/core/mod.rs b/src/core/mod.rs index 07427ac..bd7ce34 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,6 +1,4 @@ pub mod app; pub mod camera; -pub mod old_app; pub mod render; -pub mod scene; pub mod window; diff --git a/src/core/scene.rs b/src/core/scene.rs deleted file mode 100644 index a424da3..0000000 --- a/src/core/scene.rs +++ /dev/null @@ -1,21 +0,0 @@ -use bevy_ecs::world::World; - -pub struct Scene { - world: World, -} - -impl Scene { - pub fn new() -> Self { - Self { - world: World::new(), - } - } - - pub fn world(&self) -> &World { - &self.world - } - - pub fn world_mut(&mut self) -> &mut World { - &mut self.world - } -} diff --git a/src/core/window/config.rs b/src/core/window/config.rs new file mode 100644 index 0000000..8fbef8c --- /dev/null +++ b/src/core/window/config.rs @@ -0,0 +1,17 @@ +use bevy_ecs::system::Resource; +use winit::{dpi::PhysicalSize, window::WindowAttributes}; + +#[derive(Resource, Clone)] +pub struct WindowConfig { + pub title: String, + pub width: u32, + pub height: u32, +} + +impl Into for &WindowConfig { + fn into(self) -> WindowAttributes { + WindowAttributes::default() + .with_title(self.title.clone()) + .with_inner_size(PhysicalSize::new(self.width as f64, self.height as f64)) + } +} diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index 55f6a70..e600120 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,24 +1,27 @@ -pub mod window_handler; - -use super::app::{App, plugin::Plugin}; -use window_handler::WindowHandler; +use config::WindowConfig; +use state::WindowState; use winit::event_loop::EventLoop; -use winit::window::WindowAttributes; -pub struct WindowPlugin { - window_attributes: WindowAttributes, - event_loop: EventLoop<()>, +use super::app::{App, AppExit}; + +pub mod config; +pub mod state; + +pub fn init(app: &mut App, window_config: WindowConfig) { + let world = app.world_mut(); + world.insert_resource(window_config); + + let mut event_loop_builder = EventLoop::with_user_event(); + let event_loop = event_loop_builder.build().unwrap(); + + app.set_runner(Box::new(move |app| runner(app, event_loop))); } -impl Plugin for WindowPlugin { - fn build(&self, app: &mut App) { - let world = app.world_mut(); - world.insert_resource(WindowHandler::new(self.window_attributes.clone())); +fn runner(app: App, event_loop: EventLoop<()>) -> AppExit { + let mut window_state = WindowState::new(app); - let window_handler = world.get_resource_mut::().unwrap(); - - // app.set_runner(Box::new(move || { - // self.event_loop.run_app(&mut window_handler); - // })); + match event_loop.run_app(&mut window_state) { + Ok(_) => AppExit::Success, + Err(e) => AppExit::Error(Box::new(e)), } } diff --git a/src/core/window/state.rs b/src/core/window/state.rs new file mode 100644 index 0000000..94bfcc7 --- /dev/null +++ b/src/core/window/state.rs @@ -0,0 +1,45 @@ +use bevy_ecs::world::World; +use winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::ActiveEventLoop, + window::{Window, WindowId}, +}; + +use crate::core::app::App; + +use super::config::WindowConfig; + +pub struct WindowState { + app: App, + window: Option, +} + +impl WindowState { + pub fn new(app: App) -> Self { + Self { app, window: None } + } + + fn world(&self) -> &World { + self.app.world() + } +} + +impl ApplicationHandler for WindowState { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_config = self.world().get_resource::().unwrap(); + + let window = event_loop.create_window(window_config.into()).unwrap(); + self.window = Some(window); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + _ => {} + } + } +} diff --git a/src/core/window/window_handler.rs b/src/core/window/window_handler.rs deleted file mode 100644 index 2b87d35..0000000 --- a/src/core/window/window_handler.rs +++ /dev/null @@ -1,40 +0,0 @@ -use bevy_ecs::system::Resource; -use winit::{ - application::ApplicationHandler, - event::WindowEvent, - event_loop::ActiveEventLoop, - window::{Window, WindowAttributes, WindowId}, -}; - -#[derive(Resource)] -pub struct WindowHandler { - window_attributes: WindowAttributes, - window: Option>, -} - -impl WindowHandler { - pub fn new(window_attributes: WindowAttributes) -> Self { - Self { - window_attributes, - window: None, - } - } -} -impl ApplicationHandler for WindowHandler { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = event_loop - .create_window(self.window_attributes.clone()) - .unwrap(); - self.window = Some(Box::new(window)); - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => { - log::debug!("The close button was pressed; stopping"); - event_loop.exit(); - } - _ => {} - } - } -} diff --git a/src/game/mod.rs b/src/game/mod.rs index 8b13789..ffee10f 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1 +1,14 @@ +use crate::core::{ + app::App, + window::{self, config::WindowConfig}, +}; +pub fn init(app: &mut App) { + let window_config = WindowConfig { + title: "Rust ASH Test".to_string(), + width: 800, + height: 600, + }; + + window::init(app, window_config); +} diff --git a/src/main.rs b/src/main.rs index 9412fad..c981713 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,16 +3,18 @@ use winit::event_loop::{ControlFlow, EventLoop}; pub mod core; pub mod game; -pub mod vulkan; +pub mod old_app; fn main() -> Result<(), impl Error> { env_logger::init(); - run_new_app() + // run_new_app() + run_old_app() } fn run_new_app() -> Result<(), impl Error> { - let app = core::app::App::default(); + let mut app = core::app::App::default(); + game::init(&mut app); app.run() } @@ -20,8 +22,8 @@ fn run_old_app() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); - let mut app = core::old_app::App::from(vulkan_context); + let vulkan_context = old_app::vulkan_context::VulkanContext::from(&event_loop); + let mut app = old_app::app::App::from(vulkan_context); event_loop.run_app(&mut app) } diff --git a/src/core/old_app.rs b/src/old_app/app.rs similarity index 97% rename from src/core/old_app.rs rename to src/old_app/app.rs index 93a7d56..cfcf038 100644 --- a/src/core/old_app.rs +++ b/src/old_app/app.rs @@ -1,6 +1,6 @@ -use crate::vulkan::scene::Scene; -use crate::vulkan::vulkan_context::VulkanContext; -use crate::vulkan::window_render_context::WindowRenderContext; +use crate::old_app::scene::Scene; +use crate::old_app::vulkan_context::VulkanContext; +use crate::old_app::window_render_context::WindowRenderContext; use std::sync::Arc; use vulkano::command_buffer::{RenderingAttachmentInfo, RenderingInfo}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; diff --git a/src/vulkan/mod.rs b/src/old_app/mod.rs similarity index 87% rename from src/vulkan/mod.rs rename to src/old_app/mod.rs index dc3d731..9fce0a9 100644 --- a/src/vulkan/mod.rs +++ b/src/old_app/mod.rs @@ -1,5 +1,5 @@ +pub mod app; pub mod pipelines; +pub mod scene; pub mod vulkan_context; pub mod window_render_context; - -pub mod scene; diff --git a/src/vulkan/pipelines/mod.rs b/src/old_app/pipelines/mod.rs similarity index 100% rename from src/vulkan/pipelines/mod.rs rename to src/old_app/pipelines/mod.rs diff --git a/src/vulkan/pipelines/triangle_pipeline.rs b/src/old_app/pipelines/triangle_pipeline.rs similarity index 100% rename from src/vulkan/pipelines/triangle_pipeline.rs rename to src/old_app/pipelines/triangle_pipeline.rs diff --git a/src/vulkan/scene.rs b/src/old_app/scene.rs similarity index 97% rename from src/vulkan/scene.rs rename to src/old_app/scene.rs index 22986ff..72e3f48 100644 --- a/src/vulkan/scene.rs +++ b/src/old_app/scene.rs @@ -1,4 +1,4 @@ -use crate::vulkan::pipelines::triangle_pipeline::shaders::vs; +use crate::old_app::pipelines::triangle_pipeline::shaders::vs; use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; @@ -10,7 +10,7 @@ use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use crate::core::render::vertex::Vertex2D; -use crate::vulkan::pipelines::triangle_pipeline::create_triangle_pipeline; +use crate::old_app::pipelines::triangle_pipeline::create_triangle_pipeline; use super::vulkan_context::VulkanContext; use super::window_render_context::WindowRenderContext; diff --git a/src/vulkan/vulkan_context.rs b/src/old_app/vulkan_context.rs similarity index 100% rename from src/vulkan/vulkan_context.rs rename to src/old_app/vulkan_context.rs diff --git a/src/vulkan/window_render_context.rs b/src/old_app/window_render_context.rs similarity index 100% rename from src/vulkan/window_render_context.rs rename to src/old_app/window_render_context.rs From e2616a0ef5af2ae8ff0d1e351017976f97c7138a Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 18:45:33 +0200 Subject: [PATCH 39/91] Add vulkan creation from resources --- src/core/mod.rs | 1 + src/core/vulkan/context.rs | 65 ++++++++++++++++ src/core/vulkan/mod.rs | 24 ++++++ src/core/vulkan/utils.rs | 137 ++++++++++++++++++++++++++++++++++ src/core/window/mod.rs | 39 +++++++--- src/core/window/raw_handle.rs | 18 +++++ src/game/mod.rs | 6 +- src/main.rs | 4 +- 8 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 src/core/vulkan/context.rs create mode 100644 src/core/vulkan/mod.rs create mode 100644 src/core/vulkan/utils.rs create mode 100644 src/core/window/raw_handle.rs diff --git a/src/core/mod.rs b/src/core/mod.rs index bd7ce34..abaeeae 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,5 @@ pub mod app; pub mod camera; pub mod render; +pub mod vulkan; pub mod window; diff --git a/src/core/vulkan/context.rs b/src/core/vulkan/context.rs new file mode 100644 index 0000000..b4e9763 --- /dev/null +++ b/src/core/vulkan/context.rs @@ -0,0 +1,65 @@ +use std::sync::Arc; + +use bevy_ecs::system::Resource; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, + swapchain::Surface, +}; + +use crate::core::{app::App, window::raw_handle::DisplayHandleWrapper}; + +use super::utils; + +#[derive(Resource)] +pub struct VulkanContext { + pub instance: Arc, + pub device: Arc, + pub graphics_queue: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +impl From<&App> for VulkanContext { + fn from(app: &App) -> Self { + let library = utils::load_library(); + + let world = app.world(); + + let display_handle: &DisplayHandleWrapper = + world.get_resource::().unwrap(); + + let enabled_extensions = Surface::required_extensions(&display_handle.0).unwrap(); + + let instance = utils::create_instance(library.clone(), enabled_extensions); + + let (device, mut queues) = utils::pick_graphics_device(&instance, &display_handle.0); + let graphics_queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + + Self { + instance: instance.clone(), + device, + graphics_queue, + memory_allocator, + command_buffer_allocator, + descriptor_set_allocator, + } + } +} diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs new file mode 100644 index 0000000..e80d56b --- /dev/null +++ b/src/core/vulkan/mod.rs @@ -0,0 +1,24 @@ +use context::VulkanContext; + +use super::app::App; + +mod context; +mod utils; + +#[derive(Debug, thiserror::Error)] +pub enum VulkanError { + #[error("Failed to create vulkan context")] + FailedToCreateVulkanContext, +} + +pub struct Vulkan; + +impl Vulkan { + pub fn new(app: &mut App) -> Result<(), VulkanError> { + let vulkan_context = VulkanContext::from(app as &App); + + app.world_mut().insert_resource(vulkan_context); + + Ok(()) + } +} diff --git a/src/core/vulkan/utils.rs b/src/core/vulkan/utils.rs new file mode 100644 index 0000000..046528f --- /dev/null +++ b/src/core/vulkan/utils.rs @@ -0,0 +1,137 @@ +use std::sync::Arc; + +use vulkano::{ + Version, VulkanLibrary, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, + QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, + }, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, +}; +use winit::raw_window_handle::HasDisplayHandle; + +pub(super) fn load_library() -> Arc { + let library = VulkanLibrary::new().unwrap(); + + log::debug!("Available layer:"); + for layer in library.layer_properties().unwrap() { + log::debug!( + "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", + layer.name(), + layer.description(), + layer.implementation_version(), + layer.vulkan_version() + ); + } + + library +} + +pub(super) fn create_instance( + library: Arc, + required_extensions: InstanceExtensions, +) -> Arc { + Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + ..Default::default() + }, + ) + .unwrap() +} + +pub(super) fn find_physical_device_queue_family_indexes( + physical_device: &Arc, + display_handle: &impl HasDisplayHandle, +) -> Option { + let mut graphic_queue_family_index = None; + + for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { + if queue_family_property + .queue_flags + .intersects(QueueFlags::GRAPHICS) + && physical_device + .presentation_support(i as u32, display_handle) + .unwrap() + { + graphic_queue_family_index = Some(i as u32); + } + } + + graphic_queue_family_index +} + +pub(super) fn pick_physical_device_and_queue_family_indexes( + instance: &Arc, + display_handle: &impl HasDisplayHandle, + device_extensions: &DeviceExtensions, +) -> Option<(Arc, u32)> { + instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| p.supported_extensions().contains(device_extensions)) + .filter_map(|p| { + find_physical_device_queue_family_indexes(&p, display_handle) + .and_then(|indexes| Some((p, indexes))) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) +} + +pub(super) fn pick_graphics_device( + instance: &Arc, + display_handle: &impl HasDisplayHandle, +) -> (Arc, impl ExactSizeIterator>) { + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + let (physical_device, graphics_family_index) = + pick_physical_device_and_queue_family_indexes(instance, display_handle, &device_extensions) + .unwrap(); + + log::debug!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + log::debug!("Using device extensions: {:#?}", device_extensions); + + Device::new( + physical_device, + DeviceCreateInfo { + queue_create_infos: vec![QueueCreateInfo { + queue_family_index: graphics_family_index, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + ..Default::default() + }, + ) + .unwrap() +} diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index e600120..4368310 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,23 +1,44 @@ use config::WindowConfig; +use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; use state::WindowState; use winit::event_loop::EventLoop; use super::app::{App, AppExit}; pub mod config; +pub mod raw_handle; pub mod state; -pub fn init(app: &mut App, window_config: WindowConfig) { - let world = app.world_mut(); - world.insert_resource(window_config); - - let mut event_loop_builder = EventLoop::with_user_event(); - let event_loop = event_loop_builder.build().unwrap(); - - app.set_runner(Box::new(move |app| runner(app, event_loop))); +#[derive(Debug, thiserror::Error)] +pub enum WindowError { + #[error("Failed to create event loop")] + FailedToCreateEventLoop, } -fn runner(app: App, event_loop: EventLoop<()>) -> AppExit { +pub struct Window; + +impl Window { + pub fn new(app: &mut App, window_config: WindowConfig) -> Result<(), WindowError> { + let world = app.world_mut(); + world.insert_resource(window_config); + + let mut event_loop_builder = EventLoop::with_user_event(); + let event_loop = event_loop_builder + .build() + .map_err(|_| WindowError::FailedToCreateEventLoop)?; + + world.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle())); + + app.set_runner(Box::new(move |app| runner(app, event_loop))); + + Ok(()) + } +} + +fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { + app.world_mut() + .insert_resource(EventLoopProxyWrapper::new(event_loop.create_proxy())); + let mut window_state = WindowState::new(app); match event_loop.run_app(&mut window_state) { diff --git a/src/core/window/raw_handle.rs b/src/core/window/raw_handle.rs new file mode 100644 index 0000000..16b2178 --- /dev/null +++ b/src/core/window/raw_handle.rs @@ -0,0 +1,18 @@ +use bevy_ecs::system::Resource; +use winit::event_loop::EventLoopProxy; + +#[derive(Resource)] +pub struct EventLoopProxyWrapper(EventLoopProxy); + +impl EventLoopProxyWrapper { + pub fn new(event_loop: EventLoopProxy) -> Self { + Self(event_loop) + } + + pub fn proxy(&self) -> &EventLoopProxy { + &self.0 + } +} + +#[derive(Resource)] +pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); diff --git a/src/game/mod.rs b/src/game/mod.rs index ffee10f..0b7787d 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,6 +1,7 @@ use crate::core::{ app::App, - window::{self, config::WindowConfig}, + vulkan::Vulkan, + window::{Window, config::WindowConfig}, }; pub fn init(app: &mut App) { @@ -10,5 +11,6 @@ pub fn init(app: &mut App) { height: 600, }; - window::init(app, window_config); + Window::new(app, window_config).unwrap(); + Vulkan::new(app).unwrap(); } diff --git a/src/main.rs b/src/main.rs index c981713..8297797 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,8 @@ pub mod old_app; fn main() -> Result<(), impl Error> { env_logger::init(); - // run_new_app() - run_old_app() + run_new_app() + // run_old_app() } fn run_new_app() -> Result<(), impl Error> { From a04c7694389af64c1e716f29949aa9b8155b786b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 19:23:05 +0200 Subject: [PATCH 40/91] Begin add Window Render Context --- src/core/vulkan/mod.rs | 10 +- .../vulkan/{context.rs => vulkan_context.rs} | 26 +++- src/core/vulkan/window_render_context.rs | 117 ++++++++++++++++++ src/core/window/raw_handle.rs | 7 +- src/core/window/state.rs | 17 +-- 5 files changed, 163 insertions(+), 14 deletions(-) rename src/core/vulkan/{context.rs => vulkan_context.rs} (69%) create mode 100644 src/core/vulkan/window_render_context.rs diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs index e80d56b..fe22fb3 100644 --- a/src/core/vulkan/mod.rs +++ b/src/core/vulkan/mod.rs @@ -1,9 +1,11 @@ -use context::VulkanContext; +use vulkan_context::VulkanContext; +use window_render_context::WindowRenderContext; use super::app::App; -mod context; mod utils; +mod vulkan_context; +mod window_render_context; #[derive(Debug, thiserror::Error)] pub enum VulkanError { @@ -16,9 +18,11 @@ pub struct Vulkan; impl Vulkan { pub fn new(app: &mut App) -> Result<(), VulkanError> { let vulkan_context = VulkanContext::from(app as &App); - app.world_mut().insert_resource(vulkan_context); + let window_render_context = WindowRenderContext::from(app as &App); + app.world_mut().insert_resource(window_render_context); + Ok(()) } } diff --git a/src/core/vulkan/context.rs b/src/core/vulkan/vulkan_context.rs similarity index 69% rename from src/core/vulkan/context.rs rename to src/core/vulkan/vulkan_context.rs index b4e9763..f59d9a3 100644 --- a/src/core/vulkan/context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -1,14 +1,18 @@ -use std::sync::Arc; +use std::{any::Any, sync::Arc}; use bevy_ecs::system::Resource; use vulkano::{ - command_buffer::allocator::StandardCommandBufferAllocator, + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + allocator::StandardCommandBufferAllocator, + }, descriptor_set::allocator::StandardDescriptorSetAllocator, device::{Device, Queue}, instance::Instance, memory::allocator::StandardMemoryAllocator, swapchain::Surface, }; +use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use crate::core::{app::App, window::raw_handle::DisplayHandleWrapper}; @@ -25,6 +29,24 @@ pub struct VulkanContext { pub descriptor_set_allocator: Arc, } +impl VulkanContext { + pub fn create_surface( + &self, + window: Arc, + ) -> Arc { + Surface::from_window(self.instance.clone(), window).unwrap() + } + + pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { + AutoCommandBufferBuilder::primary( + self.command_buffer_allocator.clone(), + self.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap() + } +} + impl From<&App> for VulkanContext { fn from(app: &App) -> Self { let library = utils::load_library(); diff --git a/src/core/vulkan/window_render_context.rs b/src/core/vulkan/window_render_context.rs new file mode 100644 index 0000000..1c653a7 --- /dev/null +++ b/src/core/vulkan/window_render_context.rs @@ -0,0 +1,117 @@ +use bevy_ecs::system::Resource; +use std::sync::Arc; +use vulkano::image::view::ImageView; +use vulkano::image::{Image, ImageUsage}; +use vulkano::pipeline::graphics::viewport::Viewport; +use vulkano::swapchain::{Swapchain, SwapchainCreateInfo}; +use vulkano::sync::{self, GpuFuture}; +use vulkano::{Validated, VulkanError}; +use winit::window::Window; + +use crate::core::app::App; +use crate::core::window::raw_handle::WindowWrapper; + +use super::vulkan_context::VulkanContext; + +#[derive(Resource)] +pub struct WindowRenderContext { + pub window: Arc, + pub swapchain: Arc, + pub attachment_image_views: Vec>, + pub viewport: Viewport, + pub recreate_swapchain: bool, + pub previous_frame_end: Option>, +} + +impl From<&App> for WindowRenderContext { + fn from(app: &App) -> Self { + let world = app.world(); + let vulkan_context = world.get_resource::().unwrap(); + let window_handle = world.get_resource::().unwrap(); + let window_size = window_handle.0.inner_size(); + + let surface = vulkan_context.create_surface(window_handle.0.clone()); + + let (swapchain, images) = { + let surface_capabilities = vulkan_context + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + let (image_format, _) = vulkan_context + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + vulkan_context.device.clone(), + surface, + SwapchainCreateInfo { + // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; + + let attachment_image_views = window_size_dependent_setup(&images); + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let recreate_swapchain = false; + let previous_frame_end = Some(sync::now(vulkan_context.device.clone()).boxed_send_sync()); + + Self { + window: window_handle.0.clone(), + swapchain, + attachment_image_views, + viewport, + recreate_swapchain, + previous_frame_end, + } + } +} + +impl WindowRenderContext { + pub fn update_swapchain(&mut self) -> Result<(), Validated> { + if !self.recreate_swapchain { + return Ok(()); + } + + let window_size = self.window.inner_size(); + let (new_swapchain, new_images) = self.swapchain.recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..self.swapchain.create_info() + })?; + + self.swapchain = new_swapchain; + self.attachment_image_views = window_size_dependent_setup(&new_images); + self.viewport.extent = window_size.into(); + self.recreate_swapchain = false; + + Ok(()) + } +} + +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +} diff --git a/src/core/window/raw_handle.rs b/src/core/window/raw_handle.rs index 16b2178..ad9c8dd 100644 --- a/src/core/window/raw_handle.rs +++ b/src/core/window/raw_handle.rs @@ -1,5 +1,7 @@ +use std::sync::Arc; + use bevy_ecs::system::Resource; -use winit::event_loop::EventLoopProxy; +use winit::{event_loop::EventLoopProxy, window::Window}; #[derive(Resource)] pub struct EventLoopProxyWrapper(EventLoopProxy); @@ -16,3 +18,6 @@ impl EventLoopProxyWrapper { #[derive(Resource)] pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); + +#[derive(Resource)] +pub struct WindowWrapper(pub Arc); diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 94bfcc7..0b0966e 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,23 +1,22 @@ +use std::sync::Arc; + use bevy_ecs::world::World; use winit::{ - application::ApplicationHandler, - event::WindowEvent, - event_loop::ActiveEventLoop, - window::{Window, WindowId}, + application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, + window::WindowId, }; use crate::core::app::App; -use super::config::WindowConfig; +use super::{config::WindowConfig, raw_handle::WindowWrapper}; pub struct WindowState { app: App, - window: Option, } impl WindowState { pub fn new(app: App) -> Self { - Self { app, window: None } + Self { app } } fn world(&self) -> &World { @@ -30,7 +29,9 @@ impl ApplicationHandler for WindowState { let window_config = self.world().get_resource::().unwrap(); let window = event_loop.create_window(window_config.into()).unwrap(); - self.window = Some(window); + self.app + .world_mut() + .insert_resource(WindowWrapper(Arc::new(window))); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { From 8b0c59f7c0682a0391a9527033e56174640dc57f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 20:05:17 +0200 Subject: [PATCH 41/91] Remove useless schedule (for now) --- src/core/app/app.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/app/app.rs b/src/core/app/app.rs index 21ebd28..3821bfc 100644 --- a/src/core/app/app.rs +++ b/src/core/app/app.rs @@ -1,6 +1,6 @@ use std::error::Error; -use bevy_ecs::{schedule::Schedules, world::World}; +use bevy_ecs::world::World; pub enum AppExit { Success, @@ -24,11 +24,8 @@ pub struct App { impl Default for App { fn default() -> Self { - let mut world = World::new(); - world.init_resource::(); - Self { - world, + world: World::new(), runner: None, } } From a295093c97252f86c86aa624f225ab98eb1c9443 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 24 Apr 2025 13:05:38 +0200 Subject: [PATCH 42/91] Use bevy_app instead --- Cargo.lock | 63 ++++++++++++++++++++++++ Cargo.toml | 5 +- src/core/app/app.rs | 56 --------------------- src/core/app/mod.rs | 2 - src/core/mod.rs | 1 - src/core/vulkan/mod.rs | 2 +- src/core/vulkan/vulkan_context.rs | 3 +- src/core/vulkan/window_render_context.rs | 2 +- src/core/window/mod.rs | 8 +-- src/core/window/state.rs | 3 +- src/game/mod.rs | 3 +- src/main.rs | 29 +++++++---- 12 files changed, 98 insertions(+), 79 deletions(-) delete mode 100644 src/core/app/app.rs delete mode 100644 src/core/app/mod.rs diff --git a/Cargo.lock b/Cargo.lock index da7454f..9e91469 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,6 +199,36 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bevy_app" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ac033a388b8699d241499a43783a09e6a3bab2430f1297c6bd4974095efb3f" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "console_error_panic_hook", + "ctrlc", + "derive_more", + "downcast-rs", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "bevy_derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d94761ce947b0a2402fd949fe1e7a5b1535293130ba4cd9893be6295d4680a" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn", +] + [[package]] name = "bevy_ecs" version = "0.15.3" @@ -462,6 +492,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const-random" version = "0.1.18" @@ -543,6 +583,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +[[package]] +name = "ctrlc" +version = "3.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" +dependencies = [ + "nix", + "windows-sys 0.59.0", +] + [[package]] name = "cursor-icon" version = "1.1.0" @@ -993,6 +1043,18 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -1533,6 +1595,7 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", + "bevy_app", "bevy_ecs", "env_logger", "glam", diff --git a/Cargo.toml b/Cargo.toml index a84f681..4542e45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,9 @@ vulkano-shaders = "0.35" glam = { version = "0.30" } # ECS -bevy_ecs = "0.15.3" +bevy_ecs = "0.15" +bevy_app = "0.15" # Log and tracing log = "0.4" -env_logger = "0.11.5" +env_logger = "0.11" diff --git a/src/core/app/app.rs b/src/core/app/app.rs deleted file mode 100644 index 3821bfc..0000000 --- a/src/core/app/app.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::error::Error; - -use bevy_ecs::world::World; - -pub enum AppExit { - Success, - Error(Box), -} - -pub type RunnerFn = Box AppExit>; - -#[derive(Debug, thiserror::Error)] -pub enum AppError { - #[error("Runner is not set")] - RunnerNotSet, - #[error("Runner returned an error : {0}")] - RunnerError(Box), -} - -pub struct App { - world: World, - runner: Option, -} - -impl Default for App { - fn default() -> Self { - Self { - world: World::new(), - runner: None, - } - } -} - -impl App { - pub fn world_mut(&mut self) -> &mut World { - &mut self.world - } - - pub fn world(&self) -> &World { - &self.world - } - - pub fn run(mut self) -> Result<(), AppError> { - match self.runner.take() { - Some(runner) => match runner(self) { - AppExit::Success => Ok(()), - AppExit::Error(e) => Err(AppError::RunnerError(e)), - }, - None => Err(AppError::RunnerNotSet), - } - } - - pub fn set_runner(&mut self, runner: RunnerFn) { - self.runner = Some(runner); - } -} diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs deleted file mode 100644 index c6c8a20..0000000 --- a/src/core/app/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod app; -pub use app::*; diff --git a/src/core/mod.rs b/src/core/mod.rs index abaeeae..db2061f 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,3 @@ -pub mod app; pub mod camera; pub mod render; pub mod vulkan; diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs index fe22fb3..29fe224 100644 --- a/src/core/vulkan/mod.rs +++ b/src/core/vulkan/mod.rs @@ -1,7 +1,7 @@ use vulkan_context::VulkanContext; use window_render_context::WindowRenderContext; -use super::app::App; +use bevy_app::App; mod utils; mod vulkan_context; diff --git a/src/core/vulkan/vulkan_context.rs b/src/core/vulkan/vulkan_context.rs index f59d9a3..b8be6ca 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -1,5 +1,6 @@ use std::{any::Any, sync::Arc}; +use bevy_app::App; use bevy_ecs::system::Resource; use vulkano::{ command_buffer::{ @@ -14,7 +15,7 @@ use vulkano::{ }; use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use crate::core::{app::App, window::raw_handle::DisplayHandleWrapper}; +use crate::core::window::raw_handle::DisplayHandleWrapper; use super::utils; diff --git a/src/core/vulkan/window_render_context.rs b/src/core/vulkan/window_render_context.rs index 1c653a7..5cb2618 100644 --- a/src/core/vulkan/window_render_context.rs +++ b/src/core/vulkan/window_render_context.rs @@ -1,3 +1,4 @@ +use bevy_app::App; use bevy_ecs::system::Resource; use std::sync::Arc; use vulkano::image::view::ImageView; @@ -8,7 +9,6 @@ use vulkano::sync::{self, GpuFuture}; use vulkano::{Validated, VulkanError}; use winit::window::Window; -use crate::core::app::App; use crate::core::window::raw_handle::WindowWrapper; use super::vulkan_context::VulkanContext; diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index 4368310..d1623b7 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,10 +1,9 @@ +use bevy_app::{App, AppExit}; use config::WindowConfig; use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; use state::WindowState; use winit::event_loop::EventLoop; -use super::app::{App, AppExit}; - pub mod config; pub mod raw_handle; pub mod state; @@ -43,6 +42,9 @@ fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { match event_loop.run_app(&mut window_state) { Ok(_) => AppExit::Success, - Err(e) => AppExit::Error(Box::new(e)), + Err(e) => { + log::error!("Error running window state: {e}"); + AppExit::error() + } } } diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 0b0966e..8ffd44b 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,13 +1,12 @@ use std::sync::Arc; +use bevy_app::App; use bevy_ecs::world::World; use winit::{ application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId, }; -use crate::core::app::App; - use super::{config::WindowConfig, raw_handle::WindowWrapper}; pub struct WindowState { diff --git a/src/game/mod.rs b/src/game/mod.rs index 0b7787d..6eb9cab 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,5 +1,6 @@ +use bevy_app::App; + use crate::core::{ - app::App, vulkan::Vulkan, window::{Window, config::WindowConfig}, }; diff --git a/src/main.rs b/src/main.rs index 8297797..34fad4b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,40 @@ -use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; +use bevy_app::{App, AppExit}; + pub mod core; pub mod game; pub mod old_app; -fn main() -> Result<(), impl Error> { +fn main() { env_logger::init(); - run_new_app() - // run_old_app() + run_new_app(); + // run_old_app(); } -fn run_new_app() -> Result<(), impl Error> { - let mut app = core::app::App::default(); +fn run_new_app() { + let mut app = App::default(); game::init(&mut app); - app.run() + match app.run() { + AppExit::Success => {} + AppExit::Error(e) => { + log::error!("Error running new app: {e}"); + } + } } -fn run_old_app() -> Result<(), impl Error> { +fn run_old_app() { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let vulkan_context = old_app::vulkan_context::VulkanContext::from(&event_loop); let mut app = old_app::app::App::from(vulkan_context); - event_loop.run_app(&mut app) + match event_loop.run_app(&mut app) { + Ok(_) => {} + Err(e) => { + log::error!("Error running old app: {e}"); + } + } } From 285b1942803429e675ece2eb8144bf2b7e858fac Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 27 Apr 2025 17:16:50 +0200 Subject: [PATCH 43/91] Surface: Add required extensions --- src/core/vulkan/vulkan_context.rs | 1 + src/old_app/vulkan_context.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/core/vulkan/vulkan_context.rs b/src/core/vulkan/vulkan_context.rs index b8be6ca..74df916 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -58,6 +58,7 @@ impl From<&App> for VulkanContext { world.get_resource::().unwrap(); let enabled_extensions = Surface::required_extensions(&display_handle.0).unwrap(); + log::debug!("Surface required extensions: {enabled_extensions:?}"); let instance = utils::create_instance(library.clone(), enabled_extensions); diff --git a/src/old_app/vulkan_context.rs b/src/old_app/vulkan_context.rs index bad47de..332dbcf 100644 --- a/src/old_app/vulkan_context.rs +++ b/src/old_app/vulkan_context.rs @@ -36,6 +36,7 @@ impl From<&EventLoop<()>> for VulkanContext { let library = load_library(); let enabled_extensions = Surface::required_extensions(event_loop).unwrap(); + log::debug!("Surface required extensions: {enabled_extensions:?}"); let instance = create_instance(library.clone(), enabled_extensions); From dda368e8023506a090e62e59ee6378bf3e6880da Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 16 May 2025 13:47:24 +0200 Subject: [PATCH 44/91] Update all dependencies --- Cargo.lock | 689 ++++++++++++++--------- Cargo.toml | 4 +- flake.lock | 12 +- flake.nix | 10 +- rust-toolchain.toml | 2 +- src/core/vulkan/vulkan_context.rs | 2 +- src/core/vulkan/window_render_context.rs | 2 +- src/core/window/config.rs | 2 +- src/core/window/raw_handle.rs | 2 +- 9 files changed, 426 insertions(+), 299 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e91469..e9d2554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,12 +20,11 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "const-random", "getrandom", "once_cell", "version_check", @@ -41,12 +40,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android-activity" version = "0.6.0" @@ -54,7 +47,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.0", + "bitflags 2.9.1", "cc", "cesu8", "jni", @@ -126,9 +119,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arrayref" @@ -170,14 +163,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -186,12 +180,18 @@ name = "async-task" version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +dependencies = [ + "portable-atomic", +] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +dependencies = [ + "portable-atomic", +] [[package]] name = "autocfg" @@ -201,28 +201,29 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bevy_app" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ac033a388b8699d241499a43783a09e6a3bab2430f1297c6bd4974095efb3f" +checksum = "a2b6267ac23a9947d5b2725ff047a1e1add70076d85fa9fb73d044ab9bea1f3c" dependencies = [ "bevy_derive", "bevy_ecs", + "bevy_platform", "bevy_reflect", "bevy_tasks", "bevy_utils", - "console_error_panic_hook", + "cfg-if", "ctrlc", - "derive_more", - "downcast-rs", - "wasm-bindgen", - "web-sys", + "downcast-rs 2.0.1", + "log", + "thiserror 2.0.12", + "variadics_please", ] [[package]] name = "bevy_derive" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d94761ce947b0a2402fd949fe1e7a5b1535293130ba4cd9893be6295d4680a" +checksum = "f626531b9c05c25a758ede228727bd11c2c2c8498ecbed9925044386d525a2a3" dependencies = [ "bevy_macro_utils", "quote", @@ -231,30 +232,37 @@ dependencies = [ [[package]] name = "bevy_ecs" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1597106cc01e62e6217ccb662e0748b2ce330893f27c7dc17bac33e0bb99bca9" +checksum = "d9e807b5d9aab3bb8dfe47e7a44c9ff088bad2ceefe299b80ac77609a87fe9d4" dependencies = [ + "arrayvec", "bevy_ecs_macros", + "bevy_platform", "bevy_ptr", "bevy_reflect", "bevy_tasks", "bevy_utils", - "bitflags 2.9.0", + "bitflags 2.9.1", + "bumpalo", "concurrent-queue", "derive_more", "disqualified", - "fixedbitset 0.5.7", + "fixedbitset", + "indexmap", + "log", "nonmax", - "petgraph", + "serde", "smallvec", + "thiserror 2.0.12", + "variadics_please", ] [[package]] name = "bevy_ecs_macros" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f453adf07712b39826bc5845e5b0887ce03204ee8359bbe6b40a9afda60564a1" +checksum = "467d7bb98aeb8dd30f36e6a773000c12a891d4f1bee2adc3841ec89cc8eaf54e" dependencies = [ "bevy_macro_utils", "proc-macro2", @@ -264,10 +272,11 @@ dependencies = [ [[package]] name = "bevy_macro_utils" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb6ded1ddc124ea214f6a2140e47a78d1fe79b0638dad39419cdeef2e1133f1" +checksum = "7a2473db70d8785b5c75d6dd951a2e51e9be2c2311122db9692c79c9d887517b" dependencies = [ + "parking_lot", "proc-macro2", "quote", "syn", @@ -275,34 +284,58 @@ dependencies = [ ] [[package]] -name = "bevy_ptr" -version = "0.15.3" +name = "bevy_platform" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89fe0b0b919146939481a3a7c38864face2c6d0fd2c73ab3d430dc693ecd9b11" +checksum = "704db2c11b7bc31093df4fbbdd3769f9606a6a5287149f4b51f2680f25834ebc" +dependencies = [ + "cfg-if", + "critical-section", + "foldhash", + "hashbrown", + "portable-atomic", + "portable-atomic-util", + "serde", + "spin", +] + +[[package]] +name = "bevy_ptr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f1275dfb4cfef4ffc90c3fa75408964864facf833acc932413d52aa5364ba4" [[package]] name = "bevy_reflect" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ddbca0a39e88eff2c301dc794ee9d73a53f4b08d47b2c9b5a6aac182fae6217" +checksum = "607ebacc31029cf2f39ac330eabf1d4bc411b159528ec08dbe6b0593eaccfd41" dependencies = [ "assert_type_match", + "bevy_platform", "bevy_ptr", "bevy_reflect_derive", "bevy_utils", "derive_more", "disqualified", - "downcast-rs", + "downcast-rs 2.0.1", "erased-serde", + "foldhash", + "glam 0.29.3", "serde", "smallvec", + "smol_str", + "thiserror 2.0.12", + "uuid", + "variadics_please", + "wgpu-types", ] [[package]] name = "bevy_reflect_derive" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d62affb769db17d34ad0b75ff27eca94867e2acc8ea350c5eca97d102bd98709" +checksum = "cf35e45e4eb239018369f63f2adc2107a54c329f9276d020e01eee1625b0238b" dependencies = [ "bevy_macro_utils", "proc-macro2", @@ -313,41 +346,29 @@ dependencies = [ [[package]] name = "bevy_tasks" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028630ddc355563bd567df1076db3515858aa26715ddf7467d2086f9b40e5ab1" +checksum = "444c450b65e108855f42ecb6db0c041a56ea7d7f10cc6222f0ca95e9536a7d19" dependencies = [ "async-executor", - "futures-channel", + "async-task", + "atomic-waker", + "bevy_platform", + "cfg-if", + "crossbeam-queue", + "derive_more", "futures-lite", - "pin-project", - "wasm-bindgen-futures", + "heapless", ] [[package]] name = "bevy_utils" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c2174d43a0de99f863c98a472370047a2bfa7d1e5cec8d9d647fb500905d9d" +checksum = "ac2da3b3c1f94dadefcbe837aaa4aa119fcea37f7bdc5307eb05b4ede1921e24" dependencies = [ - "ahash", - "bevy_utils_proc_macros", - "getrandom", - "hashbrown 0.14.5", + "bevy_platform", "thread_local", - "tracing", - "web-time", -] - -[[package]] -name = "bevy_utils_proc_macros" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94847541f6dd2e28f54a9c2b0e857da5f2631e2201ebc25ce68781cdcb721391" -dependencies = [ - "proc-macro2", - "quote", - "syn", ] [[package]] @@ -358,9 +379,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] [[package]] name = "block2" @@ -379,24 +403,30 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.10.1" @@ -409,7 +439,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "log", "polling", "rustix", @@ -431,9 +461,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "jobserver", "libc", @@ -490,36 +520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", + "portable-atomic", ] [[package]] @@ -562,6 +563,12 @@ dependencies = [ "libc", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -585,9 +592,9 @@ checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "ctrlc" -version = "3.4.6" +version = "3.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" dependencies = [ "nix", "windows-sys 0.59.0", @@ -626,6 +633,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", +] + [[package]] name = "disqualified" version = "1.0.0" @@ -648,10 +665,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] -name = "dpi" -version = "0.1.1" +name = "downcast-rs" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "env_filter" @@ -665,9 +688,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -694,9 +717,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -708,12 +731,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "fixedbitset" version = "0.5.7" @@ -722,9 +739,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" @@ -753,15 +770,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" version = "0.3.31" @@ -799,28 +807,36 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", - "js-sys", "libc", + "r-efi", "wasi", - "wasm-bindgen", ] [[package]] name = "glam" -version = "0.30.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17fcdf9683c406c2fc4d124afd29c0d595e22210d633cbdb8695ba9935ab1dc6" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" +dependencies = [ + "serde", +] + +[[package]] +name = "glam" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b46b9ca4690308844c644e7c634d68792467260e051c8543e0c7871662b3ba7" [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "bytemuck", "cfg-if", @@ -828,21 +844,34 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.14.5" +name = "hash32" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ - "ahash", - "allocator-api2", - "serde", + "byteorder", ] [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "equivalent", + "serde", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "portable-atomic", + "stable_deref_trait", +] [[package]] name = "heck" @@ -858,12 +887,12 @@ checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown", ] [[package]] @@ -880,9 +909,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.4" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -893,9 +922,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.4" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -926,10 +955,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom", "libc", ] @@ -945,18 +975,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] @@ -965,9 +995,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", - "redox_syscall 0.5.10", + "redox_syscall 0.5.12", ] [[package]] @@ -988,9 +1018,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" @@ -1019,7 +1049,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "jni-sys", "log", "ndk-sys", @@ -1045,11 +1075,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", @@ -1110,9 +1140,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" dependencies = [ "objc2-encode", ] @@ -1123,7 +1153,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "libc", "objc2 0.5.2", @@ -1139,7 +1169,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-core-location", @@ -1163,7 +1193,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1171,12 +1201,13 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.1", + "dispatch2", + "objc2 0.6.1", ] [[package]] @@ -1215,7 +1246,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "dispatch", "libc", @@ -1224,12 +1255,12 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.1", + "objc2 0.6.1", "objc2-core-foundation", ] @@ -1251,7 +1282,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1259,13 +1290,13 @@ dependencies = [ [[package]] name = "objc2-metal" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c41bc8b0e50ea7a5304a56f25e0066f526e99641b46fd7b9ad4421dd35bff6" +checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-foundation 0.3.1", ] [[package]] @@ -1274,7 +1305,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1283,15 +1314,15 @@ dependencies = [ [[package]] name = "objc2-quartz-core" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.1", + "objc2 0.6.1", "objc2-core-foundation", - "objc2-foundation 0.3.0", - "objc2-metal 0.3.0", + "objc2-foundation 0.3.1", + "objc2-metal 0.3.1", ] [[package]] @@ -1310,7 +1341,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-cloud-kit", @@ -1342,7 +1373,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-core-location", @@ -1351,9 +1382,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "orbclient" @@ -1397,7 +1428,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.10", + "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] @@ -1408,16 +1439,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset 0.4.2", - "indexmap", -] - [[package]] name = "pin-project" version = "1.1.10" @@ -1491,18 +1512,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.37.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -1516,6 +1537,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -1528,10 +1555,10 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.1", "objc2-core-foundation", - "objc2-foundation 0.3.0", - "objc2-quartz-core 0.3.0", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", ] [[package]] @@ -1545,11 +1572,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -1598,7 +1625,7 @@ dependencies = [ "bevy_app", "bevy_ecs", "env_logger", - "glam", + "glam 0.30.3", "log", "thiserror 2.0.12", "vulkano", @@ -1612,7 +1639,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -1741,9 +1768,9 @@ checksum = "9db491c0d4152a069911a0fbdaca959691bf0b9d7110d98a7ed1c8e59b79ab30" [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smithay-client-toolkit" @@ -1751,7 +1778,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "calloop", "calloop-wayland-source", "cursor-icon", @@ -1779,6 +1806,21 @@ dependencies = [ "serde", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "portable-atomic", +] + +[[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" @@ -1787,9 +1829,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -1846,15 +1888,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tiny-skia" version = "0.11.4" @@ -1882,15 +1915,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "toml_datetime", @@ -1912,9 +1945,6 @@ name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", -] [[package]] name = "ttf-parser" @@ -1954,11 +1984,25 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.12.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ "getrandom", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "variadics_please" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2047,9 +2091,12 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] [[package]] name = "wasm-bindgen" @@ -2124,12 +2171,12 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", - "downcast-rs", + "downcast-rs 1.2.1", "rustix", "scoped-tls", "smallvec", @@ -2138,11 +2185,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "rustix", "wayland-backend", "wayland-scanner", @@ -2154,16 +2201,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" dependencies = [ "rustix", "wayland-client", @@ -2172,11 +2219,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.6" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -2184,11 +2231,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -2197,11 +2244,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -2251,6 +2298,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wgpu-types" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +dependencies = [ + "bitflags 2.9.1", + "js-sys", + "log", + "serde", + "web-sys", +] + [[package]] name = "winapi-util" version = "0.1.9" @@ -2326,13 +2386,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -2351,6 +2427,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -2369,6 +2451,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -2387,12 +2475,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -2411,6 +2511,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -2429,6 +2535,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -2447,6 +2559,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2466,15 +2584,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winit" -version = "0.30.9" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winit" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d05bd8908e14618c9609471db04007e644fd9cce6529756046cfc577f9155e" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "bytemuck", "calloop", @@ -2519,13 +2643,22 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -2570,7 +2703,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "dlib", "log", "once_cell", @@ -2585,9 +2718,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "xmlparser" @@ -2597,18 +2730,18 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4542e45..d37056f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,8 @@ vulkano-shaders = "0.35" glam = { version = "0.30" } # ECS -bevy_ecs = "0.15" -bevy_app = "0.15" +bevy_ecs = "0.16" +bevy_app = "0.16" # Log and tracing log = "0.4" diff --git a/flake.lock b/flake.lock index 2853bb7..4f4cf7e 100644 --- a/flake.lock +++ b/flake.lock @@ -44,11 +44,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1742546557, - "narHash": "sha256-QyhimDBaDBtMfRc7kyL28vo+HTwXRPq3hz+BgSJDotw=", + "lastModified": 1747312588, + "narHash": "sha256-MmJvj6mlWzeRwKGLcwmZpKaOPZ5nJb/6al5CXqJsgjo=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bfa9810ff7104a17555ab68ebdeafb6705f129b1", + "rev": "b1bebd0fe266bbd1820019612ead889e96a8fa2d", "type": "github" }, "original": { @@ -73,11 +73,11 @@ ] }, "locked": { - "lastModified": 1742524367, - "narHash": "sha256-KzTwk/5ETJavJZYV1DEWdCx05M4duFCxCpRbQSKWpng=", + "lastModified": 1747363019, + "narHash": "sha256-N4dwkRBmpOosa4gfFkFf/LTD8oOcNkAyvZ07JvRDEf0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "70bf752d176b2ce07417e346d85486acea9040ef", + "rev": "0e624f2b1972a34be1a9b35290ed18ea4b419b6f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 4f7cd2e..ec86293 100644 --- a/flake.nix +++ b/flake.nix @@ -31,12 +31,6 @@ cargo = rust; }); - renderdoc = pkgs.renderdoc.overrideAttrs (oldAttrs: { - cmakeFlags = oldAttrs.cmakeFlags ++ [ - (pkgs.lib.cmakeBool "ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND" true) - ]; - }); - buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ stdenv.cc.cc.lib @@ -62,7 +56,7 @@ mkCustomShell = { packages ? [ ] }: pkgs.mkShell { nativeBuildInputs = [ - renderdoc + pkgs.renderdoc (rust.override { extensions = [ "rust-src" "rust-analyzer" ]; }) ] ++ nativeBuildInputs; @@ -70,7 +64,7 @@ ++ packages; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; - VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${renderdoc}/share/vulkan/implicit_layer.d"; + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; RUST_LOG = "info,rust_vulkan_test=trace"; }; in diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 00822fd..b8889a3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.85.1" +channel = "1.87.0" diff --git a/src/core/vulkan/vulkan_context.rs b/src/core/vulkan/vulkan_context.rs index 74df916..1583df8 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -1,7 +1,7 @@ use std::{any::Any, sync::Arc}; use bevy_app::App; -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, diff --git a/src/core/vulkan/window_render_context.rs b/src/core/vulkan/window_render_context.rs index 5cb2618..146be52 100644 --- a/src/core/vulkan/window_render_context.rs +++ b/src/core/vulkan/window_render_context.rs @@ -1,5 +1,5 @@ use bevy_app::App; -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use std::sync::Arc; use vulkano::image::view::ImageView; use vulkano::image::{Image, ImageUsage}; diff --git a/src/core/window/config.rs b/src/core/window/config.rs index 8fbef8c..487140c 100644 --- a/src/core/window/config.rs +++ b/src/core/window/config.rs @@ -1,4 +1,4 @@ -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use winit::{dpi::PhysicalSize, window::WindowAttributes}; #[derive(Resource, Clone)] diff --git a/src/core/window/raw_handle.rs b/src/core/window/raw_handle.rs index ad9c8dd..c896b56 100644 --- a/src/core/window/raw_handle.rs +++ b/src/core/window/raw_handle.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use winit::{event_loop::EventLoopProxy, window::Window}; #[derive(Resource)] From 6639f0bb1ef04030af41a6ac990bf62445ea6e92 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 16 May 2025 14:22:18 +0200 Subject: [PATCH 45/91] Init plugins + first system --- src/core/window/mod.rs | 7 ++++++- src/core/window/state.rs | 28 +++++++++++++++++++++++++--- src/game/mod.rs | 5 ++++- src/game/test_plugin.rs | 13 +++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 src/game/test_plugin.rs diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index d1623b7..4a19fee 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,4 +1,4 @@ -use bevy_app::{App, AppExit}; +use bevy_app::{App, AppExit, PluginsState}; use config::WindowConfig; use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; use state::WindowState; @@ -35,6 +35,11 @@ impl Window { } fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { + if app.plugins_state() == PluginsState::Ready { + app.finish(); + app.cleanup(); + } + app.world_mut() .insert_resource(EventLoopProxyWrapper::new(event_loop.create_proxy())); diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 8ffd44b..58d78d2 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,13 +1,16 @@ use std::sync::Arc; -use bevy_app::App; -use bevy_ecs::world::World; +use bevy_app::{App, PluginsState}; +use bevy_ecs::{event, world::World}; use winit::{ application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId, }; -use super::{config::WindowConfig, raw_handle::WindowWrapper}; +use super::{ + config::WindowConfig, + raw_handle::{DisplayHandleWrapper, WindowWrapper}, +}; pub struct WindowState { app: App, @@ -33,13 +36,32 @@ impl ApplicationHandler for WindowState { .insert_resource(WindowWrapper(Arc::new(window))); } + fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) { + if self.app.plugins_state() == PluginsState::Ready { + self.app.finish(); + self.app.cleanup(); + } + } + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { match event { WindowEvent::CloseRequested => { log::debug!("The close button was pressed; stopping"); event_loop.exit(); } + WindowEvent::RedrawRequested => { + log::debug!("The window was requested to be redrawn"); + if self.app.plugins_state() == PluginsState::Cleaned { + self.app.update(); + } + } _ => {} } } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let window_wrapper = self.app.world().get_resource::().unwrap(); + + window_wrapper.0.request_redraw(); + } } diff --git a/src/game/mod.rs b/src/game/mod.rs index 6eb9cab..903a179 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -5,6 +5,8 @@ use crate::core::{ window::{Window, config::WindowConfig}, }; +pub mod test_plugin; + pub fn init(app: &mut App) { let window_config = WindowConfig { title: "Rust ASH Test".to_string(), @@ -12,6 +14,7 @@ pub fn init(app: &mut App) { height: 600, }; + app.add_plugins(test_plugin::TestPlugin); Window::new(app, window_config).unwrap(); - Vulkan::new(app).unwrap(); + // Vulkan::new(app).unwrap(); } diff --git a/src/game/test_plugin.rs b/src/game/test_plugin.rs new file mode 100644 index 0000000..e82e61b --- /dev/null +++ b/src/game/test_plugin.rs @@ -0,0 +1,13 @@ +use bevy_app::{App, Last, Plugin, Startup}; + +pub struct TestPlugin; + +impl Plugin for TestPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Last, setup_system); + } +} + +fn setup_system() { + log::info!("Hello, world!"); +} From 99be029ff879bf16f5782c551b83c574299792fe Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 12:41:25 +0200 Subject: [PATCH 46/91] First vulkan init working --- src/core/vulkan/mod.rs | 18 ++++++++++++------ src/core/window/mod.rs | 17 +++++++++-------- src/core/window/state.rs | 17 +++++++---------- src/game/mod.rs | 12 ++++++++---- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs index 29fe224..b4833ac 100644 --- a/src/core/vulkan/mod.rs +++ b/src/core/vulkan/mod.rs @@ -1,7 +1,9 @@ use vulkan_context::VulkanContext; use window_render_context::WindowRenderContext; -use bevy_app::App; +use bevy_app::{App, Plugin}; + +use super::window::raw_handle::WindowWrapper; mod utils; mod vulkan_context; @@ -13,16 +15,20 @@ pub enum VulkanError { FailedToCreateVulkanContext, } -pub struct Vulkan; +pub struct VulkanPlugin; -impl Vulkan { - pub fn new(app: &mut App) -> Result<(), VulkanError> { +impl Plugin for VulkanPlugin { + fn build(&self, app: &mut App) { let vulkan_context = VulkanContext::from(app as &App); app.world_mut().insert_resource(vulkan_context); + } + fn ready(&self, app: &App) -> bool { + app.world().get_resource::().is_some() + } + + fn finish(&self, app: &mut App) { let window_render_context = WindowRenderContext::from(app as &App); app.world_mut().insert_resource(window_render_context); - - Ok(()) } } diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index 4a19fee..1576e63 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,4 +1,4 @@ -use bevy_app::{App, AppExit, PluginsState}; +use bevy_app::{App, AppExit, Plugin, PluginsState}; use config::WindowConfig; use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; use state::WindowState; @@ -14,23 +14,24 @@ pub enum WindowError { FailedToCreateEventLoop, } -pub struct Window; +pub struct WindowPlugin { + pub window_config: WindowConfig, +} -impl Window { - pub fn new(app: &mut App, window_config: WindowConfig) -> Result<(), WindowError> { +impl Plugin for WindowPlugin { + fn build(&self, app: &mut App) { let world = app.world_mut(); - world.insert_resource(window_config); + world.insert_resource(self.window_config.clone()); let mut event_loop_builder = EventLoop::with_user_event(); let event_loop = event_loop_builder .build() - .map_err(|_| WindowError::FailedToCreateEventLoop)?; + .map_err(|_| WindowError::FailedToCreateEventLoop) + .expect("Failed to create event loop"); world.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle())); app.set_runner(Box::new(move |app| runner(app, event_loop))); - - Ok(()) } } diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 58d78d2..5654022 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,16 +1,13 @@ use std::sync::Arc; use bevy_app::{App, PluginsState}; -use bevy_ecs::{event, world::World}; +use bevy_ecs::world::World; use winit::{ application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId, }; -use super::{ - config::WindowConfig, - raw_handle::{DisplayHandleWrapper, WindowWrapper}, -}; +use super::{config::WindowConfig, raw_handle::WindowWrapper}; pub struct WindowState { app: App, @@ -54,14 +51,14 @@ impl ApplicationHandler for WindowState { if self.app.plugins_state() == PluginsState::Cleaned { self.app.update(); } + + let window_wrapper = self.app.world().get_resource::().unwrap(); + + window_wrapper.0.request_redraw(); } _ => {} } } - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let window_wrapper = self.app.world().get_resource::().unwrap(); - - window_wrapper.0.request_redraw(); - } + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {} } diff --git a/src/game/mod.rs b/src/game/mod.rs index 903a179..e141086 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,8 +1,8 @@ use bevy_app::App; use crate::core::{ - vulkan::Vulkan, - window::{Window, config::WindowConfig}, + vulkan, + window::{self, config::WindowConfig}, }; pub mod test_plugin; @@ -14,7 +14,11 @@ pub fn init(app: &mut App) { height: 600, }; - app.add_plugins(test_plugin::TestPlugin); - Window::new(app, window_config).unwrap(); + app.add_plugins(( + test_plugin::TestPlugin, + window::WindowPlugin { window_config }, + vulkan::VulkanPlugin, + )); + // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); } From b977f446d35e9ce861dd33ed9936fc2fe1e803fd Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 13:15:29 +0200 Subject: [PATCH 47/91] Split crates --- Cargo.lock | 104 +++++------------- Cargo.toml | 22 +++- crates/engine_vulkan/Cargo.toml | 14 +++ .../mod.rs => crates/engine_vulkan/src/lib.rs | 3 +- .../engine_vulkan/src}/utils.rs | 0 .../engine_vulkan/src}/vulkan_context.rs | 3 +- .../src}/window_render_context.rs | 3 +- crates/engine_window/Cargo.toml | 12 ++ .../engine_window/src}/config.rs | 0 .../mod.rs => crates/engine_window/src/lib.rs | 0 .../engine_window/src}/raw_handle.rs | 0 .../engine_window/src}/state.rs | 1 - flake.nix | 2 +- src/core/mod.rs | 2 - src/game/mod.rs | 15 +-- src/game/test_plugin.rs | 13 --- 16 files changed, 84 insertions(+), 110 deletions(-) create mode 100644 crates/engine_vulkan/Cargo.toml rename src/core/vulkan/mod.rs => crates/engine_vulkan/src/lib.rs (94%) rename {src/core/vulkan => crates/engine_vulkan/src}/utils.rs (100%) rename {src/core/vulkan => crates/engine_vulkan/src}/vulkan_context.rs (97%) rename {src/core/vulkan => crates/engine_vulkan/src}/window_render_context.rs (98%) create mode 100644 crates/engine_window/Cargo.toml rename {src/core/window => crates/engine_window/src}/config.rs (100%) rename src/core/window/mod.rs => crates/engine_window/src/lib.rs (100%) rename {src/core/window => crates/engine_window/src}/raw_handle.rs (100%) rename {src/core/window => crates/engine_window/src}/state.rs (96%) delete mode 100644 src/game/test_plugin.rs diff --git a/Cargo.lock b/Cargo.lock index e9d2554..d2dda1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,12 +117,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - [[package]] name = "arrayref" version = "0.3.9" @@ -676,6 +670,32 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "engine_vulkan" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", + "engine_window", + "env_logger", + "log", + "thiserror 2.0.12", + "vulkano", + "winit", +] + +[[package]] +name = "engine_window" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", + "env_logger", + "log", + "thiserror 2.0.12", + "winit", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -986,7 +1006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.52.6", ] [[package]] @@ -1621,13 +1641,13 @@ dependencies = [ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ - "anyhow", "bevy_app", "bevy_ecs", + "engine_vulkan", + "engine_window", "env_logger", "glam 0.30.3", "log", - "thiserror 2.0.12", "vulkano", "vulkano-shaders", "winit", @@ -2386,29 +2406,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -2427,12 +2431,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -2451,12 +2449,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -2475,24 +2467,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -2511,12 +2491,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -2535,12 +2509,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -2559,12 +2527,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2583,12 +2545,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winit" version = "0.30.10" diff --git a/Cargo.toml b/Cargo.toml index d37056f..c5f1546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,11 @@ edition = "2024" authors = ["Florian RICHER "] publish = false -[dependencies] +[workspace] +resolver = "2" +members = ["crates/*"] + +[workspace.dependencies] anyhow = "1.0" thiserror = "2.0" winit = { version = "0.30", features = ["rwh_06"] } @@ -23,3 +27,19 @@ bevy_app = "0.16" # Log and tracing log = "0.4" env_logger = "0.11" + +engine_vulkan = { path = "crates/engine_vulkan" } +engine_window = { path = "crates/engine_window" } + +[dependencies] +log = { workspace = true } +env_logger = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } +winit = { workspace = true } +vulkano = { workspace = true } +vulkano-shaders = { workspace = true } +glam = { workspace = true } + +engine_vulkan = { workspace = true } +engine_window = { workspace = true } diff --git a/crates/engine_vulkan/Cargo.toml b/crates/engine_vulkan/Cargo.toml new file mode 100644 index 0000000..c0fe35b --- /dev/null +++ b/crates/engine_vulkan/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "engine_vulkan" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } +winit = { workspace = true } +vulkano = { workspace = true } +engine_window = { workspace = true } diff --git a/src/core/vulkan/mod.rs b/crates/engine_vulkan/src/lib.rs similarity index 94% rename from src/core/vulkan/mod.rs rename to crates/engine_vulkan/src/lib.rs index b4833ac..e36006e 100644 --- a/src/core/vulkan/mod.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,10 +1,9 @@ +use engine_window::raw_handle::WindowWrapper; use vulkan_context::VulkanContext; use window_render_context::WindowRenderContext; use bevy_app::{App, Plugin}; -use super::window::raw_handle::WindowWrapper; - mod utils; mod vulkan_context; mod window_render_context; diff --git a/src/core/vulkan/utils.rs b/crates/engine_vulkan/src/utils.rs similarity index 100% rename from src/core/vulkan/utils.rs rename to crates/engine_vulkan/src/utils.rs diff --git a/src/core/vulkan/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs similarity index 97% rename from src/core/vulkan/vulkan_context.rs rename to crates/engine_vulkan/src/vulkan_context.rs index 1583df8..7bf3b3b 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/crates/engine_vulkan/src/vulkan_context.rs @@ -2,6 +2,7 @@ use std::{any::Any, sync::Arc}; use bevy_app::App; use bevy_ecs::resource::Resource; +use engine_window::raw_handle::DisplayHandleWrapper; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, @@ -15,8 +16,6 @@ use vulkano::{ }; use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use crate::core::window::raw_handle::DisplayHandleWrapper; - use super::utils; #[derive(Resource)] diff --git a/src/core/vulkan/window_render_context.rs b/crates/engine_vulkan/src/window_render_context.rs similarity index 98% rename from src/core/vulkan/window_render_context.rs rename to crates/engine_vulkan/src/window_render_context.rs index 146be52..87ee958 100644 --- a/src/core/vulkan/window_render_context.rs +++ b/crates/engine_vulkan/src/window_render_context.rs @@ -1,5 +1,6 @@ use bevy_app::App; use bevy_ecs::resource::Resource; +use engine_window::raw_handle::WindowWrapper; use std::sync::Arc; use vulkano::image::view::ImageView; use vulkano::image::{Image, ImageUsage}; @@ -9,8 +10,6 @@ use vulkano::sync::{self, GpuFuture}; use vulkano::{Validated, VulkanError}; use winit::window::Window; -use crate::core::window::raw_handle::WindowWrapper; - use super::vulkan_context::VulkanContext; #[derive(Resource)] diff --git a/crates/engine_window/Cargo.toml b/crates/engine_window/Cargo.toml new file mode 100644 index 0000000..dc19716 --- /dev/null +++ b/crates/engine_window/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "engine_window" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } +winit = { workspace = true } diff --git a/src/core/window/config.rs b/crates/engine_window/src/config.rs similarity index 100% rename from src/core/window/config.rs rename to crates/engine_window/src/config.rs diff --git a/src/core/window/mod.rs b/crates/engine_window/src/lib.rs similarity index 100% rename from src/core/window/mod.rs rename to crates/engine_window/src/lib.rs diff --git a/src/core/window/raw_handle.rs b/crates/engine_window/src/raw_handle.rs similarity index 100% rename from src/core/window/raw_handle.rs rename to crates/engine_window/src/raw_handle.rs diff --git a/src/core/window/state.rs b/crates/engine_window/src/state.rs similarity index 96% rename from src/core/window/state.rs rename to crates/engine_window/src/state.rs index 5654022..0f708fd 100644 --- a/src/core/window/state.rs +++ b/crates/engine_window/src/state.rs @@ -47,7 +47,6 @@ impl ApplicationHandler for WindowState { event_loop.exit(); } WindowEvent::RedrawRequested => { - log::debug!("The window was requested to be redrawn"); if self.app.plugins_state() == PluginsState::Cleaned { self.app.update(); } diff --git a/flake.nix b/flake.nix index ec86293..b00f3ab 100644 --- a/flake.nix +++ b/flake.nix @@ -65,7 +65,7 @@ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; - RUST_LOG = "info,rust_vulkan_test=trace"; + RUST_LOG = "trace,rust_vulkan_test=trace"; }; in { diff --git a/src/core/mod.rs b/src/core/mod.rs index db2061f..a8707c6 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,2 @@ pub mod camera; pub mod render; -pub mod vulkan; -pub mod window; diff --git a/src/game/mod.rs b/src/game/mod.rs index e141086..2078652 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,11 +1,6 @@ use bevy_app::App; - -use crate::core::{ - vulkan, - window::{self, config::WindowConfig}, -}; - -pub mod test_plugin; +use engine_vulkan::VulkanPlugin; +use engine_window::{WindowPlugin, config::WindowConfig}; pub fn init(app: &mut App) { let window_config = WindowConfig { @@ -14,11 +9,7 @@ pub fn init(app: &mut App) { height: 600, }; - app.add_plugins(( - test_plugin::TestPlugin, - window::WindowPlugin { window_config }, - vulkan::VulkanPlugin, - )); + app.add_plugins((WindowPlugin { window_config }, VulkanPlugin)); // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); } diff --git a/src/game/test_plugin.rs b/src/game/test_plugin.rs deleted file mode 100644 index e82e61b..0000000 --- a/src/game/test_plugin.rs +++ /dev/null @@ -1,13 +0,0 @@ -use bevy_app::{App, Last, Plugin, Startup}; - -pub struct TestPlugin; - -impl Plugin for TestPlugin { - fn build(&self, app: &mut App) { - app.add_systems(Last, setup_system); - } -} - -fn setup_system() { - log::info!("Hello, world!"); -} From f585ba78e73ebcd3e64c28a167c61f4320312044 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 17:06:59 +0200 Subject: [PATCH 48/91] Split vulkan resources --- crates/engine_vulkan/src/lib.rs | 71 ++++- crates/engine_vulkan/src/utils.rs | 137 -------- crates/engine_vulkan/src/utils/device.rs | 301 ++++++++++++++++++ crates/engine_vulkan/src/utils/instance.rs | 70 ++++ crates/engine_vulkan/src/utils/mod.rs | 2 + crates/engine_vulkan/src/vulkan_context.rs | 88 ----- .../src/window_render_context.rs | 31 +- flake.nix | 2 +- src/game/mod.rs | 25 +- 9 files changed, 483 insertions(+), 244 deletions(-) delete mode 100644 crates/engine_vulkan/src/utils.rs create mode 100644 crates/engine_vulkan/src/utils/device.rs create mode 100644 crates/engine_vulkan/src/utils/instance.rs create mode 100644 crates/engine_vulkan/src/utils/mod.rs delete mode 100644 crates/engine_vulkan/src/vulkan_context.rs diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index e36006e..310a1b9 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,25 +1,86 @@ +use std::sync::Arc; + +use bevy_ecs::resource::Resource; use engine_window::raw_handle::WindowWrapper; -use vulkan_context::VulkanContext; +use utils::{device::create_and_insert_device, instance::create_and_insert_instance}; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, DeviceExtensions, DeviceFeatures, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; use window_render_context::WindowRenderContext; use bevy_app::{App, Plugin}; mod utils; -mod vulkan_context; mod window_render_context; +#[derive(Resource)] +pub struct VulkanInstance(Arc); + +#[derive(Resource)] +pub struct VulkanDevice(Arc); + +#[derive(Resource)] +pub struct VulkanGraphicsQueue(Arc); + +#[derive(Resource)] +pub struct VulkanComputeQueue(Arc); + +#[derive(Resource)] +pub struct VulkanTransferQueue(Arc); +#[derive(Resource)] +pub struct VulkanMemoryAllocator(Arc); + +#[derive(Resource)] +pub struct VulkanCommandBufferAllocator(Arc); + +#[derive(Resource)] +pub struct VulkanDescriptorSetAllocator(Arc); + #[derive(Debug, thiserror::Error)] pub enum VulkanError { #[error("Failed to create vulkan context")] FailedToCreateVulkanContext, } -pub struct VulkanPlugin; +pub struct VulkanConfig { + pub instance_layers: Vec, + pub device_extensions: DeviceExtensions, + pub device_features: DeviceFeatures, + pub with_window_surface: bool, + pub with_graphics_queue: bool, + pub with_compute_queue: bool, + pub with_transfer_queue: bool, +} + +impl Default for VulkanConfig { + fn default() -> Self { + Self { + instance_layers: Vec::default(), + device_extensions: DeviceExtensions::default(), + device_features: DeviceFeatures::default(), + with_window_surface: true, + with_graphics_queue: true, + with_compute_queue: true, + with_transfer_queue: true, + } + } +} + +#[derive(Default)] +pub struct VulkanPlugin { + pub vulkan_config: VulkanConfig, +} impl Plugin for VulkanPlugin { fn build(&self, app: &mut App) { - let vulkan_context = VulkanContext::from(app as &App); - app.world_mut().insert_resource(vulkan_context); + let world = app.world_mut(); + + create_and_insert_instance(world, &self.vulkan_config); + create_and_insert_device(world, &self.vulkan_config); } fn ready(&self, app: &App) -> bool { diff --git a/crates/engine_vulkan/src/utils.rs b/crates/engine_vulkan/src/utils.rs deleted file mode 100644 index 046528f..0000000 --- a/crates/engine_vulkan/src/utils.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::sync::Arc; - -use vulkano::{ - Version, VulkanLibrary, - device::{ - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, - QueueFlags, - physical::{PhysicalDevice, PhysicalDeviceType}, - }, - instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, -}; -use winit::raw_window_handle::HasDisplayHandle; - -pub(super) fn load_library() -> Arc { - let library = VulkanLibrary::new().unwrap(); - - log::debug!("Available layer:"); - for layer in library.layer_properties().unwrap() { - log::debug!( - "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", - layer.name(), - layer.description(), - layer.implementation_version(), - layer.vulkan_version() - ); - } - - library -} - -pub(super) fn create_instance( - library: Arc, - required_extensions: InstanceExtensions, -) -> Arc { - Instance::new( - library, - InstanceCreateInfo { - // Enable enumerating devices that use non-conformant Vulkan implementations. - // (e.g. MoltenVK) - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], - ..Default::default() - }, - ) - .unwrap() -} - -pub(super) fn find_physical_device_queue_family_indexes( - physical_device: &Arc, - display_handle: &impl HasDisplayHandle, -) -> Option { - let mut graphic_queue_family_index = None; - - for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { - if queue_family_property - .queue_flags - .intersects(QueueFlags::GRAPHICS) - && physical_device - .presentation_support(i as u32, display_handle) - .unwrap() - { - graphic_queue_family_index = Some(i as u32); - } - } - - graphic_queue_family_index -} - -pub(super) fn pick_physical_device_and_queue_family_indexes( - instance: &Arc, - display_handle: &impl HasDisplayHandle, - device_extensions: &DeviceExtensions, -) -> Option<(Arc, u32)> { - instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) - .filter(|p| p.supported_extensions().contains(device_extensions)) - .filter_map(|p| { - find_physical_device_queue_family_indexes(&p, display_handle) - .and_then(|indexes| Some((p, indexes))) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) -} - -pub(super) fn pick_graphics_device( - instance: &Arc, - display_handle: &impl HasDisplayHandle, -) -> (Arc, impl ExactSizeIterator>) { - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, graphics_family_index) = - pick_physical_device_and_queue_family_indexes(instance, display_handle, &device_extensions) - .unwrap(); - - log::debug!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - if physical_device.api_version() < Version::V1_3 { - device_extensions.khr_dynamic_rendering = true; - } - - log::debug!("Using device extensions: {:#?}", device_extensions); - - Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index: graphics_family_index, - ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - ..Default::default() - }, - ) - .unwrap() -} diff --git a/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/utils/device.rs new file mode 100644 index 0000000..6c4b49f --- /dev/null +++ b/crates/engine_vulkan/src/utils/device.rs @@ -0,0 +1,301 @@ +use std::sync::Arc; + +use bevy_ecs::world::World; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, + }, + memory::allocator::StandardMemoryAllocator, +}; + +use crate::{ + VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanConfig, VulkanDescriptorSetAllocator, + VulkanDevice, VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator, VulkanTransferQueue, +}; + +pub fn create_and_insert_device(world: &mut World, config: &VulkanConfig) { + let picked_device = + pick_physical_device(world, &config).expect("Failed to pick physical device"); + + let device = picked_device.device; + let physical_device = device.physical_device(); + + log::debug!("Vulkan device created"); + log::debug!( + "\tPhysical device: {:?} ({:?})", + physical_device.properties().device_name, + physical_device.properties().device_type + ); + log::debug!("\tDevice extensions: {:?}", device.enabled_extensions()); + log::debug!("\tDevice features: {:?}", device.enabled_features()); + + world.insert_resource(VulkanDevice(device.clone())); + + log::debug!("\tDevice selected queues:"); + if config.with_graphics_queue { + world.insert_resource(VulkanGraphicsQueue( + picked_device + .graphics_queue + .expect("Failed to get graphics queue"), + )); + log::debug!("\t\t- Graphics queue"); + } + + if config.with_compute_queue { + world.insert_resource(VulkanComputeQueue( + picked_device + .compute_queue + .expect("Failed to get compute queue"), + )); + log::debug!("\t\t- Compute queue"); + } + + if config.with_transfer_queue { + world.insert_resource(VulkanTransferQueue( + picked_device + .transfer_queue + .expect("Failed to get transfer queue"), + )); + log::debug!("\t\t- Transfer queue"); + } + + world.insert_resource(VulkanMemoryAllocator(Arc::new( + StandardMemoryAllocator::new_default(device.clone()), + ))); + world.insert_resource(VulkanCommandBufferAllocator(Arc::new( + StandardCommandBufferAllocator::new(device.clone(), Default::default()), + ))); + world.insert_resource(VulkanDescriptorSetAllocator(Arc::new( + StandardDescriptorSetAllocator::new(device.clone(), Default::default()), + ))); +} + +struct PickedDevice { + pub device: Arc, + pub graphics_queue: Option>, + pub compute_queue: Option>, + pub transfer_queue: Option>, +} + +fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option { + let instance = world + .get_resource::() + .expect("Failed to get VulkanInstance during vulkan plugin initialization"); + + instance + .0 + .enumerate_physical_devices() + .expect("Failed to enumerate physical devices") + .filter_map(|p| check_physical_device(world, &p, config)) + .min_by_key( + |p| match p.device.physical_device().properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }, + ) + .take() +} + +fn check_device_extensions_support( + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + let device_extensions = DeviceExtensions { + khr_swapchain: config.with_window_surface, + ..config.device_extensions + }; + + if physical_device + .supported_extensions() + .contains(&device_extensions) + { + log::debug!( + "\t\t[OK] Device supports required extensions {:?}", + device_extensions + ); + Some(device_extensions) + } else { + log::debug!( + "\t\t[FAILED] Device does not support required extensions {:?}", + device_extensions + ); + None + } +} + +struct PickedQueuesInfo { + graphics_queue_family_index: Option, + compute_queue_family_index: Option, + transfer_queue_family_index: Option, +} + +fn check_queues_support( + world: &World, + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + let mut graphics_queue_family_index: Option = None; + let mut compute_queue_family_index: Option = None; + let mut transfer_queue_family_index: Option = None; + + for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { + if config.with_graphics_queue { + let graphics_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::GRAPHICS); + + let presentation_valid = if config.with_window_surface { + let display_handle = world + .get_resource::() + .expect("DisplayHandleWrapper must be added before VulkanPlugin"); + + physical_device + .presentation_support(i as u32, &display_handle.0) + .expect("Failed to check presentation support") + } else { + true + }; + + if graphics_supported && presentation_valid { + graphics_queue_family_index = Some(i as u32); + } + } + + if config.with_compute_queue { + let compute_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::COMPUTE); + + if compute_supported { + compute_queue_family_index = Some(i as u32); + } + } + + if config.with_transfer_queue { + let transfer_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::TRANSFER); + + if transfer_supported { + transfer_queue_family_index = Some(i as u32); + } + } + } + + if !config.with_graphics_queue { + log::debug!("\t\t[SKIPPED] Graphics queue is not required"); + } else if graphics_queue_family_index.is_some() { + log::debug!("\t\t[OK] Graphics queue is supported"); + } else { + log::debug!("\t\t[FAILED] Graphics queue is not supported"); + return None; + } + + if !config.with_compute_queue { + log::debug!("\t\t[SKIPPED] Compute queue is not required"); + } else if compute_queue_family_index.is_some() { + log::debug!("\t\t[OK] Compute queue is supported"); + } else { + log::debug!("\t\t[FAILED] Compute queue is not supported"); + return None; + } + + if !config.with_transfer_queue { + log::debug!("\t\t[SKIPPED] Transfer queue is not required"); + } else if transfer_queue_family_index.is_some() { + log::debug!("\t\t[OK] Transfer queue is supported"); + } else { + log::debug!("\t\t[FAILED] Transfer queue is not supported"); + return None; + } + + Some(PickedQueuesInfo { + graphics_queue_family_index, + compute_queue_family_index, + transfer_queue_family_index, + }) +} + +fn check_physical_device( + world: &World, + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + log::debug!("Checking physical device"); + log::debug!("\tProperties"); + log::debug!("\t\tName: {}", physical_device.properties().device_name); + log::debug!("\t\tAPI version: {}", physical_device.api_version()); + log::debug!( + "\t\tDevice type: {:?}", + physical_device.properties().device_type + ); + log::debug!("\tRequired supports checking report"); + + let device_extensions = check_device_extensions_support(physical_device, config)?; + let picked_queues_info = check_queues_support(world, physical_device, config)?; + + let mut queue_create_infos = vec![]; + + if config.with_graphics_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), + ..Default::default() + }); + } + + if config.with_compute_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), + ..Default::default() + }); + } + + if config.with_transfer_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), + ..Default::default() + }); + } + + let (device, mut queues) = Device::new( + physical_device.clone(), + DeviceCreateInfo { + queue_create_infos, + enabled_extensions: device_extensions, + enabled_features: config.device_features, + ..Default::default() + }, + ) + .expect("Failed to create device"); + + let mut graphics_queue = None; + let mut compute_queue = None; + let mut transfer_queue = None; + + if config.with_graphics_queue { + graphics_queue = queues.next(); + } + + if config.with_compute_queue { + compute_queue = queues.next(); + } + + if config.with_transfer_queue { + transfer_queue = queues.next(); + } + + Some(PickedDevice { + device, + graphics_queue, + compute_queue, + transfer_queue, + }) +} diff --git a/crates/engine_vulkan/src/utils/instance.rs b/crates/engine_vulkan/src/utils/instance.rs new file mode 100644 index 0000000..dcda857 --- /dev/null +++ b/crates/engine_vulkan/src/utils/instance.rs @@ -0,0 +1,70 @@ +use std::sync::Arc; + +use bevy_ecs::world::World; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + VulkanLibrary, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + swapchain::Surface, +}; + +use crate::{VulkanConfig, VulkanInstance}; + +fn load_library() -> Arc { + let library = VulkanLibrary::new().unwrap(); + + log::debug!("Available Instance layers:"); + for layer in library.layer_properties().unwrap() { + log::debug!( + "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", + layer.name(), + layer.description(), + layer.implementation_version(), + layer.vulkan_version() + ); + } + + library +} + +pub fn create_and_insert_instance(world: &mut World, config: &VulkanConfig) { + let library = load_library(); + + let instance_extensions = { + if config.with_window_surface { + let display_handle = world + .get_resource::() + .expect("DisplayHandleWrapper must be added before VulkanPlugin"); + + Surface::required_extensions(&display_handle.0) + .expect("Failed to get surface required extensions") + } else { + InstanceExtensions::default() + } + }; + + let instance = Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: instance_extensions, + enabled_layers: config.instance_layers.clone(), + ..Default::default() + }, + ) + .expect("Failed to create vulkan instance"); + + log::debug!("Instance created"); + log::debug!( + "\t- Enabled extensions: {:?}", + instance.enabled_extensions() + ); + log::debug!("\t- Enabled layers: {:?}", instance.enabled_layers()); + log::debug!("\t- API version: {:?}", instance.api_version()); + log::debug!("\t- Max API version: {:?}", instance.max_api_version()); + log::debug!("\t- Flags: {:?}", instance.flags()); + + world.insert_resource(VulkanInstance(instance)); +} diff --git a/crates/engine_vulkan/src/utils/mod.rs b/crates/engine_vulkan/src/utils/mod.rs new file mode 100644 index 0000000..09e4b35 --- /dev/null +++ b/crates/engine_vulkan/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod device; +pub mod instance; diff --git a/crates/engine_vulkan/src/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs deleted file mode 100644 index 7bf3b3b..0000000 --- a/crates/engine_vulkan/src/vulkan_context.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::{any::Any, sync::Arc}; - -use bevy_app::App; -use bevy_ecs::resource::Resource; -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::{ - command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, - allocator::StandardCommandBufferAllocator, - }, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{Device, Queue}, - instance::Instance, - memory::allocator::StandardMemoryAllocator, - swapchain::Surface, -}; -use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; - -use super::utils; - -#[derive(Resource)] -pub struct VulkanContext { - pub instance: Arc, - pub device: Arc, - pub graphics_queue: Arc, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, -} - -impl VulkanContext { - pub fn create_surface( - &self, - window: Arc, - ) -> Arc { - Surface::from_window(self.instance.clone(), window).unwrap() - } - - pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { - AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap() - } -} - -impl From<&App> for VulkanContext { - fn from(app: &App) -> Self { - let library = utils::load_library(); - - let world = app.world(); - - let display_handle: &DisplayHandleWrapper = - world.get_resource::().unwrap(); - - let enabled_extensions = Surface::required_extensions(&display_handle.0).unwrap(); - log::debug!("Surface required extensions: {enabled_extensions:?}"); - - let instance = utils::create_instance(library.clone(), enabled_extensions); - - let (device, mut queues) = utils::pick_graphics_device(&instance, &display_handle.0); - let graphics_queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - - Self { - instance: instance.clone(), - device, - graphics_queue, - memory_allocator, - command_buffer_allocator, - descriptor_set_allocator, - } - } -} diff --git a/crates/engine_vulkan/src/window_render_context.rs b/crates/engine_vulkan/src/window_render_context.rs index 87ee958..95b3d90 100644 --- a/crates/engine_vulkan/src/window_render_context.rs +++ b/crates/engine_vulkan/src/window_render_context.rs @@ -5,12 +5,12 @@ use std::sync::Arc; use vulkano::image::view::ImageView; use vulkano::image::{Image, ImageUsage}; use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::swapchain::{Swapchain, SwapchainCreateInfo}; +use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use vulkano::sync::{self, GpuFuture}; use vulkano::{Validated, VulkanError}; use winit::window::Window; -use super::vulkan_context::VulkanContext; +use crate::{VulkanDevice, VulkanInstance}; #[derive(Resource)] pub struct WindowRenderContext { @@ -25,27 +25,36 @@ pub struct WindowRenderContext { impl From<&App> for WindowRenderContext { fn from(app: &App) -> Self { let world = app.world(); - let vulkan_context = world.get_resource::().unwrap(); - let window_handle = world.get_resource::().unwrap(); + let window_handle = world + .get_resource::() + .expect("Failed to find window handle"); + let vulkan_instance = world + .get_resource::() + .expect("Failed to find vulkan instance"); + let vulkan_device = world + .get_resource::() + .expect("Failed to find vulkan device"); + let window_size = window_handle.0.inner_size(); - let surface = vulkan_context.create_surface(window_handle.0.clone()); + let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone()) + .expect("Failed to create surface"); let (swapchain, images) = { - let surface_capabilities = vulkan_context - .device + let surface_capabilities = vulkan_device + .0 .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let (image_format, _) = vulkan_context - .device + let (image_format, _) = vulkan_device + .0 .physical_device() .surface_formats(&surface, Default::default()) .unwrap()[0]; Swapchain::new( - vulkan_context.device.clone(), + vulkan_device.0.clone(), surface, SwapchainCreateInfo { // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. @@ -74,7 +83,7 @@ impl From<&App> for WindowRenderContext { }; let recreate_swapchain = false; - let previous_frame_end = Some(sync::now(vulkan_context.device.clone()).boxed_send_sync()); + let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync()); Self { window: window_handle.0.clone(), diff --git a/flake.nix b/flake.nix index b00f3ab..9cef9c1 100644 --- a/flake.nix +++ b/flake.nix @@ -65,7 +65,7 @@ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; - RUST_LOG = "trace,rust_vulkan_test=trace"; + RUST_LOG = "debug,rust_vulkan_test=trace"; }; in { diff --git a/src/game/mod.rs b/src/game/mod.rs index 2078652..2c30bf8 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,6 +1,7 @@ use bevy_app::App; -use engine_vulkan::VulkanPlugin; +use engine_vulkan::{VulkanConfig, VulkanPlugin}; use engine_window::{WindowPlugin, config::WindowConfig}; +use vulkano::device::{DeviceExtensions, DeviceFeatures}; pub fn init(app: &mut App) { let window_config = WindowConfig { @@ -9,7 +10,27 @@ pub fn init(app: &mut App) { height: 600, }; - app.add_plugins((WindowPlugin { window_config }, VulkanPlugin)); + let device_extensions = DeviceExtensions { + khr_dynamic_rendering: true, + ..Default::default() + }; + + let device_features = DeviceFeatures { + dynamic_rendering: true, + ..Default::default() + }; + + let vulkan_config = VulkanConfig { + instance_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + device_extensions, + device_features, + ..Default::default() + }; + + app.add_plugins(( + WindowPlugin { window_config }, + VulkanPlugin { vulkan_config }, + )); // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); } From 0ee29a3649c507f451ae7f63e06deacc67664c91 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 18:02:54 +0200 Subject: [PATCH 49/91] render_plugin: Add first SubApp and default schedules --- Cargo.lock | 11 +++- Cargo.toml | 2 + crates/engine_render/Cargo.toml | 9 +++ crates/engine_render/src/lib.rs | 65 ++++++++++++++++++ crates/engine_vulkan/src/vulkan_context.rs | 76 ++++++++++++++++++++++ crates/engine_window/Cargo.toml | 1 - src/game/mod.rs | 2 + 7 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 crates/engine_render/Cargo.toml create mode 100644 crates/engine_render/src/lib.rs create mode 100644 crates/engine_vulkan/src/vulkan_context.rs diff --git a/Cargo.lock b/Cargo.lock index d2dda1a..6467d38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -670,6 +670,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "engine_render" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", + "log", +] + [[package]] name = "engine_vulkan" version = "0.1.0" @@ -690,7 +699,6 @@ version = "0.1.0" dependencies = [ "bevy_app", "bevy_ecs", - "env_logger", "log", "thiserror 2.0.12", "winit", @@ -1643,6 +1651,7 @@ version = "0.1.0" dependencies = [ "bevy_app", "bevy_ecs", + "engine_render", "engine_vulkan", "engine_window", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index c5f1546..d8c8792 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ env_logger = "0.11" engine_vulkan = { path = "crates/engine_vulkan" } engine_window = { path = "crates/engine_window" } +engine_render = { path = "crates/engine_render" } [dependencies] log = { workspace = true } @@ -43,3 +44,4 @@ glam = { workspace = true } engine_vulkan = { workspace = true } engine_window = { workspace = true } +engine_render = { workspace = true } diff --git a/crates/engine_render/Cargo.toml b/crates/engine_render/Cargo.toml new file mode 100644 index 0000000..bd283f0 --- /dev/null +++ b/crates/engine_render/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "engine_render" +version = "0.1.0" +edition = "2024" + +[dependencies] +log = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs new file mode 100644 index 0000000..febefbf --- /dev/null +++ b/crates/engine_render/src/lib.rs @@ -0,0 +1,65 @@ +use bevy_app::{App, AppLabel, Last, Plugin, SubApp}; +use bevy_ecs::{ + schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet}, + system::Commands, +}; + +#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] +pub enum RenderSystems { + Prepare, + Queue, + Render, + Present, +} + +#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)] +pub struct Render; + +impl Render { + pub fn base_schedule() -> Schedule { + use RenderSystems::*; + + let mut schedule = Schedule::new(Self); + + schedule.configure_sets((Prepare, Queue, Render, Present).chain()); + + schedule + } +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)] +pub struct RenderApp; + +pub struct RenderPlugin; + +impl Plugin for RenderPlugin { + fn build(&self, app: &mut App) { + let mut render_app = SubApp::new(); + render_app.update_schedule = Some(Render.intern()); + + render_app.add_schedule(Render::base_schedule()); + + render_app.add_systems(Render, test_prepare.in_set(RenderSystems::Prepare)); + render_app.add_systems(Render, test_queue.in_set(RenderSystems::Queue)); + render_app.add_systems(Render, test_render.in_set(RenderSystems::Render)); + render_app.add_systems(Render, test_present.in_set(RenderSystems::Present)); + + app.insert_sub_app(RenderApp, render_app); + } +} + +fn test_prepare(mut commands: Commands) { + log::info!("test_prepare"); +} + +fn test_queue(mut commands: Commands) { + log::info!("test_queue"); +} + +fn test_render(mut commands: Commands) { + log::info!("test_render"); +} + +fn test_present(mut commands: Commands) { + log::info!("test_present"); +} diff --git a/crates/engine_vulkan/src/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs new file mode 100644 index 0000000..08ff1ac --- /dev/null +++ b/crates/engine_vulkan/src/vulkan_context.rs @@ -0,0 +1,76 @@ +use std::{any::Any, sync::Arc}; + +use bevy_app::App; +use bevy_ecs::resource::Resource; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + allocator::StandardCommandBufferAllocator, + }, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, + swapchain::Surface, +}; +use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; + +use super::utils; + +#[derive(Resource)] +pub struct VulkanContext { + pub instance: Arc, + pub device: Arc, + pub graphics_queue: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +impl VulkanContext { + pub fn create_surface( + &self, + window: Arc, + ) -> Arc { + Surface::from_window(self.instance.clone(), window).unwrap() + } + + pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { + AutoCommandBufferBuilder::primary( + self.command_buffer_allocator.clone(), + self.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap() + } +} + +impl From<&App> for VulkanContext { + fn from(app: &App) -> Self { + let (device, mut queues) = utils::pick_graphics_device(&instance, &display_handle.0); + let graphics_queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + + Self { + instance: instance.clone(), + device, + graphics_queue, + memory_allocator, + command_buffer_allocator, + descriptor_set_allocator, + } + } +} diff --git a/crates/engine_window/Cargo.toml b/crates/engine_window/Cargo.toml index dc19716..b5016cc 100644 --- a/crates/engine_window/Cargo.toml +++ b/crates/engine_window/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] thiserror = { workspace = true } log = { workspace = true } -env_logger = { workspace = true } bevy_app = { workspace = true } bevy_ecs = { workspace = true } winit = { workspace = true } diff --git a/src/game/mod.rs b/src/game/mod.rs index 2c30bf8..ff3df3c 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,4 +1,5 @@ use bevy_app::App; +use engine_render::RenderPlugin; use engine_vulkan::{VulkanConfig, VulkanPlugin}; use engine_window::{WindowPlugin, config::WindowConfig}; use vulkano::device::{DeviceExtensions, DeviceFeatures}; @@ -30,6 +31,7 @@ pub fn init(app: &mut App) { app.add_plugins(( WindowPlugin { window_config }, VulkanPlugin { vulkan_config }, + RenderPlugin, )); // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); From ae0a2be097a0565cb862d7384a292ffec0958227 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 19:28:34 +0200 Subject: [PATCH 50/91] render_plugin: Begin add window plugin --- Cargo.lock | 3 + crates/engine_render/Cargo.toml | 3 + crates/engine_render/src/lib.rs | 82 ++++++++++++++++++++------ crates/engine_render/src/window/mod.rs | 35 +++++++++++ crates/engine_vulkan/src/lib.rs | 42 ++++++------- crates/engine_window/src/lib.rs | 2 +- crates/engine_window/src/raw_handle.rs | 4 +- src/game/mod.rs | 11 +++- 8 files changed, 134 insertions(+), 48 deletions(-) create mode 100644 crates/engine_render/src/window/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6467d38..e538714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -676,7 +676,10 @@ version = "0.1.0" dependencies = [ "bevy_app", "bevy_ecs", + "engine_vulkan", + "engine_window", "log", + "vulkano", ] [[package]] diff --git a/crates/engine_render/Cargo.toml b/crates/engine_render/Cargo.toml index bd283f0..5016279 100644 --- a/crates/engine_render/Cargo.toml +++ b/crates/engine_render/Cargo.toml @@ -7,3 +7,6 @@ edition = "2024" log = { workspace = true } bevy_app = { workspace = true } bevy_ecs = { workspace = true } +vulkano = { workspace = true } +engine_vulkan = { workspace = true } +engine_window = { workspace = true } diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs index febefbf..f42013e 100644 --- a/crates/engine_render/src/lib.rs +++ b/crates/engine_render/src/lib.rs @@ -1,11 +1,21 @@ use bevy_app::{App, AppLabel, Last, Plugin, SubApp}; use bevy_ecs::{ schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet}, - system::Commands, + system::{Commands, Res}, + world::World, }; +use engine_vulkan::{ + VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, + VulkanMemoryAllocator, +}; +use engine_window::raw_handle::WindowWrapper; +use window::WindowRenderPlugin; + +pub mod window; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum RenderSystems { + ManageViews, Prepare, Queue, Render, @@ -33,33 +43,69 @@ pub struct RenderApp; pub struct RenderPlugin; impl Plugin for RenderPlugin { - fn build(&self, app: &mut App) { + fn build(&self, _app: &mut App) {} + + fn ready(&self, app: &App) -> bool { + let world = app.world(); + + world.get_resource::().is_some() + && world.get_resource::().is_some() + && world.get_resource::().is_some() + && world.get_resource::().is_some() + && world + .get_resource::() + .is_some() + && world + .get_resource::() + .is_some() + } + + fn finish(&self, app: &mut App) { let mut render_app = SubApp::new(); render_app.update_schedule = Some(Render.intern()); - render_app.add_schedule(Render::base_schedule()); - render_app.add_systems(Render, test_prepare.in_set(RenderSystems::Prepare)); - render_app.add_systems(Render, test_queue.in_set(RenderSystems::Queue)); - render_app.add_systems(Render, test_render.in_set(RenderSystems::Render)); - render_app.add_systems(Render, test_present.in_set(RenderSystems::Present)); + extract_app_resources(app.world_mut(), render_app.world_mut()); app.insert_sub_app(RenderApp, render_app); + + app.add_plugins(WindowRenderPlugin); + } + + fn cleanup(&self, app: &mut App) { + app.remove_sub_app(RenderApp); } } -fn test_prepare(mut commands: Commands) { - log::info!("test_prepare"); -} +fn extract_app_resources(world: &mut World, render_world: &mut World) { + let window_wrapper = world + .get_resource::() + .expect("Failed to get WindowWrapper. Check is WindowPlugin is added before RenderPlugin."); -fn test_queue(mut commands: Commands) { - log::info!("test_queue"); -} + let vulkan_device = world + .get_resource::() + .expect("Failed to get Vulkan device. Check is VulkanPlugin is added before RenderPlugin."); -fn test_render(mut commands: Commands) { - log::info!("test_render"); -} + let vulkan_graphics_queue = world.get_resource::().expect( + "Failed to get Vulkan graphics queue. Check is VulkanPlugin is added before RenderPlugin.", + ); -fn test_present(mut commands: Commands) { - log::info!("test_present"); + let vulkan_memory_allocator = world + .get_resource::() + .expect("Failed to get Vulkan memory allocator. Check is VulkanPlugin is added before RenderPlugin."); + + let vulkan_command_buffer_allocator = world + .get_resource::() + .expect("Failed to get Vulkan command buffer allocator. Check is VulkanPlugin is added before RenderPlugin."); + + let vulkan_descriptor_set_allocator = world + .get_resource::() + .expect("Failed to get Vulkan descriptor set allocator. Check is VulkanPlugin is added before RenderPlugin."); + + render_world.insert_resource(vulkan_device.clone()); + render_world.insert_resource(vulkan_graphics_queue.clone()); + render_world.insert_resource(vulkan_memory_allocator.clone()); + render_world.insert_resource(vulkan_command_buffer_allocator.clone()); + render_world.insert_resource(vulkan_descriptor_set_allocator.clone()); + render_world.insert_resource(window_wrapper.clone()); } diff --git a/crates/engine_render/src/window/mod.rs b/crates/engine_render/src/window/mod.rs new file mode 100644 index 0000000..bcb3ea6 --- /dev/null +++ b/crates/engine_render/src/window/mod.rs @@ -0,0 +1,35 @@ +use std::sync::Arc; + +use bevy_app::{App, Plugin}; +use bevy_ecs::resource::Resource; +use vulkano::{ + image::view::ImageView, pipeline::graphics::viewport::Viewport, swapchain::Swapchain, + sync::GpuFuture, +}; + +use crate::RenderApp; + +pub struct WindowSurfaceData { + pub swapchain: Arc, + pub attachment_image_views: Vec>, + pub viewport: Viewport, + pub recreate_swapchain: bool, + pub previous_frame_end: Option>, +} + +#[derive(Resource, Default)] +pub struct WindowSurface { + pub surface: Option, +} + +pub struct WindowRenderPlugin; + +impl Plugin for WindowRenderPlugin { + fn build(&self, app: &mut App) { + let render_app = app + .get_sub_app_mut(RenderApp) + .expect("Failed to get RenderApp. Check is RenderPlugin is added."); + + render_app.init_resource::(); + } +} diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index 310a1b9..65e9349 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -17,28 +17,29 @@ use bevy_app::{App, Plugin}; mod utils; mod window_render_context; -#[derive(Resource)] -pub struct VulkanInstance(Arc); +#[derive(Resource, Clone)] +pub struct VulkanInstance(pub Arc); -#[derive(Resource)] -pub struct VulkanDevice(Arc); +#[derive(Resource, Clone)] +pub struct VulkanDevice(pub Arc); -#[derive(Resource)] -pub struct VulkanGraphicsQueue(Arc); +#[derive(Resource, Clone)] +pub struct VulkanGraphicsQueue(pub Arc); -#[derive(Resource)] -pub struct VulkanComputeQueue(Arc); +#[derive(Resource, Clone)] +pub struct VulkanComputeQueue(pub Arc); -#[derive(Resource)] -pub struct VulkanTransferQueue(Arc); -#[derive(Resource)] -pub struct VulkanMemoryAllocator(Arc); +#[derive(Resource, Clone)] +pub struct VulkanTransferQueue(pub Arc); -#[derive(Resource)] -pub struct VulkanCommandBufferAllocator(Arc); +#[derive(Resource, Clone)] +pub struct VulkanMemoryAllocator(pub Arc); -#[derive(Resource)] -pub struct VulkanDescriptorSetAllocator(Arc); +#[derive(Resource, Clone)] +pub struct VulkanCommandBufferAllocator(pub Arc); + +#[derive(Resource, Clone)] +pub struct VulkanDescriptorSetAllocator(pub Arc); #[derive(Debug, thiserror::Error)] pub enum VulkanError { @@ -82,13 +83,4 @@ impl Plugin for VulkanPlugin { create_and_insert_instance(world, &self.vulkan_config); create_and_insert_device(world, &self.vulkan_config); } - - fn ready(&self, app: &App) -> bool { - app.world().get_resource::().is_some() - } - - fn finish(&self, app: &mut App) { - let window_render_context = WindowRenderContext::from(app as &App); - app.world_mut().insert_resource(window_render_context); - } } diff --git a/crates/engine_window/src/lib.rs b/crates/engine_window/src/lib.rs index 1576e63..33e150c 100644 --- a/crates/engine_window/src/lib.rs +++ b/crates/engine_window/src/lib.rs @@ -1,6 +1,6 @@ use bevy_app::{App, AppExit, Plugin, PluginsState}; use config::WindowConfig; -use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; +use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper, WindowWrapper}; use state::WindowState; use winit::event_loop::EventLoop; diff --git a/crates/engine_window/src/raw_handle.rs b/crates/engine_window/src/raw_handle.rs index c896b56..87d5988 100644 --- a/crates/engine_window/src/raw_handle.rs +++ b/crates/engine_window/src/raw_handle.rs @@ -16,8 +16,8 @@ impl EventLoopProxyWrapper { } } -#[derive(Resource)] +#[derive(Resource, Clone)] pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); -#[derive(Resource)] +#[derive(Resource, Clone)] pub struct WindowWrapper(pub Arc); diff --git a/src/game/mod.rs b/src/game/mod.rs index ff3df3c..9c5b547 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -33,6 +33,13 @@ pub fn init(app: &mut App) { VulkanPlugin { vulkan_config }, RenderPlugin, )); - // Window::new(app, window_config).unwrap(); - // Vulkan::new(app).unwrap(); + + // app.get_sub_app_mut(RenderApp) + // .expect("Failed to get RenderApp. Check is RenderPlugin is added.") + // .add_systems(Render, test_system.in_set(RenderSystems::Prepare)); } + +// fn test_system(vulkan_device: Res, vulkan_graphics_queue: Res) { +// log::trace!("vulkan_device: {:?}", vulkan_device.0); +// log::trace!("vulkan_graphics_queue: {:?}", vulkan_graphics_queue.0); +// } From 62d12f2ab827c5e6a327d10d841362b2fa19484b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 21:00:22 +0200 Subject: [PATCH 51/91] render_plugin: Autocreate swapchain and update --- crates/engine_render/src/lib.rs | 14 +- crates/engine_render/src/window/mod.rs | 197 +++++++++++++++++- crates/engine_vulkan/src/lib.rs | 3 - .../src/window_render_context.rs | 125 ----------- src/game/mod.rs | 9 - 5 files changed, 201 insertions(+), 147 deletions(-) delete mode 100644 crates/engine_vulkan/src/window_render_context.rs diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs index f42013e..ccbc39d 100644 --- a/crates/engine_render/src/lib.rs +++ b/crates/engine_render/src/lib.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ }; use engine_vulkan::{ VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, - VulkanMemoryAllocator, + VulkanInstance, VulkanMemoryAllocator, }; use engine_window::raw_handle::WindowWrapper; use window::WindowRenderPlugin; @@ -31,7 +31,7 @@ impl Render { let mut schedule = Schedule::new(Self); - schedule.configure_sets((Prepare, Queue, Render, Present).chain()); + schedule.configure_sets((ManageViews, Prepare, Queue, Render, Present).chain()); schedule } @@ -49,6 +49,7 @@ impl Plugin for RenderPlugin { let world = app.world(); world.get_resource::().is_some() + && world.get_resource::().is_some() && world.get_resource::().is_some() && world.get_resource::().is_some() && world.get_resource::().is_some() @@ -71,10 +72,6 @@ impl Plugin for RenderPlugin { app.add_plugins(WindowRenderPlugin); } - - fn cleanup(&self, app: &mut App) { - app.remove_sub_app(RenderApp); - } } fn extract_app_resources(world: &mut World, render_world: &mut World) { @@ -82,6 +79,10 @@ fn extract_app_resources(world: &mut World, render_world: &mut World) { .get_resource::() .expect("Failed to get WindowWrapper. Check is WindowPlugin is added before RenderPlugin."); + let vulkan_instance = world.get_resource::().expect( + "Failed to get Vulkan instance. Check is VulkanPlugin is added before RenderPlugin.", + ); + let vulkan_device = world .get_resource::() .expect("Failed to get Vulkan device. Check is VulkanPlugin is added before RenderPlugin."); @@ -102,6 +103,7 @@ fn extract_app_resources(world: &mut World, render_world: &mut World) { .get_resource::() .expect("Failed to get Vulkan descriptor set allocator. Check is VulkanPlugin is added before RenderPlugin."); + render_world.insert_resource(vulkan_instance.clone()); render_world.insert_resource(vulkan_device.clone()); render_world.insert_resource(vulkan_graphics_queue.clone()); render_world.insert_resource(vulkan_memory_allocator.clone()); diff --git a/crates/engine_render/src/window/mod.rs b/crates/engine_render/src/window/mod.rs index bcb3ea6..9af4184 100644 --- a/crates/engine_render/src/window/mod.rs +++ b/crates/engine_render/src/window/mod.rs @@ -1,13 +1,21 @@ use std::sync::Arc; use bevy_app::{App, Plugin}; -use bevy_ecs::resource::Resource; +use bevy_ecs::{ + resource::Resource, + schedule::IntoScheduleConfigs, + system::{Res, ResMut}, +}; +use engine_vulkan::{VulkanDevice, VulkanInstance}; +use engine_window::raw_handle::WindowWrapper; use vulkano::{ - image::view::ImageView, pipeline::graphics::viewport::Viewport, swapchain::Swapchain, - sync::GpuFuture, + image::{Image, ImageUsage, view::ImageView}, + pipeline::graphics::viewport::Viewport, + swapchain::{Surface, Swapchain, SwapchainCreateInfo}, + sync::{self, GpuFuture}, }; -use crate::RenderApp; +use super::{Render, RenderApp, RenderSystems}; pub struct WindowSurfaceData { pub swapchain: Arc, @@ -31,5 +39,186 @@ impl Plugin for WindowRenderPlugin { .expect("Failed to get RenderApp. Check is RenderPlugin is added."); render_app.init_resource::(); + + render_app.add_systems( + Render, + create_window_surface + .in_set(RenderSystems::ManageViews) + .run_if(need_create_window_surface) + .before(need_update_window_surface), + ); + + render_app.add_systems( + Render, + update_window_surface + .in_set(RenderSystems::ManageViews) + .run_if(need_update_window_surface), + ); } } + +fn need_create_window_surface(window_surface: Res) -> bool { + window_surface.surface.is_none() +} + +fn create_window_surface( + mut window_surface: ResMut, + window_handle: Res, + vulkan_instance: Res, + vulkan_device: Res, +) { + let window_size = window_handle.0.inner_size(); + + let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone()) + .expect("Failed to create surface"); + log::debug!("Surface created"); + + let (swapchain, images) = { + let surface_capabilities = vulkan_device + .0 + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + let (image_format, _) = vulkan_device + .0 + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + vulkan_device.0.clone(), + surface, + SwapchainCreateInfo { + // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; + log_swapchain_info(&swapchain, false); + + let attachment_image_views = window_size_dependent_setup(&images); + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + log_viewport_info(&viewport, false); + + let recreate_swapchain = false; + let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync()); + + window_surface.surface = Some(WindowSurfaceData { + swapchain, + attachment_image_views, + viewport, + recreate_swapchain, + previous_frame_end, + }); +} + +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +} + +fn need_update_window_surface(window_surface: Res) -> bool { + match &window_surface.surface { + Some(surface) => surface.recreate_swapchain, + None => false, + } +} + +fn update_window_surface( + mut window_surface: ResMut, + window_handle: Res, +) { + if window_surface.surface.is_none() { + return; + } + + let window_surface = window_surface.surface.as_mut().unwrap(); + + if !window_surface.recreate_swapchain { + return; + } + + let window_size = window_handle.0.inner_size(); + let (new_swapchain, new_images) = window_surface + .swapchain + .recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..window_surface.swapchain.create_info() + }) + .expect("Failed to recreate swapchain"); + + window_surface.swapchain = new_swapchain; + window_surface.attachment_image_views = window_size_dependent_setup(&new_images); + window_surface.viewport.extent = window_size.into(); + window_surface.recreate_swapchain = false; + + log_swapchain_info(&window_surface.swapchain, true); + log_viewport_info(&window_surface.viewport, true); +} + +fn log_swapchain_info(swapchain: &Swapchain, recreate_swapchain: bool) { + if recreate_swapchain { + log::debug!("Swapchain recreated"); + } else { + log::debug!("Swapchain created"); + } + log::debug!( + "\tMin image count: {}", + swapchain.create_info().min_image_count + ); + log::debug!("\tImage format: {:?}", swapchain.create_info().image_format); + log::debug!("\tImage extent: {:?}", swapchain.create_info().image_extent); + log::debug!("\tImage usage: {:?}", swapchain.create_info().image_usage); + log::debug!( + "\tComposite alpha: {:?}", + swapchain.create_info().composite_alpha + ); + log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode); + log::debug!( + "\tImage sharing: {:?}", + swapchain.create_info().image_sharing + ); + log::debug!( + "\tPre transform: {:?}", + swapchain.create_info().pre_transform + ); + log::debug!( + "\tComposite alpha: {:?}", + swapchain.create_info().composite_alpha + ); + log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode); + log::debug!( + "\tFull screen exclusive: {:?}", + swapchain.create_info().full_screen_exclusive + ); +} + +fn log_viewport_info(viewport: &Viewport, recreate_viewport: bool) { + if recreate_viewport { + log::debug!("Viewport recreated"); + } else { + log::debug!("Viewport created"); + } + log::debug!("\tOffset: {:?}", viewport.offset); + log::debug!("\tExtent: {:?}", viewport.extent); + log::debug!("\tDepth range: {:?}", viewport.depth_range); +} diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index 65e9349..4f5129b 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use bevy_ecs::resource::Resource; -use engine_window::raw_handle::WindowWrapper; use utils::{device::create_and_insert_device, instance::create_and_insert_instance}; use vulkano::{ command_buffer::allocator::StandardCommandBufferAllocator, @@ -10,12 +9,10 @@ use vulkano::{ instance::Instance, memory::allocator::StandardMemoryAllocator, }; -use window_render_context::WindowRenderContext; use bevy_app::{App, Plugin}; mod utils; -mod window_render_context; #[derive(Resource, Clone)] pub struct VulkanInstance(pub Arc); diff --git a/crates/engine_vulkan/src/window_render_context.rs b/crates/engine_vulkan/src/window_render_context.rs deleted file mode 100644 index 95b3d90..0000000 --- a/crates/engine_vulkan/src/window_render_context.rs +++ /dev/null @@ -1,125 +0,0 @@ -use bevy_app::App; -use bevy_ecs::resource::Resource; -use engine_window::raw_handle::WindowWrapper; -use std::sync::Arc; -use vulkano::image::view::ImageView; -use vulkano::image::{Image, ImageUsage}; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; -use vulkano::sync::{self, GpuFuture}; -use vulkano::{Validated, VulkanError}; -use winit::window::Window; - -use crate::{VulkanDevice, VulkanInstance}; - -#[derive(Resource)] -pub struct WindowRenderContext { - pub window: Arc, - pub swapchain: Arc, - pub attachment_image_views: Vec>, - pub viewport: Viewport, - pub recreate_swapchain: bool, - pub previous_frame_end: Option>, -} - -impl From<&App> for WindowRenderContext { - fn from(app: &App) -> Self { - let world = app.world(); - let window_handle = world - .get_resource::() - .expect("Failed to find window handle"); - let vulkan_instance = world - .get_resource::() - .expect("Failed to find vulkan instance"); - let vulkan_device = world - .get_resource::() - .expect("Failed to find vulkan device"); - - let window_size = window_handle.0.inner_size(); - - let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone()) - .expect("Failed to create surface"); - - let (swapchain, images) = { - let surface_capabilities = vulkan_device - .0 - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - - let (image_format, _) = vulkan_device - .0 - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - Swapchain::new( - vulkan_device.0.clone(), - surface, - SwapchainCreateInfo { - // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - - ..Default::default() - }, - ) - .unwrap() - }; - - let attachment_image_views = window_size_dependent_setup(&images); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let recreate_swapchain = false; - let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync()); - - Self { - window: window_handle.0.clone(), - swapchain, - attachment_image_views, - viewport, - recreate_swapchain, - previous_frame_end, - } - } -} - -impl WindowRenderContext { - pub fn update_swapchain(&mut self) -> Result<(), Validated> { - if !self.recreate_swapchain { - return Ok(()); - } - - let window_size = self.window.inner_size(); - let (new_swapchain, new_images) = self.swapchain.recreate(SwapchainCreateInfo { - image_extent: window_size.into(), - ..self.swapchain.create_info() - })?; - - self.swapchain = new_swapchain; - self.attachment_image_views = window_size_dependent_setup(&new_images); - self.viewport.extent = window_size.into(); - self.recreate_swapchain = false; - - Ok(()) - } -} - -fn window_size_dependent_setup(images: &[Arc]) -> Vec> { - images - .iter() - .map(|image| ImageView::new_default(image.clone()).unwrap()) - .collect::>() -} diff --git a/src/game/mod.rs b/src/game/mod.rs index 9c5b547..e04b2a7 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -33,13 +33,4 @@ pub fn init(app: &mut App) { VulkanPlugin { vulkan_config }, RenderPlugin, )); - - // app.get_sub_app_mut(RenderApp) - // .expect("Failed to get RenderApp. Check is RenderPlugin is added.") - // .add_systems(Render, test_system.in_set(RenderSystems::Prepare)); } - -// fn test_system(vulkan_device: Res, vulkan_graphics_queue: Res) { -// log::trace!("vulkan_device: {:?}", vulkan_device.0); -// log::trace!("vulkan_graphics_queue: {:?}", vulkan_graphics_queue.0); -// } From 7951b05ab3641e936faf0d02acd39cfdca48d1eb Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 19 May 2025 23:30:35 +0200 Subject: [PATCH 52/91] Add README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c74b5d4 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Project + +## Usefull links + +- https://vulkan-tutorial.com/fr/Introduction +- https://github.com/bwasty/vulkan-tutorial-rs From 18174e42e984de129b22c25473a268a7aea2b9ec Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 21 May 2025 13:42:36 +0200 Subject: [PATCH 53/91] render_vulkan: Fixes - Avoid create device for each physical device during support checking - Avoid to select different queue family index and add support of creating multiple queues on same queue family - Log select queue family index for each queue type --- crates/engine_vulkan/src/utils/device.rs | 115 ++++++++++++++------- crates/engine_vulkan/src/vulkan_context.rs | 76 -------------- 2 files changed, 77 insertions(+), 114 deletions(-) delete mode 100644 crates/engine_vulkan/src/vulkan_context.rs diff --git a/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/utils/device.rs index 6c4b49f..e44ac52 100644 --- a/crates/engine_vulkan/src/utils/device.rs +++ b/crates/engine_vulkan/src/utils/device.rs @@ -1,8 +1,9 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use bevy_ecs::world::World; use engine_window::raw_handle::DisplayHandleWrapper; use vulkano::{ + VulkanError, command_buffer::allocator::StandardCommandBufferAllocator, descriptor_set::allocator::StandardDescriptorSetAllocator, device::{ @@ -86,22 +87,30 @@ fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option() .expect("Failed to get VulkanInstance during vulkan plugin initialization"); - instance + let result = instance .0 .enumerate_physical_devices() .expect("Failed to enumerate physical devices") - .filter_map(|p| check_physical_device(world, &p, config)) - .min_by_key( - |p| match p.device.physical_device().properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }, - ) - .take() + .filter_map(|p| check_physical_device_support(world, &p, config).and_then(|r| Some((p, r)))) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .take(); + + match result { + Some((p, (device_extensions, picked_queues_info))) => Some(create_device( + config, + &p, + device_extensions, + &picked_queues_info, + )), + None => None, + } } fn check_device_extensions_support( @@ -147,7 +156,7 @@ fn check_queues_support( let mut transfer_queue_family_index: Option = None; for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { - if config.with_graphics_queue { + if config.with_graphics_queue && graphics_queue_family_index.is_none() { let graphics_supported = queue_family_property .queue_flags .intersects(QueueFlags::GRAPHICS); @@ -169,7 +178,7 @@ fn check_queues_support( } } - if config.with_compute_queue { + if config.with_compute_queue && compute_queue_family_index.is_none() { let compute_supported = queue_family_property .queue_flags .intersects(QueueFlags::COMPUTE); @@ -179,7 +188,7 @@ fn check_queues_support( } } - if config.with_transfer_queue { + if config.with_transfer_queue && transfer_queue_family_index.is_none() { let transfer_supported = queue_family_property .queue_flags .intersects(QueueFlags::TRANSFER); @@ -193,7 +202,10 @@ fn check_queues_support( if !config.with_graphics_queue { log::debug!("\t\t[SKIPPED] Graphics queue is not required"); } else if graphics_queue_family_index.is_some() { - log::debug!("\t\t[OK] Graphics queue is supported"); + log::debug!( + "\t\t[OK] Graphics queue is supported (family index: {:?})", + graphics_queue_family_index + ); } else { log::debug!("\t\t[FAILED] Graphics queue is not supported"); return None; @@ -202,7 +214,10 @@ fn check_queues_support( if !config.with_compute_queue { log::debug!("\t\t[SKIPPED] Compute queue is not required"); } else if compute_queue_family_index.is_some() { - log::debug!("\t\t[OK] Compute queue is supported"); + log::debug!( + "\t\t[OK] Compute queue is supported (family index: {:?})", + compute_queue_family_index + ); } else { log::debug!("\t\t[FAILED] Compute queue is not supported"); return None; @@ -211,7 +226,10 @@ fn check_queues_support( if !config.with_transfer_queue { log::debug!("\t\t[SKIPPED] Transfer queue is not required"); } else if transfer_queue_family_index.is_some() { - log::debug!("\t\t[OK] Transfer queue is supported"); + log::debug!( + "\t\t[OK] Transfer queue is supported (family index: {:?})", + transfer_queue_family_index + ); } else { log::debug!("\t\t[FAILED] Transfer queue is not supported"); return None; @@ -224,11 +242,11 @@ fn check_queues_support( }) } -fn check_physical_device( +fn check_physical_device_support( world: &World, physical_device: &Arc, config: &VulkanConfig, -) -> Option { +) -> Option<(DeviceExtensions, PickedQueuesInfo)> { log::debug!("Checking physical device"); log::debug!("\tProperties"); log::debug!("\t\tName: {}", physical_device.properties().device_name); @@ -242,33 +260,54 @@ fn check_physical_device( let device_extensions = check_device_extensions_support(physical_device, config)?; let picked_queues_info = check_queues_support(world, physical_device, config)?; - let mut queue_create_infos = vec![]; + Some((device_extensions, picked_queues_info)) +} + +fn create_device( + config: &VulkanConfig, + physical_device: &Arc, + device_extensions: DeviceExtensions, + picked_queues_info: &PickedQueuesInfo, +) -> PickedDevice { + let mut queue_create_infos = HashMap::::new(); if config.with_graphics_queue { - queue_create_infos.push(QueueCreateInfo { - queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), - ..Default::default() - }); + let entry = queue_create_infos + .entry(picked_queues_info.graphics_queue_family_index.unwrap()) + .or_insert(QueueCreateInfo { + queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), + ..Default::default() + }); + + entry.queues.push(1.0); } if config.with_compute_queue { - queue_create_infos.push(QueueCreateInfo { - queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), - ..Default::default() - }); + let entry = queue_create_infos + .entry(picked_queues_info.compute_queue_family_index.unwrap()) + .or_insert(QueueCreateInfo { + queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), + ..Default::default() + }); + + entry.queues.push(1.0); } if config.with_transfer_queue { - queue_create_infos.push(QueueCreateInfo { - queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), - ..Default::default() - }); + let entry = queue_create_infos + .entry(picked_queues_info.transfer_queue_family_index.unwrap()) + .or_insert(QueueCreateInfo { + queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), + ..Default::default() + }); + + entry.queues.push(1.0); } let (device, mut queues) = Device::new( physical_device.clone(), DeviceCreateInfo { - queue_create_infos, + queue_create_infos: queue_create_infos.values().cloned().collect(), enabled_extensions: device_extensions, enabled_features: config.device_features, ..Default::default() @@ -292,10 +331,10 @@ fn check_physical_device( transfer_queue = queues.next(); } - Some(PickedDevice { + PickedDevice { device, graphics_queue, compute_queue, transfer_queue, - }) + } } diff --git a/crates/engine_vulkan/src/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs deleted file mode 100644 index 08ff1ac..0000000 --- a/crates/engine_vulkan/src/vulkan_context.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::{any::Any, sync::Arc}; - -use bevy_app::App; -use bevy_ecs::resource::Resource; -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::{ - command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, - allocator::StandardCommandBufferAllocator, - }, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{Device, Queue}, - instance::Instance, - memory::allocator::StandardMemoryAllocator, - swapchain::Surface, -}; -use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; - -use super::utils; - -#[derive(Resource)] -pub struct VulkanContext { - pub instance: Arc, - pub device: Arc, - pub graphics_queue: Arc, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, -} - -impl VulkanContext { - pub fn create_surface( - &self, - window: Arc, - ) -> Arc { - Surface::from_window(self.instance.clone(), window).unwrap() - } - - pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { - AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap() - } -} - -impl From<&App> for VulkanContext { - fn from(app: &App) -> Self { - let (device, mut queues) = utils::pick_graphics_device(&instance, &display_handle.0); - let graphics_queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - - Self { - instance: instance.clone(), - device, - graphics_queue, - memory_allocator, - command_buffer_allocator, - descriptor_set_allocator, - } - } -} From 9ea8721346c1060f95b5fd7515a814a97221b0ac Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 21 May 2025 13:46:13 +0200 Subject: [PATCH 54/91] render_vulkan: Avoid continue loop if all queues is found --- crates/engine_vulkan/src/utils/device.rs | 30 ++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/utils/device.rs index e44ac52..38bf5fd 100644 --- a/crates/engine_vulkan/src/utils/device.rs +++ b/crates/engine_vulkan/src/utils/device.rs @@ -87,7 +87,7 @@ fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option() .expect("Failed to get VulkanInstance during vulkan plugin initialization"); - let result = instance + instance .0 .enumerate_physical_devices() .expect("Failed to enumerate physical devices") @@ -100,17 +100,15 @@ fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option 4, _ => 5, }) - .take(); - - match result { - Some((p, (device_extensions, picked_queues_info))) => Some(create_device( - config, - &p, - device_extensions, - &picked_queues_info, - )), - None => None, - } + .take() + .and_then(|(p, (device_extensions, picked_queues_info))| { + Some(create_device( + config, + &p, + device_extensions, + &picked_queues_info, + )) + }) } fn check_device_extensions_support( @@ -197,6 +195,14 @@ fn check_queues_support( transfer_queue_family_index = Some(i as u32); } } + + if (!config.with_graphics_queue || graphics_queue_family_index.is_some()) + && (!config.with_compute_queue || compute_queue_family_index.is_some()) + && (!config.with_transfer_queue || transfer_queue_family_index.is_some()) + { + // We found all required queues, no need to continue iterating + break; + } } if !config.with_graphics_queue { From 4676b1b5b8db38ab2b3cddfca20ad96fad034eb2 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 23 May 2025 14:10:51 +0200 Subject: [PATCH 55/91] engine_vulkan: Refactor queue finding --- .../engine_vulkan/src/{utils => }/device.rs | 178 +++--------------- .../engine_vulkan/src/{utils => }/instance.rs | 0 crates/engine_vulkan/src/lib.rs | 8 +- crates/engine_vulkan/src/queues.rs | 174 +++++++++++++++++ crates/engine_vulkan/src/utils/mod.rs | 2 - 5 files changed, 202 insertions(+), 160 deletions(-) rename crates/engine_vulkan/src/{utils => }/device.rs (51%) rename crates/engine_vulkan/src/{utils => }/instance.rs (100%) create mode 100644 crates/engine_vulkan/src/queues.rs delete mode 100644 crates/engine_vulkan/src/utils/mod.rs diff --git a/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/device.rs similarity index 51% rename from crates/engine_vulkan/src/utils/device.rs rename to crates/engine_vulkan/src/device.rs index 38bf5fd..f20335f 100644 --- a/crates/engine_vulkan/src/utils/device.rs +++ b/crates/engine_vulkan/src/device.rs @@ -1,13 +1,12 @@ -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; use bevy_ecs::world::World; use engine_window::raw_handle::DisplayHandleWrapper; use vulkano::{ - VulkanError, command_buffer::allocator::StandardCommandBufferAllocator, descriptor_set::allocator::StandardDescriptorSetAllocator, device::{ - Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags, + Device, DeviceCreateInfo, DeviceExtensions, Queue, physical::{PhysicalDevice, PhysicalDeviceType}, }, memory::allocator::StandardMemoryAllocator, @@ -16,6 +15,7 @@ use vulkano::{ use crate::{ VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanConfig, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator, VulkanTransferQueue, + queues::{VulkanQueueFamilyIndices, VulkanQueuesQuery, find_queues_family_indices}, }; pub fn create_and_insert_device(world: &mut World, config: &VulkanConfig) { @@ -101,12 +101,12 @@ fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option 5, }) .take() - .and_then(|(p, (device_extensions, picked_queues_info))| { + .and_then(|(p, (device_extensions, queue_family_indices))| { Some(create_device( config, &p, device_extensions, - &picked_queues_info, + queue_family_indices, )) }) } @@ -138,121 +138,11 @@ fn check_device_extensions_support( } } -struct PickedQueuesInfo { - graphics_queue_family_index: Option, - compute_queue_family_index: Option, - transfer_queue_family_index: Option, -} - -fn check_queues_support( - world: &World, - physical_device: &Arc, - config: &VulkanConfig, -) -> Option { - let mut graphics_queue_family_index: Option = None; - let mut compute_queue_family_index: Option = None; - let mut transfer_queue_family_index: Option = None; - - for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { - if config.with_graphics_queue && graphics_queue_family_index.is_none() { - let graphics_supported = queue_family_property - .queue_flags - .intersects(QueueFlags::GRAPHICS); - - let presentation_valid = if config.with_window_surface { - let display_handle = world - .get_resource::() - .expect("DisplayHandleWrapper must be added before VulkanPlugin"); - - physical_device - .presentation_support(i as u32, &display_handle.0) - .expect("Failed to check presentation support") - } else { - true - }; - - if graphics_supported && presentation_valid { - graphics_queue_family_index = Some(i as u32); - } - } - - if config.with_compute_queue && compute_queue_family_index.is_none() { - let compute_supported = queue_family_property - .queue_flags - .intersects(QueueFlags::COMPUTE); - - if compute_supported { - compute_queue_family_index = Some(i as u32); - } - } - - if config.with_transfer_queue && transfer_queue_family_index.is_none() { - let transfer_supported = queue_family_property - .queue_flags - .intersects(QueueFlags::TRANSFER); - - if transfer_supported { - transfer_queue_family_index = Some(i as u32); - } - } - - if (!config.with_graphics_queue || graphics_queue_family_index.is_some()) - && (!config.with_compute_queue || compute_queue_family_index.is_some()) - && (!config.with_transfer_queue || transfer_queue_family_index.is_some()) - { - // We found all required queues, no need to continue iterating - break; - } - } - - if !config.with_graphics_queue { - log::debug!("\t\t[SKIPPED] Graphics queue is not required"); - } else if graphics_queue_family_index.is_some() { - log::debug!( - "\t\t[OK] Graphics queue is supported (family index: {:?})", - graphics_queue_family_index - ); - } else { - log::debug!("\t\t[FAILED] Graphics queue is not supported"); - return None; - } - - if !config.with_compute_queue { - log::debug!("\t\t[SKIPPED] Compute queue is not required"); - } else if compute_queue_family_index.is_some() { - log::debug!( - "\t\t[OK] Compute queue is supported (family index: {:?})", - compute_queue_family_index - ); - } else { - log::debug!("\t\t[FAILED] Compute queue is not supported"); - return None; - } - - if !config.with_transfer_queue { - log::debug!("\t\t[SKIPPED] Transfer queue is not required"); - } else if transfer_queue_family_index.is_some() { - log::debug!( - "\t\t[OK] Transfer queue is supported (family index: {:?})", - transfer_queue_family_index - ); - } else { - log::debug!("\t\t[FAILED] Transfer queue is not supported"); - return None; - } - - Some(PickedQueuesInfo { - graphics_queue_family_index, - compute_queue_family_index, - transfer_queue_family_index, - }) -} - fn check_physical_device_support( world: &World, physical_device: &Arc, config: &VulkanConfig, -) -> Option<(DeviceExtensions, PickedQueuesInfo)> { +) -> Option<(DeviceExtensions, VulkanQueueFamilyIndices)> { log::debug!("Checking physical device"); log::debug!("\tProperties"); log::debug!("\t\tName: {}", physical_device.properties().device_name); @@ -264,56 +154,34 @@ fn check_physical_device_support( log::debug!("\tRequired supports checking report"); let device_extensions = check_device_extensions_support(physical_device, config)?; - let picked_queues_info = check_queues_support(world, physical_device, config)?; - Some((device_extensions, picked_queues_info)) + let display_handle = world + .get_resource::() + .expect("DisplayHandleWrapper must be added before VulkanPlugin"); + + let queue_support_query = VulkanQueuesQuery { + with_surface: Some(&display_handle), + with_graphics_queue: config.with_graphics_queue, + with_compute_queue: config.with_compute_queue, + with_transfer_queue: config.with_transfer_queue, + }; + + let queue_family_indices = find_queues_family_indices(physical_device, &queue_support_query); + log::debug!("\t\tQueue family indices: {:#?}", queue_family_indices); + + Some((device_extensions, queue_family_indices)) } fn create_device( config: &VulkanConfig, physical_device: &Arc, device_extensions: DeviceExtensions, - picked_queues_info: &PickedQueuesInfo, + queue_family_indices: VulkanQueueFamilyIndices, ) -> PickedDevice { - let mut queue_create_infos = HashMap::::new(); - - if config.with_graphics_queue { - let entry = queue_create_infos - .entry(picked_queues_info.graphics_queue_family_index.unwrap()) - .or_insert(QueueCreateInfo { - queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), - ..Default::default() - }); - - entry.queues.push(1.0); - } - - if config.with_compute_queue { - let entry = queue_create_infos - .entry(picked_queues_info.compute_queue_family_index.unwrap()) - .or_insert(QueueCreateInfo { - queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), - ..Default::default() - }); - - entry.queues.push(1.0); - } - - if config.with_transfer_queue { - let entry = queue_create_infos - .entry(picked_queues_info.transfer_queue_family_index.unwrap()) - .or_insert(QueueCreateInfo { - queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), - ..Default::default() - }); - - entry.queues.push(1.0); - } - let (device, mut queues) = Device::new( physical_device.clone(), DeviceCreateInfo { - queue_create_infos: queue_create_infos.values().cloned().collect(), + queue_create_infos: queue_family_indices.into(), enabled_extensions: device_extensions, enabled_features: config.device_features, ..Default::default() diff --git a/crates/engine_vulkan/src/utils/instance.rs b/crates/engine_vulkan/src/instance.rs similarity index 100% rename from crates/engine_vulkan/src/utils/instance.rs rename to crates/engine_vulkan/src/instance.rs diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index 4f5129b..38cb456 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,7 +1,7 @@ use std::sync::Arc; +use bevy_app::{App, Plugin}; use bevy_ecs::resource::Resource; -use utils::{device::create_and_insert_device, instance::create_and_insert_instance}; use vulkano::{ command_buffer::allocator::StandardCommandBufferAllocator, descriptor_set::allocator::StandardDescriptorSetAllocator, @@ -10,9 +10,11 @@ use vulkano::{ memory::allocator::StandardMemoryAllocator, }; -use bevy_app::{App, Plugin}; +mod device; +mod instance; +mod queues; -mod utils; +use crate::{device::create_and_insert_device, instance::create_and_insert_instance}; #[derive(Resource, Clone)] pub struct VulkanInstance(pub Arc); diff --git a/crates/engine_vulkan/src/queues.rs b/crates/engine_vulkan/src/queues.rs new file mode 100644 index 0000000..fc49dcc --- /dev/null +++ b/crates/engine_vulkan/src/queues.rs @@ -0,0 +1,174 @@ +use std::{collections::HashMap, sync::Arc}; + +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::device::{ + QueueCreateInfo, QueueFamilyProperties, QueueFlags, physical::PhysicalDevice, +}; + +#[derive(Debug)] +pub enum VulkanQueueFamilyStatus { + Unused, + NotSupported, + Supported(u32), +} + +impl VulkanQueueFamilyStatus { + pub fn can_be_set(&self) -> bool { + match self { + VulkanQueueFamilyStatus::NotSupported => true, + _ => false, + } + } +} + +/// Convert a boolean to a VulkanQueueFamilyStatus as default value +impl From for VulkanQueueFamilyStatus { + fn from(value: bool) -> Self { + if value { + VulkanQueueFamilyStatus::NotSupported + } else { + VulkanQueueFamilyStatus::Unused + } + } +} + +/// Convert a DisplayHandleWrapper Option to a VulkanQueueFamilyStatus as default value +impl From> for VulkanQueueFamilyStatus { + fn from(value: Option<&DisplayHandleWrapper>) -> Self { + if value.is_some() { + VulkanQueueFamilyStatus::NotSupported + } else { + VulkanQueueFamilyStatus::Unused + } + } +} + +#[derive(Debug)] +pub struct VulkanQueueFamilyIndices { + pub present_queue_family_index: VulkanQueueFamilyStatus, + pub graphics_queue_family_index: VulkanQueueFamilyStatus, + pub compute_queue_family_index: VulkanQueueFamilyStatus, + pub transfer_queue_family_index: VulkanQueueFamilyStatus, +} + +impl From for Vec { + fn from(indices: VulkanQueueFamilyIndices) -> Self { + vec![ + indices.present_queue_family_index, + indices.graphics_queue_family_index, + indices.compute_queue_family_index, + indices.transfer_queue_family_index, + ] + } +} + +impl From for Vec { + fn from(indices: VulkanQueueFamilyIndices) -> Self { + let mut queue_create_infos = HashMap::::new(); + + let statuses: Vec = indices.into(); + + for status in statuses.iter() { + match status { + VulkanQueueFamilyStatus::Supported(index) => { + let entry = queue_create_infos.entry(*index).or_insert(QueueCreateInfo { + queue_family_index: *index, + ..Default::default() + }); + + entry.queues.push(1.0); + } + _ => {} + } + } + + queue_create_infos + .into_iter() + .map(|(_, value)| value) + .collect() + } +} + +/// Convert a VulkanQueuesQuery to a VulkanQueuesFamilyIndices as default value +impl<'a> From<&'a VulkanQueuesQuery<'a>> for VulkanQueueFamilyIndices { + fn from(query: &'a VulkanQueuesQuery<'a>) -> Self { + VulkanQueueFamilyIndices { + present_queue_family_index: query.with_surface.into(), + graphics_queue_family_index: query.with_graphics_queue.into(), + compute_queue_family_index: query.with_compute_queue.into(), + transfer_queue_family_index: query.with_transfer_queue.into(), + } + } +} + +pub struct VulkanQueuesQuery<'a> { + pub with_surface: Option<&'a DisplayHandleWrapper>, + pub with_graphics_queue: bool, + pub with_compute_queue: bool, + pub with_transfer_queue: bool, +} + +pub fn find_queues_family_indices( + physical_device: &Arc, + query: &VulkanQueuesQuery, +) -> VulkanQueueFamilyIndices { + let mut indices: VulkanQueueFamilyIndices = query.into(); + + for (i, queue_family_properties) in physical_device.queue_family_properties().iter().enumerate() + { + if indices.present_queue_family_index.can_be_set() + && check_presentation_support( + physical_device, + i as u32, + &query.with_surface.as_ref().unwrap(), + ) + { + indices.present_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); + } + + if indices.graphics_queue_family_index.can_be_set() + && check_queue_support(queue_family_properties, QueueFlags::GRAPHICS) + { + indices.graphics_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); + } + + if indices.compute_queue_family_index.can_be_set() + && check_queue_support(queue_family_properties, QueueFlags::COMPUTE) + { + indices.compute_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); + } + + if indices.transfer_queue_family_index.can_be_set() + && check_queue_support(queue_family_properties, QueueFlags::TRANSFER) + { + indices.transfer_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); + } + + if !indices.present_queue_family_index.can_be_set() + && !indices.graphics_queue_family_index.can_be_set() + && !indices.compute_queue_family_index.can_be_set() + && !indices.transfer_queue_family_index.can_be_set() + { + break; + } + } + + return indices; +} + +fn check_presentation_support( + physical_device: &Arc, + queue_family_index: u32, + surface: &DisplayHandleWrapper, +) -> bool { + physical_device + .presentation_support(queue_family_index, &surface.0) + .unwrap() +} + +fn check_queue_support( + queue_family_property: &QueueFamilyProperties, + queue_flags: QueueFlags, +) -> bool { + queue_family_property.queue_flags.intersects(queue_flags) +} diff --git a/crates/engine_vulkan/src/utils/mod.rs b/crates/engine_vulkan/src/utils/mod.rs deleted file mode 100644 index 09e4b35..0000000 --- a/crates/engine_vulkan/src/utils/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod device; -pub mod instance; From d10212ac3bf492eae34ee79861c02eb9996f15d2 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sat, 24 May 2025 16:44:51 +0200 Subject: [PATCH 56/91] engine_vulkan: Fixes of queue allocation - Avoid to allocate dedicated queue for presentation and use graphics queue for it - Use queueCount of Queue Family Properties for queue support checking --- crates/engine_vulkan/src/queues.rs | 31 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/crates/engine_vulkan/src/queues.rs b/crates/engine_vulkan/src/queues.rs index fc49dcc..6b94f76 100644 --- a/crates/engine_vulkan/src/queues.rs +++ b/crates/engine_vulkan/src/queues.rs @@ -45,7 +45,6 @@ impl From> for VulkanQueueFamilyStatus { #[derive(Debug)] pub struct VulkanQueueFamilyIndices { - pub present_queue_family_index: VulkanQueueFamilyStatus, pub graphics_queue_family_index: VulkanQueueFamilyStatus, pub compute_queue_family_index: VulkanQueueFamilyStatus, pub transfer_queue_family_index: VulkanQueueFamilyStatus, @@ -54,7 +53,6 @@ pub struct VulkanQueueFamilyIndices { impl From for Vec { fn from(indices: VulkanQueueFamilyIndices) -> Self { vec![ - indices.present_queue_family_index, indices.graphics_queue_family_index, indices.compute_queue_family_index, indices.transfer_queue_family_index, @@ -93,7 +91,6 @@ impl From for Vec { impl<'a> From<&'a VulkanQueuesQuery<'a>> for VulkanQueueFamilyIndices { fn from(query: &'a VulkanQueuesQuery<'a>) -> Self { VulkanQueueFamilyIndices { - present_queue_family_index: query.with_surface.into(), graphics_queue_family_index: query.with_graphics_queue.into(), compute_queue_family_index: query.with_compute_queue.into(), transfer_queue_family_index: query.with_transfer_queue.into(), @@ -116,36 +113,40 @@ pub fn find_queues_family_indices( for (i, queue_family_properties) in physical_device.queue_family_properties().iter().enumerate() { - if indices.present_queue_family_index.can_be_set() - && check_presentation_support( - physical_device, - i as u32, - &query.with_surface.as_ref().unwrap(), - ) - { - indices.present_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); - } + let mut available_queue_count = queue_family_properties.queue_count; if indices.graphics_queue_family_index.can_be_set() && check_queue_support(queue_family_properties, QueueFlags::GRAPHICS) + && available_queue_count > 0 { - indices.graphics_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); + if query.with_surface.is_none() + || check_presentation_support( + physical_device, + i as u32, + &query.with_surface.as_ref().unwrap(), + ) + { + indices.graphics_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); + available_queue_count -= 1; + } } if indices.compute_queue_family_index.can_be_set() && check_queue_support(queue_family_properties, QueueFlags::COMPUTE) + && available_queue_count > 0 { indices.compute_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); + available_queue_count -= 1; } if indices.transfer_queue_family_index.can_be_set() && check_queue_support(queue_family_properties, QueueFlags::TRANSFER) + && available_queue_count > 0 { indices.transfer_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); } - if !indices.present_queue_family_index.can_be_set() - && !indices.graphics_queue_family_index.can_be_set() + if !indices.graphics_queue_family_index.can_be_set() && !indices.compute_queue_family_index.can_be_set() && !indices.transfer_queue_family_index.can_be_set() { From 15565d03c1616221b850163efaa5ee7cec697a1a Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sat, 24 May 2025 17:02:01 +0200 Subject: [PATCH 57/91] engine_vulkan: Fix allocate more queue as available and add different priorities --- crates/engine_vulkan/src/queues.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/engine_vulkan/src/queues.rs b/crates/engine_vulkan/src/queues.rs index 6b94f76..17e6c08 100644 --- a/crates/engine_vulkan/src/queues.rs +++ b/crates/engine_vulkan/src/queues.rs @@ -50,12 +50,12 @@ pub struct VulkanQueueFamilyIndices { pub transfer_queue_family_index: VulkanQueueFamilyStatus, } -impl From for Vec { +impl From for Vec<(VulkanQueueFamilyStatus, f32)> { fn from(indices: VulkanQueueFamilyIndices) -> Self { vec![ - indices.graphics_queue_family_index, - indices.compute_queue_family_index, - indices.transfer_queue_family_index, + (indices.graphics_queue_family_index, 1.0), + (indices.compute_queue_family_index, 0.5), + (indices.transfer_queue_family_index, 0.5), ] } } @@ -64,17 +64,18 @@ impl From for Vec { fn from(indices: VulkanQueueFamilyIndices) -> Self { let mut queue_create_infos = HashMap::::new(); - let statuses: Vec = indices.into(); + let statuses: Vec<(VulkanQueueFamilyStatus, f32)> = indices.into(); - for status in statuses.iter() { + for (status, priority) in statuses.iter() { match status { VulkanQueueFamilyStatus::Supported(index) => { let entry = queue_create_infos.entry(*index).or_insert(QueueCreateInfo { queue_family_index: *index, + queues: Vec::new(), ..Default::default() }); - entry.queues.push(1.0); + entry.queues.push(*priority); } _ => {} } From dbe415d2623388faa1cf91d220e391e75b0c8994 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sat, 24 May 2025 18:04:56 +0200 Subject: [PATCH 58/91] engine_render: Add first render --- crates/engine_render/src/lib.rs | 7 ++ crates/engine_render/src/render.rs | 122 +++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 crates/engine_render/src/render.rs diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs index ccbc39d..8c869e9 100644 --- a/crates/engine_render/src/lib.rs +++ b/crates/engine_render/src/lib.rs @@ -11,6 +11,7 @@ use engine_vulkan::{ use engine_window::raw_handle::WindowWrapper; use window::WindowRenderPlugin; +pub mod render; pub mod window; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] @@ -65,6 +66,12 @@ impl Plugin for RenderPlugin { let mut render_app = SubApp::new(); render_app.update_schedule = Some(Render.intern()); render_app.add_schedule(Render::base_schedule()); + render_app.add_systems( + Render, + render::render_system + .in_set(RenderSystems::Render) + .run_if(render::can_render), + ); extract_app_resources(app.world_mut(), render_app.world_mut()); diff --git a/crates/engine_render/src/render.rs b/crates/engine_render/src/render.rs new file mode 100644 index 0000000..1a8c95e --- /dev/null +++ b/crates/engine_render/src/render.rs @@ -0,0 +1,122 @@ +use bevy_ecs::system::{Res, ResMut}; +use engine_vulkan::{VulkanCommandBufferAllocator, VulkanDevice, VulkanGraphicsQueue}; +use vulkano::{ + Validated, VulkanError, + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, + }, + render_pass::{AttachmentLoadOp, AttachmentStoreOp}, + swapchain::{SwapchainAcquireFuture, SwapchainPresentInfo, acquire_next_image}, + sync::{self, GpuFuture}, +}; + +use crate::window::{WindowSurface, WindowSurfaceData}; + +pub fn can_render(window_surface: Res) -> bool { + window_surface.surface.is_some() +} + +pub fn render_system( + mut window_surface: ResMut, + command_buffer_allocator: Res, + graphics_queue: Res, + device: Res, +) { + { + let surface = window_surface.surface.as_ref().unwrap(); + if surface.viewport.extent[0] == 0.0 || surface.viewport.extent[1] == 0.0 { + return; + } + } + + let (image_index, acquire_future) = + match acquire_image(&mut window_surface.surface.as_mut().unwrap()) { + Some(r) => r, + None => return, + }; + + let mut builder = AutoCommandBufferBuilder::primary( + command_buffer_allocator.0.clone(), + graphics_queue.0.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .expect("failed to create command buffer builder"); + + { + let surface = window_surface.surface.as_ref().unwrap(); + builder + .begin_rendering(RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 0.0].into()), + ..RenderingAttachmentInfo::image_view( + surface.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + .set_viewport(0, [surface.viewport.clone()].into_iter().collect()) + .unwrap(); + } + + builder.end_rendering().unwrap(); + + let command_buffer = builder.build().unwrap(); + + { + let surface = window_surface.surface.as_mut().unwrap(); + + let future = surface + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(graphics_queue.0.clone(), command_buffer) + .unwrap() + .then_swapchain_present( + graphics_queue.0.clone(), + SwapchainPresentInfo::swapchain_image_index(surface.swapchain.clone(), image_index), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + surface.previous_frame_end = Some(future.boxed_send_sync()); + } + Err(VulkanError::OutOfDate) => { + surface.recreate_swapchain = true; + surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync()); + } + Err(e) => { + println!("failed to flush future: {e}"); + surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync()); + } + } + } +} + +fn acquire_image(surface: &mut WindowSurfaceData) -> Option<(u32, SwapchainAcquireFuture)> { + surface + .previous_frame_end + .as_mut() + .unwrap() + .cleanup_finished(); + + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(surface.swapchain.clone(), None).map_err(Validated::unwrap) { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + surface.recreate_swapchain = true; + return None; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + surface.recreate_swapchain = true; + } + + Some((image_index, acquire_future)) +} From d232706f6830be5685435feb8126f08aeb7b2c93 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 25 May 2025 18:05:20 +0200 Subject: [PATCH 59/91] engine_render: Resize swapchain --- crates/engine_render/src/window/mod.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/crates/engine_render/src/window/mod.rs b/crates/engine_render/src/window/mod.rs index 9af4184..d019444 100644 --- a/crates/engine_render/src/window/mod.rs +++ b/crates/engine_render/src/window/mod.rs @@ -136,9 +136,15 @@ fn window_size_dependent_setup(images: &[Arc]) -> Vec> { .collect::>() } -fn need_update_window_surface(window_surface: Res) -> bool { +fn need_update_window_surface( + window_surface: Res, + window_handle: Res, +) -> bool { match &window_surface.surface { - Some(surface) => surface.recreate_swapchain, + Some(surface) => { + let window_size: [f32; 2] = window_handle.0.inner_size().into(); + surface.recreate_swapchain || surface.viewport.extent != window_size + } None => false, } } @@ -147,16 +153,8 @@ fn update_window_surface( mut window_surface: ResMut, window_handle: Res, ) { - if window_surface.surface.is_none() { - return; - } - let window_surface = window_surface.surface.as_mut().unwrap(); - if !window_surface.recreate_swapchain { - return; - } - let window_size = window_handle.0.inner_size(); let (new_swapchain, new_images) = window_surface .swapchain From f486486be37a6d4f0ac6eea655373a0d45368fbf Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 25 May 2025 18:55:58 +0200 Subject: [PATCH 60/91] Remove ECS pattern: Split into new repo --- Cargo.lock | 489 +----------------- Cargo.toml | 27 +- crates/engine_render/Cargo.toml | 12 - crates/engine_render/src/lib.rs | 120 ----- crates/engine_render/src/render.rs | 122 ----- crates/engine_render/src/window/mod.rs | 222 -------- crates/engine_vulkan/Cargo.toml | 14 - crates/engine_vulkan/src/device.rs | 214 -------- crates/engine_vulkan/src/instance.rs | 70 --- crates/engine_vulkan/src/lib.rs | 85 --- crates/engine_vulkan/src/queues.rs | 176 ------- crates/engine_window/Cargo.toml | 11 - crates/engine_window/src/config.rs | 17 - crates/engine_window/src/lib.rs | 56 -- crates/engine_window/src/raw_handle.rs | 23 - crates/engine_window/src/state.rs | 63 --- flake.nix | 2 +- src/core/camera/mod.rs | 19 - src/core/mod.rs | 2 - src/core/render/material.rs | 7 - src/core/render/mesh.rs | 14 - src/core/render/mod.rs | 3 - src/game/mod.rs | 36 -- src/main.rs | 27 +- src/{old_app => render}/app.rs | 6 +- src/{old_app => render}/mod.rs | 1 + src/{old_app => render}/pipelines/mod.rs | 0 .../pipelines/triangle_pipeline.rs | 2 +- src/{old_app => render}/scene.rs | 6 +- src/{core => }/render/vertex.rs | 0 src/{old_app => render}/vulkan_context.rs | 0 .../window_render_context.rs | 0 32 files changed, 23 insertions(+), 1823 deletions(-) delete mode 100644 crates/engine_render/Cargo.toml delete mode 100644 crates/engine_render/src/lib.rs delete mode 100644 crates/engine_render/src/render.rs delete mode 100644 crates/engine_render/src/window/mod.rs delete mode 100644 crates/engine_vulkan/Cargo.toml delete mode 100644 crates/engine_vulkan/src/device.rs delete mode 100644 crates/engine_vulkan/src/instance.rs delete mode 100644 crates/engine_vulkan/src/lib.rs delete mode 100644 crates/engine_vulkan/src/queues.rs delete mode 100644 crates/engine_window/Cargo.toml delete mode 100644 crates/engine_window/src/config.rs delete mode 100644 crates/engine_window/src/lib.rs delete mode 100644 crates/engine_window/src/raw_handle.rs delete mode 100644 crates/engine_window/src/state.rs delete mode 100644 src/core/camera/mod.rs delete mode 100644 src/core/mod.rs delete mode 100644 src/core/render/material.rs delete mode 100644 src/core/render/mesh.rs delete mode 100644 src/core/render/mod.rs delete mode 100644 src/game/mod.rs rename src/{old_app => render}/app.rs (97%) rename src/{old_app => render}/mod.rs (86%) rename src/{old_app => render}/pipelines/mod.rs (100%) rename src/{old_app => render}/pipelines/triangle_pipeline.rs (98%) rename src/{old_app => render}/scene.rs (96%) rename src/{core => }/render/vertex.rs (100%) rename src/{old_app => render}/vulkan_context.rs (100%) rename src/{old_app => render}/window_render_context.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index e538714..1f58cce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "arrayref" version = "0.3.9" @@ -144,48 +150,11 @@ dependencies = [ "libloading", ] -[[package]] -name = "assert_type_match" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-executor" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" -dependencies = [ - "portable-atomic", -] - [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -dependencies = [ - "portable-atomic", -] [[package]] name = "autocfg" @@ -193,178 +162,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "bevy_app" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b6267ac23a9947d5b2725ff047a1e1add70076d85fa9fb73d044ab9bea1f3c" -dependencies = [ - "bevy_derive", - "bevy_ecs", - "bevy_platform", - "bevy_reflect", - "bevy_tasks", - "bevy_utils", - "cfg-if", - "ctrlc", - "downcast-rs 2.0.1", - "log", - "thiserror 2.0.12", - "variadics_please", -] - -[[package]] -name = "bevy_derive" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f626531b9c05c25a758ede228727bd11c2c2c8498ecbed9925044386d525a2a3" -dependencies = [ - "bevy_macro_utils", - "quote", - "syn", -] - -[[package]] -name = "bevy_ecs" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9e807b5d9aab3bb8dfe47e7a44c9ff088bad2ceefe299b80ac77609a87fe9d4" -dependencies = [ - "arrayvec", - "bevy_ecs_macros", - "bevy_platform", - "bevy_ptr", - "bevy_reflect", - "bevy_tasks", - "bevy_utils", - "bitflags 2.9.1", - "bumpalo", - "concurrent-queue", - "derive_more", - "disqualified", - "fixedbitset", - "indexmap", - "log", - "nonmax", - "serde", - "smallvec", - "thiserror 2.0.12", - "variadics_please", -] - -[[package]] -name = "bevy_ecs_macros" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d7bb98aeb8dd30f36e6a773000c12a891d4f1bee2adc3841ec89cc8eaf54e" -dependencies = [ - "bevy_macro_utils", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "bevy_macro_utils" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2473db70d8785b5c75d6dd951a2e51e9be2c2311122db9692c79c9d887517b" -dependencies = [ - "parking_lot", - "proc-macro2", - "quote", - "syn", - "toml_edit", -] - -[[package]] -name = "bevy_platform" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704db2c11b7bc31093df4fbbdd3769f9606a6a5287149f4b51f2680f25834ebc" -dependencies = [ - "cfg-if", - "critical-section", - "foldhash", - "hashbrown", - "portable-atomic", - "portable-atomic-util", - "serde", - "spin", -] - -[[package]] -name = "bevy_ptr" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f1275dfb4cfef4ffc90c3fa75408964864facf833acc932413d52aa5364ba4" - -[[package]] -name = "bevy_reflect" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607ebacc31029cf2f39ac330eabf1d4bc411b159528ec08dbe6b0593eaccfd41" -dependencies = [ - "assert_type_match", - "bevy_platform", - "bevy_ptr", - "bevy_reflect_derive", - "bevy_utils", - "derive_more", - "disqualified", - "downcast-rs 2.0.1", - "erased-serde", - "foldhash", - "glam 0.29.3", - "serde", - "smallvec", - "smol_str", - "thiserror 2.0.12", - "uuid", - "variadics_please", - "wgpu-types", -] - -[[package]] -name = "bevy_reflect_derive" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf35e45e4eb239018369f63f2adc2107a54c329f9276d020e01eee1625b0238b" -dependencies = [ - "bevy_macro_utils", - "proc-macro2", - "quote", - "syn", - "uuid", -] - -[[package]] -name = "bevy_tasks" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444c450b65e108855f42ecb6db0c041a56ea7d7f10cc6222f0ca95e9536a7d19" -dependencies = [ - "async-executor", - "async-task", - "atomic-waker", - "bevy_platform", - "cfg-if", - "crossbeam-queue", - "derive_more", - "futures-lite", - "heapless", -] - -[[package]] -name = "bevy_utils" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2da3b3c1f94dadefcbe837aaa4aa119fcea37f7bdc5307eb05b4ede1921e24" -dependencies = [ - "bevy_platform", - "thread_local", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -376,9 +173,6 @@ name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" -dependencies = [ - "serde", -] [[package]] name = "block2" @@ -415,12 +209,6 @@ dependencies = [ "syn", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.10.1" @@ -514,7 +302,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", - "portable-atomic", ] [[package]] @@ -557,12 +344,6 @@ dependencies = [ "libc", ] -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -584,43 +365,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" -[[package]] -name = "ctrlc" -version = "3.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" -dependencies = [ - "nix", - "windows-sys 0.59.0", -] - [[package]] name = "cursor-icon" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -637,12 +387,6 @@ dependencies = [ "objc2 0.6.1", ] -[[package]] -name = "disqualified" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd" - [[package]] name = "dlib" version = "0.5.2" @@ -658,55 +402,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "downcast-rs" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" - [[package]] name = "dpi" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" -[[package]] -name = "engine_render" -version = "0.1.0" -dependencies = [ - "bevy_app", - "bevy_ecs", - "engine_vulkan", - "engine_window", - "log", - "vulkano", -] - -[[package]] -name = "engine_vulkan" -version = "0.1.0" -dependencies = [ - "bevy_app", - "bevy_ecs", - "engine_window", - "env_logger", - "log", - "thiserror 2.0.12", - "vulkano", - "winit", -] - -[[package]] -name = "engine_window" -version = "0.1.0" -dependencies = [ - "bevy_app", - "bevy_ecs", - "log", - "thiserror 2.0.12", - "winit", -] - [[package]] name = "env_filter" version = "0.1.3" @@ -736,16 +437,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" -dependencies = [ - "serde", - "typeid", -] - [[package]] name = "errno" version = "0.3.12" @@ -756,18 +447,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "foldhash" version = "0.1.5" @@ -801,31 +480,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-lite" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "gethostname" version = "0.4.3" @@ -848,15 +502,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "glam" -version = "0.29.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" -dependencies = [ - "serde", -] - [[package]] name = "glam" version = "0.30.3" @@ -874,35 +519,11 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" -dependencies = [ - "equivalent", - "serde", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "portable-atomic", - "stable_deref_trait", -] [[package]] name = "heck" @@ -1104,18 +725,6 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nom" version = "7.1.3" @@ -1126,12 +735,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nonmax" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" - [[package]] name = "num_enum" version = "0.7.3" @@ -1435,12 +1038,6 @@ dependencies = [ "ttf-parser", ] -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - [[package]] name = "parking_lot" version = "0.12.3" @@ -1652,14 +1249,11 @@ dependencies = [ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ - "bevy_app", - "bevy_ecs", - "engine_render", - "engine_vulkan", - "engine_window", + "anyhow", "env_logger", - "glam 0.30.3", + "glam", "log", + "thiserror 2.0.12", "vulkano", "vulkano-shaders", "winit", @@ -1838,21 +1432,6 @@ dependencies = [ "serde", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "portable-atomic", -] - -[[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" @@ -1984,12 +1563,6 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - [[package]] name = "unicode-ident" version = "1.0.18" @@ -2002,41 +1575,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" -dependencies = [ - "getrandom", - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "variadics_please" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "version_check" version = "0.9.5" @@ -2208,7 +1752,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", - "downcast-rs 1.2.1", + "downcast-rs", "rustix", "scoped-tls", "smallvec", @@ -2330,19 +1874,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "wgpu-types" -version = "24.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" -dependencies = [ - "bitflags 2.9.1", - "js-sys", - "log", - "serde", - "web-sys", -] - [[package]] name = "winapi-util" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index d8c8792..ce36410 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,7 @@ edition = "2024" authors = ["Florian RICHER "] publish = false -[workspace] -resolver = "2" -members = ["crates/*"] - -[workspace.dependencies] +[dependencies] anyhow = "1.0" thiserror = "2.0" winit = { version = "0.30", features = ["rwh_06"] } @@ -20,28 +16,7 @@ vulkano-shaders = "0.35" # Math glam = { version = "0.30" } -# ECS -bevy_ecs = "0.16" -bevy_app = "0.16" # Log and tracing log = "0.4" env_logger = "0.11" - -engine_vulkan = { path = "crates/engine_vulkan" } -engine_window = { path = "crates/engine_window" } -engine_render = { path = "crates/engine_render" } - -[dependencies] -log = { workspace = true } -env_logger = { workspace = true } -bevy_app = { workspace = true } -bevy_ecs = { workspace = true } -winit = { workspace = true } -vulkano = { workspace = true } -vulkano-shaders = { workspace = true } -glam = { workspace = true } - -engine_vulkan = { workspace = true } -engine_window = { workspace = true } -engine_render = { workspace = true } diff --git a/crates/engine_render/Cargo.toml b/crates/engine_render/Cargo.toml deleted file mode 100644 index 5016279..0000000 --- a/crates/engine_render/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "engine_render" -version = "0.1.0" -edition = "2024" - -[dependencies] -log = { workspace = true } -bevy_app = { workspace = true } -bevy_ecs = { workspace = true } -vulkano = { workspace = true } -engine_vulkan = { workspace = true } -engine_window = { workspace = true } diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs deleted file mode 100644 index 8c869e9..0000000 --- a/crates/engine_render/src/lib.rs +++ /dev/null @@ -1,120 +0,0 @@ -use bevy_app::{App, AppLabel, Last, Plugin, SubApp}; -use bevy_ecs::{ - schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet}, - system::{Commands, Res}, - world::World, -}; -use engine_vulkan::{ - VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, - VulkanInstance, VulkanMemoryAllocator, -}; -use engine_window::raw_handle::WindowWrapper; -use window::WindowRenderPlugin; - -pub mod render; -pub mod window; - -#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] -pub enum RenderSystems { - ManageViews, - Prepare, - Queue, - Render, - Present, -} - -#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)] -pub struct Render; - -impl Render { - pub fn base_schedule() -> Schedule { - use RenderSystems::*; - - let mut schedule = Schedule::new(Self); - - schedule.configure_sets((ManageViews, Prepare, Queue, Render, Present).chain()); - - schedule - } -} - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)] -pub struct RenderApp; - -pub struct RenderPlugin; - -impl Plugin for RenderPlugin { - fn build(&self, _app: &mut App) {} - - fn ready(&self, app: &App) -> bool { - let world = app.world(); - - world.get_resource::().is_some() - && world.get_resource::().is_some() - && world.get_resource::().is_some() - && world.get_resource::().is_some() - && world.get_resource::().is_some() - && world - .get_resource::() - .is_some() - && world - .get_resource::() - .is_some() - } - - fn finish(&self, app: &mut App) { - let mut render_app = SubApp::new(); - render_app.update_schedule = Some(Render.intern()); - render_app.add_schedule(Render::base_schedule()); - render_app.add_systems( - Render, - render::render_system - .in_set(RenderSystems::Render) - .run_if(render::can_render), - ); - - extract_app_resources(app.world_mut(), render_app.world_mut()); - - app.insert_sub_app(RenderApp, render_app); - - app.add_plugins(WindowRenderPlugin); - } -} - -fn extract_app_resources(world: &mut World, render_world: &mut World) { - let window_wrapper = world - .get_resource::() - .expect("Failed to get WindowWrapper. Check is WindowPlugin is added before RenderPlugin."); - - let vulkan_instance = world.get_resource::().expect( - "Failed to get Vulkan instance. Check is VulkanPlugin is added before RenderPlugin.", - ); - - let vulkan_device = world - .get_resource::() - .expect("Failed to get Vulkan device. Check is VulkanPlugin is added before RenderPlugin."); - - let vulkan_graphics_queue = world.get_resource::().expect( - "Failed to get Vulkan graphics queue. Check is VulkanPlugin is added before RenderPlugin.", - ); - - let vulkan_memory_allocator = world - .get_resource::() - .expect("Failed to get Vulkan memory allocator. Check is VulkanPlugin is added before RenderPlugin."); - - let vulkan_command_buffer_allocator = world - .get_resource::() - .expect("Failed to get Vulkan command buffer allocator. Check is VulkanPlugin is added before RenderPlugin."); - - let vulkan_descriptor_set_allocator = world - .get_resource::() - .expect("Failed to get Vulkan descriptor set allocator. Check is VulkanPlugin is added before RenderPlugin."); - - render_world.insert_resource(vulkan_instance.clone()); - render_world.insert_resource(vulkan_device.clone()); - render_world.insert_resource(vulkan_graphics_queue.clone()); - render_world.insert_resource(vulkan_memory_allocator.clone()); - render_world.insert_resource(vulkan_command_buffer_allocator.clone()); - render_world.insert_resource(vulkan_descriptor_set_allocator.clone()); - render_world.insert_resource(window_wrapper.clone()); -} diff --git a/crates/engine_render/src/render.rs b/crates/engine_render/src/render.rs deleted file mode 100644 index 1a8c95e..0000000 --- a/crates/engine_render/src/render.rs +++ /dev/null @@ -1,122 +0,0 @@ -use bevy_ecs::system::{Res, ResMut}; -use engine_vulkan::{VulkanCommandBufferAllocator, VulkanDevice, VulkanGraphicsQueue}; -use vulkano::{ - Validated, VulkanError, - command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, - }, - render_pass::{AttachmentLoadOp, AttachmentStoreOp}, - swapchain::{SwapchainAcquireFuture, SwapchainPresentInfo, acquire_next_image}, - sync::{self, GpuFuture}, -}; - -use crate::window::{WindowSurface, WindowSurfaceData}; - -pub fn can_render(window_surface: Res) -> bool { - window_surface.surface.is_some() -} - -pub fn render_system( - mut window_surface: ResMut, - command_buffer_allocator: Res, - graphics_queue: Res, - device: Res, -) { - { - let surface = window_surface.surface.as_ref().unwrap(); - if surface.viewport.extent[0] == 0.0 || surface.viewport.extent[1] == 0.0 { - return; - } - } - - let (image_index, acquire_future) = - match acquire_image(&mut window_surface.surface.as_mut().unwrap()) { - Some(r) => r, - None => return, - }; - - let mut builder = AutoCommandBufferBuilder::primary( - command_buffer_allocator.0.clone(), - graphics_queue.0.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .expect("failed to create command buffer builder"); - - { - let surface = window_surface.surface.as_ref().unwrap(); - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 0.0].into()), - ..RenderingAttachmentInfo::image_view( - surface.attachment_image_views[image_index as usize].clone(), - ) - })], - ..Default::default() - }) - .unwrap() - .set_viewport(0, [surface.viewport.clone()].into_iter().collect()) - .unwrap(); - } - - builder.end_rendering().unwrap(); - - let command_buffer = builder.build().unwrap(); - - { - let surface = window_surface.surface.as_mut().unwrap(); - - let future = surface - .previous_frame_end - .take() - .unwrap() - .join(acquire_future) - .then_execute(graphics_queue.0.clone(), command_buffer) - .unwrap() - .then_swapchain_present( - graphics_queue.0.clone(), - SwapchainPresentInfo::swapchain_image_index(surface.swapchain.clone(), image_index), - ) - .then_signal_fence_and_flush(); - - match future.map_err(Validated::unwrap) { - Ok(future) => { - surface.previous_frame_end = Some(future.boxed_send_sync()); - } - Err(VulkanError::OutOfDate) => { - surface.recreate_swapchain = true; - surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync()); - } - Err(e) => { - println!("failed to flush future: {e}"); - surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync()); - } - } - } -} - -fn acquire_image(surface: &mut WindowSurfaceData) -> Option<(u32, SwapchainAcquireFuture)> { - surface - .previous_frame_end - .as_mut() - .unwrap() - .cleanup_finished(); - - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(surface.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - surface.recreate_swapchain = true; - return None; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - if suboptimal { - surface.recreate_swapchain = true; - } - - Some((image_index, acquire_future)) -} diff --git a/crates/engine_render/src/window/mod.rs b/crates/engine_render/src/window/mod.rs deleted file mode 100644 index d019444..0000000 --- a/crates/engine_render/src/window/mod.rs +++ /dev/null @@ -1,222 +0,0 @@ -use std::sync::Arc; - -use bevy_app::{App, Plugin}; -use bevy_ecs::{ - resource::Resource, - schedule::IntoScheduleConfigs, - system::{Res, ResMut}, -}; -use engine_vulkan::{VulkanDevice, VulkanInstance}; -use engine_window::raw_handle::WindowWrapper; -use vulkano::{ - image::{Image, ImageUsage, view::ImageView}, - pipeline::graphics::viewport::Viewport, - swapchain::{Surface, Swapchain, SwapchainCreateInfo}, - sync::{self, GpuFuture}, -}; - -use super::{Render, RenderApp, RenderSystems}; - -pub struct WindowSurfaceData { - pub swapchain: Arc, - pub attachment_image_views: Vec>, - pub viewport: Viewport, - pub recreate_swapchain: bool, - pub previous_frame_end: Option>, -} - -#[derive(Resource, Default)] -pub struct WindowSurface { - pub surface: Option, -} - -pub struct WindowRenderPlugin; - -impl Plugin for WindowRenderPlugin { - fn build(&self, app: &mut App) { - let render_app = app - .get_sub_app_mut(RenderApp) - .expect("Failed to get RenderApp. Check is RenderPlugin is added."); - - render_app.init_resource::(); - - render_app.add_systems( - Render, - create_window_surface - .in_set(RenderSystems::ManageViews) - .run_if(need_create_window_surface) - .before(need_update_window_surface), - ); - - render_app.add_systems( - Render, - update_window_surface - .in_set(RenderSystems::ManageViews) - .run_if(need_update_window_surface), - ); - } -} - -fn need_create_window_surface(window_surface: Res) -> bool { - window_surface.surface.is_none() -} - -fn create_window_surface( - mut window_surface: ResMut, - window_handle: Res, - vulkan_instance: Res, - vulkan_device: Res, -) { - let window_size = window_handle.0.inner_size(); - - let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone()) - .expect("Failed to create surface"); - log::debug!("Surface created"); - - let (swapchain, images) = { - let surface_capabilities = vulkan_device - .0 - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - - let (image_format, _) = vulkan_device - .0 - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - Swapchain::new( - vulkan_device.0.clone(), - surface, - SwapchainCreateInfo { - // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - - ..Default::default() - }, - ) - .unwrap() - }; - log_swapchain_info(&swapchain, false); - - let attachment_image_views = window_size_dependent_setup(&images); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - log_viewport_info(&viewport, false); - - let recreate_swapchain = false; - let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync()); - - window_surface.surface = Some(WindowSurfaceData { - swapchain, - attachment_image_views, - viewport, - recreate_swapchain, - previous_frame_end, - }); -} - -fn window_size_dependent_setup(images: &[Arc]) -> Vec> { - images - .iter() - .map(|image| ImageView::new_default(image.clone()).unwrap()) - .collect::>() -} - -fn need_update_window_surface( - window_surface: Res, - window_handle: Res, -) -> bool { - match &window_surface.surface { - Some(surface) => { - let window_size: [f32; 2] = window_handle.0.inner_size().into(); - surface.recreate_swapchain || surface.viewport.extent != window_size - } - None => false, - } -} - -fn update_window_surface( - mut window_surface: ResMut, - window_handle: Res, -) { - let window_surface = window_surface.surface.as_mut().unwrap(); - - let window_size = window_handle.0.inner_size(); - let (new_swapchain, new_images) = window_surface - .swapchain - .recreate(SwapchainCreateInfo { - image_extent: window_size.into(), - ..window_surface.swapchain.create_info() - }) - .expect("Failed to recreate swapchain"); - - window_surface.swapchain = new_swapchain; - window_surface.attachment_image_views = window_size_dependent_setup(&new_images); - window_surface.viewport.extent = window_size.into(); - window_surface.recreate_swapchain = false; - - log_swapchain_info(&window_surface.swapchain, true); - log_viewport_info(&window_surface.viewport, true); -} - -fn log_swapchain_info(swapchain: &Swapchain, recreate_swapchain: bool) { - if recreate_swapchain { - log::debug!("Swapchain recreated"); - } else { - log::debug!("Swapchain created"); - } - log::debug!( - "\tMin image count: {}", - swapchain.create_info().min_image_count - ); - log::debug!("\tImage format: {:?}", swapchain.create_info().image_format); - log::debug!("\tImage extent: {:?}", swapchain.create_info().image_extent); - log::debug!("\tImage usage: {:?}", swapchain.create_info().image_usage); - log::debug!( - "\tComposite alpha: {:?}", - swapchain.create_info().composite_alpha - ); - log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode); - log::debug!( - "\tImage sharing: {:?}", - swapchain.create_info().image_sharing - ); - log::debug!( - "\tPre transform: {:?}", - swapchain.create_info().pre_transform - ); - log::debug!( - "\tComposite alpha: {:?}", - swapchain.create_info().composite_alpha - ); - log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode); - log::debug!( - "\tFull screen exclusive: {:?}", - swapchain.create_info().full_screen_exclusive - ); -} - -fn log_viewport_info(viewport: &Viewport, recreate_viewport: bool) { - if recreate_viewport { - log::debug!("Viewport recreated"); - } else { - log::debug!("Viewport created"); - } - log::debug!("\tOffset: {:?}", viewport.offset); - log::debug!("\tExtent: {:?}", viewport.extent); - log::debug!("\tDepth range: {:?}", viewport.depth_range); -} diff --git a/crates/engine_vulkan/Cargo.toml b/crates/engine_vulkan/Cargo.toml deleted file mode 100644 index c0fe35b..0000000 --- a/crates/engine_vulkan/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "engine_vulkan" -version = "0.1.0" -edition = "2024" - -[dependencies] -thiserror = { workspace = true } -log = { workspace = true } -env_logger = { workspace = true } -bevy_app = { workspace = true } -bevy_ecs = { workspace = true } -winit = { workspace = true } -vulkano = { workspace = true } -engine_window = { workspace = true } diff --git a/crates/engine_vulkan/src/device.rs b/crates/engine_vulkan/src/device.rs deleted file mode 100644 index f20335f..0000000 --- a/crates/engine_vulkan/src/device.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::sync::Arc; - -use bevy_ecs::world::World; -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::{ - command_buffer::allocator::StandardCommandBufferAllocator, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{ - Device, DeviceCreateInfo, DeviceExtensions, Queue, - physical::{PhysicalDevice, PhysicalDeviceType}, - }, - memory::allocator::StandardMemoryAllocator, -}; - -use crate::{ - VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanConfig, VulkanDescriptorSetAllocator, - VulkanDevice, VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator, VulkanTransferQueue, - queues::{VulkanQueueFamilyIndices, VulkanQueuesQuery, find_queues_family_indices}, -}; - -pub fn create_and_insert_device(world: &mut World, config: &VulkanConfig) { - let picked_device = - pick_physical_device(world, &config).expect("Failed to pick physical device"); - - let device = picked_device.device; - let physical_device = device.physical_device(); - - log::debug!("Vulkan device created"); - log::debug!( - "\tPhysical device: {:?} ({:?})", - physical_device.properties().device_name, - physical_device.properties().device_type - ); - log::debug!("\tDevice extensions: {:?}", device.enabled_extensions()); - log::debug!("\tDevice features: {:?}", device.enabled_features()); - - world.insert_resource(VulkanDevice(device.clone())); - - log::debug!("\tDevice selected queues:"); - if config.with_graphics_queue { - world.insert_resource(VulkanGraphicsQueue( - picked_device - .graphics_queue - .expect("Failed to get graphics queue"), - )); - log::debug!("\t\t- Graphics queue"); - } - - if config.with_compute_queue { - world.insert_resource(VulkanComputeQueue( - picked_device - .compute_queue - .expect("Failed to get compute queue"), - )); - log::debug!("\t\t- Compute queue"); - } - - if config.with_transfer_queue { - world.insert_resource(VulkanTransferQueue( - picked_device - .transfer_queue - .expect("Failed to get transfer queue"), - )); - log::debug!("\t\t- Transfer queue"); - } - - world.insert_resource(VulkanMemoryAllocator(Arc::new( - StandardMemoryAllocator::new_default(device.clone()), - ))); - world.insert_resource(VulkanCommandBufferAllocator(Arc::new( - StandardCommandBufferAllocator::new(device.clone(), Default::default()), - ))); - world.insert_resource(VulkanDescriptorSetAllocator(Arc::new( - StandardDescriptorSetAllocator::new(device.clone(), Default::default()), - ))); -} - -struct PickedDevice { - pub device: Arc, - pub graphics_queue: Option>, - pub compute_queue: Option>, - pub transfer_queue: Option>, -} - -fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option { - let instance = world - .get_resource::() - .expect("Failed to get VulkanInstance during vulkan plugin initialization"); - - instance - .0 - .enumerate_physical_devices() - .expect("Failed to enumerate physical devices") - .filter_map(|p| check_physical_device_support(world, &p, config).and_then(|r| Some((p, r)))) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .take() - .and_then(|(p, (device_extensions, queue_family_indices))| { - Some(create_device( - config, - &p, - device_extensions, - queue_family_indices, - )) - }) -} - -fn check_device_extensions_support( - physical_device: &Arc, - config: &VulkanConfig, -) -> Option { - let device_extensions = DeviceExtensions { - khr_swapchain: config.with_window_surface, - ..config.device_extensions - }; - - if physical_device - .supported_extensions() - .contains(&device_extensions) - { - log::debug!( - "\t\t[OK] Device supports required extensions {:?}", - device_extensions - ); - Some(device_extensions) - } else { - log::debug!( - "\t\t[FAILED] Device does not support required extensions {:?}", - device_extensions - ); - None - } -} - -fn check_physical_device_support( - world: &World, - physical_device: &Arc, - config: &VulkanConfig, -) -> Option<(DeviceExtensions, VulkanQueueFamilyIndices)> { - log::debug!("Checking physical device"); - log::debug!("\tProperties"); - log::debug!("\t\tName: {}", physical_device.properties().device_name); - log::debug!("\t\tAPI version: {}", physical_device.api_version()); - log::debug!( - "\t\tDevice type: {:?}", - physical_device.properties().device_type - ); - log::debug!("\tRequired supports checking report"); - - let device_extensions = check_device_extensions_support(physical_device, config)?; - - let display_handle = world - .get_resource::() - .expect("DisplayHandleWrapper must be added before VulkanPlugin"); - - let queue_support_query = VulkanQueuesQuery { - with_surface: Some(&display_handle), - with_graphics_queue: config.with_graphics_queue, - with_compute_queue: config.with_compute_queue, - with_transfer_queue: config.with_transfer_queue, - }; - - let queue_family_indices = find_queues_family_indices(physical_device, &queue_support_query); - log::debug!("\t\tQueue family indices: {:#?}", queue_family_indices); - - Some((device_extensions, queue_family_indices)) -} - -fn create_device( - config: &VulkanConfig, - physical_device: &Arc, - device_extensions: DeviceExtensions, - queue_family_indices: VulkanQueueFamilyIndices, -) -> PickedDevice { - let (device, mut queues) = Device::new( - physical_device.clone(), - DeviceCreateInfo { - queue_create_infos: queue_family_indices.into(), - enabled_extensions: device_extensions, - enabled_features: config.device_features, - ..Default::default() - }, - ) - .expect("Failed to create device"); - - let mut graphics_queue = None; - let mut compute_queue = None; - let mut transfer_queue = None; - - if config.with_graphics_queue { - graphics_queue = queues.next(); - } - - if config.with_compute_queue { - compute_queue = queues.next(); - } - - if config.with_transfer_queue { - transfer_queue = queues.next(); - } - - PickedDevice { - device, - graphics_queue, - compute_queue, - transfer_queue, - } -} diff --git a/crates/engine_vulkan/src/instance.rs b/crates/engine_vulkan/src/instance.rs deleted file mode 100644 index dcda857..0000000 --- a/crates/engine_vulkan/src/instance.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::sync::Arc; - -use bevy_ecs::world::World; -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::{ - VulkanLibrary, - instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, - swapchain::Surface, -}; - -use crate::{VulkanConfig, VulkanInstance}; - -fn load_library() -> Arc { - let library = VulkanLibrary::new().unwrap(); - - log::debug!("Available Instance layers:"); - for layer in library.layer_properties().unwrap() { - log::debug!( - "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", - layer.name(), - layer.description(), - layer.implementation_version(), - layer.vulkan_version() - ); - } - - library -} - -pub fn create_and_insert_instance(world: &mut World, config: &VulkanConfig) { - let library = load_library(); - - let instance_extensions = { - if config.with_window_surface { - let display_handle = world - .get_resource::() - .expect("DisplayHandleWrapper must be added before VulkanPlugin"); - - Surface::required_extensions(&display_handle.0) - .expect("Failed to get surface required extensions") - } else { - InstanceExtensions::default() - } - }; - - let instance = Instance::new( - library, - InstanceCreateInfo { - // Enable enumerating devices that use non-conformant Vulkan implementations. - // (e.g. MoltenVK) - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: instance_extensions, - enabled_layers: config.instance_layers.clone(), - ..Default::default() - }, - ) - .expect("Failed to create vulkan instance"); - - log::debug!("Instance created"); - log::debug!( - "\t- Enabled extensions: {:?}", - instance.enabled_extensions() - ); - log::debug!("\t- Enabled layers: {:?}", instance.enabled_layers()); - log::debug!("\t- API version: {:?}", instance.api_version()); - log::debug!("\t- Max API version: {:?}", instance.max_api_version()); - log::debug!("\t- Flags: {:?}", instance.flags()); - - world.insert_resource(VulkanInstance(instance)); -} diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs deleted file mode 100644 index 38cb456..0000000 --- a/crates/engine_vulkan/src/lib.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::sync::Arc; - -use bevy_app::{App, Plugin}; -use bevy_ecs::resource::Resource; -use vulkano::{ - command_buffer::allocator::StandardCommandBufferAllocator, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{Device, DeviceExtensions, DeviceFeatures, Queue}, - instance::Instance, - memory::allocator::StandardMemoryAllocator, -}; - -mod device; -mod instance; -mod queues; - -use crate::{device::create_and_insert_device, instance::create_and_insert_instance}; - -#[derive(Resource, Clone)] -pub struct VulkanInstance(pub Arc); - -#[derive(Resource, Clone)] -pub struct VulkanDevice(pub Arc); - -#[derive(Resource, Clone)] -pub struct VulkanGraphicsQueue(pub Arc); - -#[derive(Resource, Clone)] -pub struct VulkanComputeQueue(pub Arc); - -#[derive(Resource, Clone)] -pub struct VulkanTransferQueue(pub Arc); - -#[derive(Resource, Clone)] -pub struct VulkanMemoryAllocator(pub Arc); - -#[derive(Resource, Clone)] -pub struct VulkanCommandBufferAllocator(pub Arc); - -#[derive(Resource, Clone)] -pub struct VulkanDescriptorSetAllocator(pub Arc); - -#[derive(Debug, thiserror::Error)] -pub enum VulkanError { - #[error("Failed to create vulkan context")] - FailedToCreateVulkanContext, -} - -pub struct VulkanConfig { - pub instance_layers: Vec, - pub device_extensions: DeviceExtensions, - pub device_features: DeviceFeatures, - pub with_window_surface: bool, - pub with_graphics_queue: bool, - pub with_compute_queue: bool, - pub with_transfer_queue: bool, -} - -impl Default for VulkanConfig { - fn default() -> Self { - Self { - instance_layers: Vec::default(), - device_extensions: DeviceExtensions::default(), - device_features: DeviceFeatures::default(), - with_window_surface: true, - with_graphics_queue: true, - with_compute_queue: true, - with_transfer_queue: true, - } - } -} - -#[derive(Default)] -pub struct VulkanPlugin { - pub vulkan_config: VulkanConfig, -} - -impl Plugin for VulkanPlugin { - fn build(&self, app: &mut App) { - let world = app.world_mut(); - - create_and_insert_instance(world, &self.vulkan_config); - create_and_insert_device(world, &self.vulkan_config); - } -} diff --git a/crates/engine_vulkan/src/queues.rs b/crates/engine_vulkan/src/queues.rs deleted file mode 100644 index 17e6c08..0000000 --- a/crates/engine_vulkan/src/queues.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::device::{ - QueueCreateInfo, QueueFamilyProperties, QueueFlags, physical::PhysicalDevice, -}; - -#[derive(Debug)] -pub enum VulkanQueueFamilyStatus { - Unused, - NotSupported, - Supported(u32), -} - -impl VulkanQueueFamilyStatus { - pub fn can_be_set(&self) -> bool { - match self { - VulkanQueueFamilyStatus::NotSupported => true, - _ => false, - } - } -} - -/// Convert a boolean to a VulkanQueueFamilyStatus as default value -impl From for VulkanQueueFamilyStatus { - fn from(value: bool) -> Self { - if value { - VulkanQueueFamilyStatus::NotSupported - } else { - VulkanQueueFamilyStatus::Unused - } - } -} - -/// Convert a DisplayHandleWrapper Option to a VulkanQueueFamilyStatus as default value -impl From> for VulkanQueueFamilyStatus { - fn from(value: Option<&DisplayHandleWrapper>) -> Self { - if value.is_some() { - VulkanQueueFamilyStatus::NotSupported - } else { - VulkanQueueFamilyStatus::Unused - } - } -} - -#[derive(Debug)] -pub struct VulkanQueueFamilyIndices { - pub graphics_queue_family_index: VulkanQueueFamilyStatus, - pub compute_queue_family_index: VulkanQueueFamilyStatus, - pub transfer_queue_family_index: VulkanQueueFamilyStatus, -} - -impl From for Vec<(VulkanQueueFamilyStatus, f32)> { - fn from(indices: VulkanQueueFamilyIndices) -> Self { - vec![ - (indices.graphics_queue_family_index, 1.0), - (indices.compute_queue_family_index, 0.5), - (indices.transfer_queue_family_index, 0.5), - ] - } -} - -impl From for Vec { - fn from(indices: VulkanQueueFamilyIndices) -> Self { - let mut queue_create_infos = HashMap::::new(); - - let statuses: Vec<(VulkanQueueFamilyStatus, f32)> = indices.into(); - - for (status, priority) in statuses.iter() { - match status { - VulkanQueueFamilyStatus::Supported(index) => { - let entry = queue_create_infos.entry(*index).or_insert(QueueCreateInfo { - queue_family_index: *index, - queues: Vec::new(), - ..Default::default() - }); - - entry.queues.push(*priority); - } - _ => {} - } - } - - queue_create_infos - .into_iter() - .map(|(_, value)| value) - .collect() - } -} - -/// Convert a VulkanQueuesQuery to a VulkanQueuesFamilyIndices as default value -impl<'a> From<&'a VulkanQueuesQuery<'a>> for VulkanQueueFamilyIndices { - fn from(query: &'a VulkanQueuesQuery<'a>) -> Self { - VulkanQueueFamilyIndices { - graphics_queue_family_index: query.with_graphics_queue.into(), - compute_queue_family_index: query.with_compute_queue.into(), - transfer_queue_family_index: query.with_transfer_queue.into(), - } - } -} - -pub struct VulkanQueuesQuery<'a> { - pub with_surface: Option<&'a DisplayHandleWrapper>, - pub with_graphics_queue: bool, - pub with_compute_queue: bool, - pub with_transfer_queue: bool, -} - -pub fn find_queues_family_indices( - physical_device: &Arc, - query: &VulkanQueuesQuery, -) -> VulkanQueueFamilyIndices { - let mut indices: VulkanQueueFamilyIndices = query.into(); - - for (i, queue_family_properties) in physical_device.queue_family_properties().iter().enumerate() - { - let mut available_queue_count = queue_family_properties.queue_count; - - if indices.graphics_queue_family_index.can_be_set() - && check_queue_support(queue_family_properties, QueueFlags::GRAPHICS) - && available_queue_count > 0 - { - if query.with_surface.is_none() - || check_presentation_support( - physical_device, - i as u32, - &query.with_surface.as_ref().unwrap(), - ) - { - indices.graphics_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); - available_queue_count -= 1; - } - } - - if indices.compute_queue_family_index.can_be_set() - && check_queue_support(queue_family_properties, QueueFlags::COMPUTE) - && available_queue_count > 0 - { - indices.compute_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); - available_queue_count -= 1; - } - - if indices.transfer_queue_family_index.can_be_set() - && check_queue_support(queue_family_properties, QueueFlags::TRANSFER) - && available_queue_count > 0 - { - indices.transfer_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32); - } - - if !indices.graphics_queue_family_index.can_be_set() - && !indices.compute_queue_family_index.can_be_set() - && !indices.transfer_queue_family_index.can_be_set() - { - break; - } - } - - return indices; -} - -fn check_presentation_support( - physical_device: &Arc, - queue_family_index: u32, - surface: &DisplayHandleWrapper, -) -> bool { - physical_device - .presentation_support(queue_family_index, &surface.0) - .unwrap() -} - -fn check_queue_support( - queue_family_property: &QueueFamilyProperties, - queue_flags: QueueFlags, -) -> bool { - queue_family_property.queue_flags.intersects(queue_flags) -} diff --git a/crates/engine_window/Cargo.toml b/crates/engine_window/Cargo.toml deleted file mode 100644 index b5016cc..0000000 --- a/crates/engine_window/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "engine_window" -version = "0.1.0" -edition = "2024" - -[dependencies] -thiserror = { workspace = true } -log = { workspace = true } -bevy_app = { workspace = true } -bevy_ecs = { workspace = true } -winit = { workspace = true } diff --git a/crates/engine_window/src/config.rs b/crates/engine_window/src/config.rs deleted file mode 100644 index 487140c..0000000 --- a/crates/engine_window/src/config.rs +++ /dev/null @@ -1,17 +0,0 @@ -use bevy_ecs::resource::Resource; -use winit::{dpi::PhysicalSize, window::WindowAttributes}; - -#[derive(Resource, Clone)] -pub struct WindowConfig { - pub title: String, - pub width: u32, - pub height: u32, -} - -impl Into for &WindowConfig { - fn into(self) -> WindowAttributes { - WindowAttributes::default() - .with_title(self.title.clone()) - .with_inner_size(PhysicalSize::new(self.width as f64, self.height as f64)) - } -} diff --git a/crates/engine_window/src/lib.rs b/crates/engine_window/src/lib.rs deleted file mode 100644 index 33e150c..0000000 --- a/crates/engine_window/src/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -use bevy_app::{App, AppExit, Plugin, PluginsState}; -use config::WindowConfig; -use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper, WindowWrapper}; -use state::WindowState; -use winit::event_loop::EventLoop; - -pub mod config; -pub mod raw_handle; -pub mod state; - -#[derive(Debug, thiserror::Error)] -pub enum WindowError { - #[error("Failed to create event loop")] - FailedToCreateEventLoop, -} - -pub struct WindowPlugin { - pub window_config: WindowConfig, -} - -impl Plugin for WindowPlugin { - fn build(&self, app: &mut App) { - let world = app.world_mut(); - world.insert_resource(self.window_config.clone()); - - let mut event_loop_builder = EventLoop::with_user_event(); - let event_loop = event_loop_builder - .build() - .map_err(|_| WindowError::FailedToCreateEventLoop) - .expect("Failed to create event loop"); - - world.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle())); - - app.set_runner(Box::new(move |app| runner(app, event_loop))); - } -} - -fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { - if app.plugins_state() == PluginsState::Ready { - app.finish(); - app.cleanup(); - } - - app.world_mut() - .insert_resource(EventLoopProxyWrapper::new(event_loop.create_proxy())); - - let mut window_state = WindowState::new(app); - - match event_loop.run_app(&mut window_state) { - Ok(_) => AppExit::Success, - Err(e) => { - log::error!("Error running window state: {e}"); - AppExit::error() - } - } -} diff --git a/crates/engine_window/src/raw_handle.rs b/crates/engine_window/src/raw_handle.rs deleted file mode 100644 index 87d5988..0000000 --- a/crates/engine_window/src/raw_handle.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::sync::Arc; - -use bevy_ecs::resource::Resource; -use winit::{event_loop::EventLoopProxy, window::Window}; - -#[derive(Resource)] -pub struct EventLoopProxyWrapper(EventLoopProxy); - -impl EventLoopProxyWrapper { - pub fn new(event_loop: EventLoopProxy) -> Self { - Self(event_loop) - } - - pub fn proxy(&self) -> &EventLoopProxy { - &self.0 - } -} - -#[derive(Resource, Clone)] -pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); - -#[derive(Resource, Clone)] -pub struct WindowWrapper(pub Arc); diff --git a/crates/engine_window/src/state.rs b/crates/engine_window/src/state.rs deleted file mode 100644 index 0f708fd..0000000 --- a/crates/engine_window/src/state.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::sync::Arc; - -use bevy_app::{App, PluginsState}; -use bevy_ecs::world::World; -use winit::{ - application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, - window::WindowId, -}; - -use super::{config::WindowConfig, raw_handle::WindowWrapper}; - -pub struct WindowState { - app: App, -} - -impl WindowState { - pub fn new(app: App) -> Self { - Self { app } - } - - fn world(&self) -> &World { - self.app.world() - } -} - -impl ApplicationHandler for WindowState { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window_config = self.world().get_resource::().unwrap(); - - let window = event_loop.create_window(window_config.into()).unwrap(); - self.app - .world_mut() - .insert_resource(WindowWrapper(Arc::new(window))); - } - - fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) { - if self.app.plugins_state() == PluginsState::Ready { - self.app.finish(); - self.app.cleanup(); - } - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => { - log::debug!("The close button was pressed; stopping"); - event_loop.exit(); - } - WindowEvent::RedrawRequested => { - if self.app.plugins_state() == PluginsState::Cleaned { - self.app.update(); - } - - let window_wrapper = self.app.world().get_resource::().unwrap(); - - window_wrapper.0.request_redraw(); - } - _ => {} - } - } - - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {} -} diff --git a/flake.nix b/flake.nix index 9cef9c1..dbd8d4c 100644 --- a/flake.nix +++ b/flake.nix @@ -76,7 +76,7 @@ packages = { default = rustPlatform.buildRustPackage { - pname = "rust_ash_test"; + pname = "vulkan_test"; version = "0.1.0"; src = self; diff --git a/src/core/camera/mod.rs b/src/core/camera/mod.rs deleted file mode 100644 index f9b6925..0000000 --- a/src/core/camera/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -use bevy_ecs::component::Component; -use glam::{Mat4, Quat, Vec3}; - -pub trait Camera: Into + Component {} - -#[derive(Component)] -pub struct Camera3D { - pub projection: Mat4, - pub position: Vec3, - pub rotation: Quat, -} - -impl Into for Camera3D { - fn into(self) -> Mat4 { - Mat4::from_rotation_translation(self.rotation, self.position) * self.projection - } -} - -impl Camera for Camera3D {} diff --git a/src/core/mod.rs b/src/core/mod.rs deleted file mode 100644 index a8707c6..0000000 --- a/src/core/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod camera; -pub mod render; diff --git a/src/core/render/material.rs b/src/core/render/material.rs deleted file mode 100644 index 539d4bc..0000000 --- a/src/core/render/material.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::sync::Arc; - -use bevy_ecs::component::Component; -use vulkano::pipeline::GraphicsPipeline; - -#[derive(Component)] -pub struct Material(pub Arc); diff --git a/src/core/render/mesh.rs b/src/core/render/mesh.rs deleted file mode 100644 index ca5ec0f..0000000 --- a/src/core/render/mesh.rs +++ /dev/null @@ -1,14 +0,0 @@ -use bevy_ecs::component::Component; - -use super::vertex::Vertex2D; - -#[derive(Component)] -pub struct Mesh2D { - pub vertices: Vec, -} - -impl Mesh2D { - pub fn new(vertices: Vec) -> Self { - Self { vertices } - } -} diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs deleted file mode 100644 index 5d003a8..0000000 --- a/src/core/render/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod material; -pub mod mesh; -pub mod vertex; diff --git a/src/game/mod.rs b/src/game/mod.rs deleted file mode 100644 index e04b2a7..0000000 --- a/src/game/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -use bevy_app::App; -use engine_render::RenderPlugin; -use engine_vulkan::{VulkanConfig, VulkanPlugin}; -use engine_window::{WindowPlugin, config::WindowConfig}; -use vulkano::device::{DeviceExtensions, DeviceFeatures}; - -pub fn init(app: &mut App) { - let window_config = WindowConfig { - title: "Rust ASH Test".to_string(), - width: 800, - height: 600, - }; - - let device_extensions = DeviceExtensions { - khr_dynamic_rendering: true, - ..Default::default() - }; - - let device_features = DeviceFeatures { - dynamic_rendering: true, - ..Default::default() - }; - - let vulkan_config = VulkanConfig { - instance_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], - device_extensions, - device_features, - ..Default::default() - }; - - app.add_plugins(( - WindowPlugin { window_config }, - VulkanPlugin { vulkan_config }, - RenderPlugin, - )); -} diff --git a/src/main.rs b/src/main.rs index 34fad4b..ca3f901 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,14 @@ use winit::event_loop::{ControlFlow, EventLoop}; -use bevy_app::{App, AppExit}; - -pub mod core; -pub mod game; -pub mod old_app; - +mod render; fn main() { env_logger::init(); - run_new_app(); - // run_old_app(); -} - -fn run_new_app() { - let mut app = App::default(); - game::init(&mut app); - match app.run() { - AppExit::Success => {} - AppExit::Error(e) => { - log::error!("Error running new app: {e}"); - } - } -} - -fn run_old_app() { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let vulkan_context = old_app::vulkan_context::VulkanContext::from(&event_loop); - let mut app = old_app::app::App::from(vulkan_context); + let vulkan_context = render::vulkan_context::VulkanContext::from(&event_loop); + let mut app = render::app::App::from(vulkan_context); match event_loop.run_app(&mut app) { Ok(_) => {} diff --git a/src/old_app/app.rs b/src/render/app.rs similarity index 97% rename from src/old_app/app.rs rename to src/render/app.rs index cfcf038..d46757e 100644 --- a/src/old_app/app.rs +++ b/src/render/app.rs @@ -1,6 +1,6 @@ -use crate::old_app::scene::Scene; -use crate::old_app::vulkan_context::VulkanContext; -use crate::old_app::window_render_context::WindowRenderContext; +use crate::render::scene::Scene; +use crate::render::vulkan_context::VulkanContext; +use crate::render::window_render_context::WindowRenderContext; use std::sync::Arc; use vulkano::command_buffer::{RenderingAttachmentInfo, RenderingInfo}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; diff --git a/src/old_app/mod.rs b/src/render/mod.rs similarity index 86% rename from src/old_app/mod.rs rename to src/render/mod.rs index 9fce0a9..c1bda07 100644 --- a/src/old_app/mod.rs +++ b/src/render/mod.rs @@ -3,3 +3,4 @@ pub mod pipelines; pub mod scene; pub mod vulkan_context; pub mod window_render_context; +pub mod vertex; diff --git a/src/old_app/pipelines/mod.rs b/src/render/pipelines/mod.rs similarity index 100% rename from src/old_app/pipelines/mod.rs rename to src/render/pipelines/mod.rs diff --git a/src/old_app/pipelines/triangle_pipeline.rs b/src/render/pipelines/triangle_pipeline.rs similarity index 98% rename from src/old_app/pipelines/triangle_pipeline.rs rename to src/render/pipelines/triangle_pipeline.rs index e573747..04aef22 100644 --- a/src/old_app/pipelines/triangle_pipeline.rs +++ b/src/render/pipelines/triangle_pipeline.rs @@ -20,7 +20,7 @@ use vulkano::pipeline::{ use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; -use crate::core::render::vertex::Vertex2D; +use crate::render::vertex::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/old_app/scene.rs b/src/render/scene.rs similarity index 96% rename from src/old_app/scene.rs rename to src/render/scene.rs index 72e3f48..9dc7bf9 100644 --- a/src/old_app/scene.rs +++ b/src/render/scene.rs @@ -1,4 +1,4 @@ -use crate::old_app::pipelines::triangle_pipeline::shaders::vs; +use crate::render::pipelines::triangle_pipeline::shaders::vs; use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; @@ -9,8 +9,8 @@ use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use crate::core::render::vertex::Vertex2D; -use crate::old_app::pipelines::triangle_pipeline::create_triangle_pipeline; +use crate::render::vertex::Vertex2D; +use crate::render::pipelines::triangle_pipeline::create_triangle_pipeline; use super::vulkan_context::VulkanContext; use super::window_render_context::WindowRenderContext; diff --git a/src/core/render/vertex.rs b/src/render/vertex.rs similarity index 100% rename from src/core/render/vertex.rs rename to src/render/vertex.rs diff --git a/src/old_app/vulkan_context.rs b/src/render/vulkan_context.rs similarity index 100% rename from src/old_app/vulkan_context.rs rename to src/render/vulkan_context.rs diff --git a/src/old_app/window_render_context.rs b/src/render/window_render_context.rs similarity index 100% rename from src/old_app/window_render_context.rs rename to src/render/window_render_context.rs From a4a6c0c60a94109b83e8ef4ef078def157336371 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 25 May 2025 19:48:59 +0200 Subject: [PATCH 61/91] Use vulkano_util instead --- Cargo.lock | 12 ++ Cargo.toml | 1 + src/main.rs | 24 ++- src/render/app.rs | 163 +++++++---------- src/render/mod.rs | 3 +- src/render/pipelines/triangle_pipeline.rs | 5 +- src/render/scene.rs | 40 +++-- src/render/vulkan_context.rs | 202 ++-------------------- src/render/window_render_context.rs | 102 ----------- 9 files changed, 137 insertions(+), 415 deletions(-) delete mode 100644 src/render/window_render_context.rs diff --git a/Cargo.lock b/Cargo.lock index 1f58cce..43de2a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,6 +1256,7 @@ dependencies = [ "thiserror 2.0.12", "vulkano", "vulkano-shaders", + "vulkano-util", "winit", ] @@ -1655,6 +1656,17 @@ dependencies = [ "vulkano", ] +[[package]] +name = "vulkano-util" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dc54fd5e14a0e01c7282f9b5d9c6e133745e1df3228b0352366e34c83bb6b" +dependencies = [ + "foldhash", + "vulkano", + "winit", +] + [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index ce36410..a79f536 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ winit = { version = "0.30", features = ["rwh_06"] } vulkano = "0.35" vulkano-shaders = "0.35" +vulkano-util = "0.35" # Math glam = { version = "0.30" } diff --git a/src/main.rs b/src/main.rs index ca3f901..4ef9dfb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,34 @@ +use vulkano::device::{DeviceExtensions, DeviceFeatures}; +use vulkano_util::context::{VulkanoConfig, VulkanoContext}; use winit::event_loop::{ControlFlow, EventLoop}; mod render; fn main() { env_logger::init(); + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..Default::default() + }; + + let device_features = DeviceFeatures { + dynamic_rendering: true, + ..Default::default() + }; + + let vulkano_config = VulkanoConfig { + print_device_name: true, + device_extensions, + device_features, + ..Default::default() + }; + + let vulkano_context = VulkanoContext::new(vulkano_config); + let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let vulkan_context = render::vulkan_context::VulkanContext::from(&event_loop); - let mut app = render::app::App::from(vulkan_context); + let mut app = render::app::App::from(vulkano_context); match event_loop.run_app(&mut app) { Ok(_) => {} diff --git a/src/render/app.rs b/src/render/app.rs index d46757e..ebcf8be 100644 --- a/src/render/app.rs +++ b/src/render/app.rs @@ -1,28 +1,32 @@ use crate::render::scene::Scene; -use crate::render::vulkan_context::VulkanContext; -use crate::render::window_render_context::WindowRenderContext; -use std::sync::Arc; -use vulkano::command_buffer::{RenderingAttachmentInfo, RenderingInfo}; +use vulkano::command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, +}; +use vulkano::pipeline::graphics::viewport::Viewport; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{SwapchainPresentInfo, acquire_next_image}; +use vulkano::swapchain::{PresentMode, SwapchainPresentInfo}; use vulkano::sync::GpuFuture; use vulkano::{Validated, VulkanError, sync}; +use vulkano_util::context::VulkanoContext; +use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; +use super::vulkan_context::VulkanContext; + pub struct App { vulkan_context: VulkanContext, - window_render_context: Option, + vulkano_windows: VulkanoWindows, scene: Option, } -impl From for App { - fn from(vulkan_context: VulkanContext) -> Self { +impl From for App { + fn from(vulkano_context: VulkanoContext) -> Self { Self { - vulkan_context, - window_render_context: None, + vulkan_context: VulkanContext::new(vulkano_context), + vulkano_windows: VulkanoWindows::default(), scene: None, } } @@ -30,26 +34,23 @@ impl From for App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window_attributes = winit::window::Window::default_attributes() - .with_title("Rust ASH Test") - .with_inner_size(winit::dpi::PhysicalSize::new( - f64::from(800), - f64::from(600), - )); + self.vulkano_windows.create_window( + event_loop, + self.vulkan_context.vulkano_context(), + &WindowDescriptor { + title: "Rust ASH Test".to_string(), + width: 800.0, + height: 600.0, + present_mode: PresentMode::Fifo, + ..Default::default() + }, + |_| {}, + ); - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - let surface = self.vulkan_context.create_surface(window.clone()); - - self.window_render_context = Some(WindowRenderContext::new( - window, - surface, - &self.vulkan_context.device, - )); self.scene = Some( Scene::load( &self.vulkan_context, - self.window_render_context.as_ref().unwrap(), + &self.vulkano_windows.get_primary_renderer_mut().unwrap(), ) .unwrap(), ); @@ -61,45 +62,27 @@ impl ApplicationHandler for App { log::debug!("The close button was pressed; stopping"); event_loop.exit(); } - WindowEvent::Resized(_) => { - let rcx = self.window_render_context.as_mut().unwrap(); - rcx.recreate_swapchain = true; - } WindowEvent::RedrawRequested => { - let (image_index, acquire_future) = { - let rcx = self.window_render_context.as_mut().unwrap(); - let window_size = rcx.window.inner_size(); + let renderer = self.vulkano_windows.get_primary_renderer_mut().unwrap(); + let acquire_future = renderer.acquire(None, |_| {}).unwrap(); - if window_size.width == 0 || window_size.height == 0 { - return; - } - - rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - rcx.update_swapchain().unwrap(); - - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - if suboptimal { - rcx.recreate_swapchain = true; - } - - (image_index, acquire_future) - }; - - let mut builder = self.vulkan_context.create_render_builder(); + let mut builder = AutoCommandBufferBuilder::primary( + self.vulkan_context.command_buffer_allocator().clone(), + self.vulkan_context + .vulkano_context() + .graphics_queue() + .queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); { - let rcx = self.window_render_context.as_ref().unwrap(); + let viewport = Viewport { + offset: [0.0, 0.0], + extent: renderer.resolution(), + depth_range: 0.0..=1.0, + }; + builder .begin_rendering(RenderingInfo { color_attachments: vec![Some(RenderingAttachmentInfo { @@ -107,23 +90,19 @@ impl ApplicationHandler for App { store_op: AttachmentStoreOp::Store, clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), ..RenderingAttachmentInfo::image_view( - rcx.attachment_image_views[image_index as usize].clone(), + renderer.swapchain_image_view().clone(), ) })], ..Default::default() }) .unwrap() - .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) + .set_viewport(0, [viewport].into_iter().collect()) .unwrap(); } if let Some(scene) = self.scene.as_ref() { scene - .render( - &self.vulkan_context, - &self.window_render_context.as_ref().unwrap(), - &mut builder, - ) + .render(&self.vulkan_context, &renderer, &mut builder) .unwrap(); } @@ -131,48 +110,24 @@ impl ApplicationHandler for App { let command_buffer = builder.build().unwrap(); - { - let rcx = self.window_render_context.as_mut().unwrap(); + let future = acquire_future + .then_execute( + self.vulkan_context + .vulkano_context() + .graphics_queue() + .clone(), + command_buffer, + ) + .unwrap(); - let future = rcx - .previous_frame_end - .take() - .unwrap() - .join(acquire_future) - .then_execute(self.vulkan_context.graphics_queue.clone(), command_buffer) - .unwrap() - .then_swapchain_present( - self.vulkan_context.graphics_queue.clone(), - SwapchainPresentInfo::swapchain_image_index( - rcx.swapchain.clone(), - image_index, - ), - ) - .then_signal_fence_and_flush(); - - match future.map_err(Validated::unwrap) { - Ok(future) => { - rcx.previous_frame_end = Some(future.boxed()); - } - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - rcx.previous_frame_end = - Some(sync::now(self.vulkan_context.device.clone()).boxed()); - } - Err(e) => { - println!("failed to flush future: {e}"); - rcx.previous_frame_end = - Some(sync::now(self.vulkan_context.device.clone()).boxed()); - } - } - } + renderer.present(future.boxed(), true); } _ => {} } } fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let rcx = self.window_render_context.as_mut().unwrap(); - rcx.window.request_redraw(); + let window = self.vulkano_windows.get_primary_window().unwrap(); + window.request_redraw(); } } diff --git a/src/render/mod.rs b/src/render/mod.rs index c1bda07..d93a2e0 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,6 +1,5 @@ pub mod app; pub mod pipelines; pub mod scene; -pub mod vulkan_context; -pub mod window_render_context; pub mod vertex; +pub mod vulkan_context; diff --git a/src/render/pipelines/triangle_pipeline.rs b/src/render/pipelines/triangle_pipeline.rs index 04aef22..b5f91b2 100644 --- a/src/render/pipelines/triangle_pipeline.rs +++ b/src/render/pipelines/triangle_pipeline.rs @@ -5,6 +5,7 @@ use vulkano::descriptor_set::layout::{ DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, }; use vulkano::device::Device; +use vulkano::format::Format; use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; @@ -40,7 +41,7 @@ pub mod shaders { pub fn create_triangle_pipeline( device: &Arc, - swapchain: &Arc, + swapchain_format: Format, ) -> Result, Box> { let (vs, fs) = load_shaders(device)?; let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; @@ -71,7 +72,7 @@ pub fn create_triangle_pipeline( let layout = PipelineLayout::new(device.clone(), create_info)?; let subpass = PipelineRenderingCreateInfo { - color_attachment_formats: vec![Some(swapchain.image_format())], + color_attachment_formats: vec![Some(swapchain_format)], ..Default::default() }; diff --git a/src/render/scene.rs b/src/render/scene.rs index 9dc7bf9..82ae981 100644 --- a/src/render/scene.rs +++ b/src/render/scene.rs @@ -6,14 +6,14 @@ use std::time::Instant; use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; -use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; +use vulkano_util::renderer::VulkanoWindowRenderer; -use crate::render::vertex::Vertex2D; use crate::render::pipelines::triangle_pipeline::create_triangle_pipeline; +use crate::render::vertex::Vertex2D; use super::vulkan_context::VulkanContext; -use super::window_render_context::WindowRenderContext; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche @@ -79,13 +79,17 @@ pub struct Scene { impl Scene { pub fn load( - vulkan_context: &VulkanContext, - window_render_context: &WindowRenderContext, + vulkano_context: &VulkanContext, + vulkano_window_renderer: &VulkanoWindowRenderer, ) -> Result> { - let pipeline = - create_triangle_pipeline(&vulkan_context.device, &window_render_context.swapchain)?; - let vertex_buffer = - Vertex2D::create_buffer(Vec::from_iter(VERTICES), &vulkan_context.memory_allocator)?; + let pipeline = create_triangle_pipeline( + &vulkano_context.vulkano_context().device(), + vulkano_window_renderer.swapchain_format(), + )?; + let vertex_buffer = Vertex2D::create_buffer( + Vec::from_iter(VERTICES), + &vulkano_context.vulkano_context().memory_allocator(), + )?; Ok(Scene { pipeline, @@ -97,16 +101,19 @@ impl Scene { pub fn render( &self, vulkan_context: &VulkanContext, - window_render_context: &WindowRenderContext, + vulkano_window_renderer: &VulkanoWindowRenderer, builder: &mut AutoCommandBufferBuilder, ) -> Result<(), Box> { let vertex_count = self.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; - let uniform_buffer = self.get_uniform_buffer(vulkan_context, window_render_context); + let uniform_buffer = self.get_uniform_buffer( + &vulkan_context.vulkano_context().memory_allocator(), + vulkano_window_renderer.aspect_ratio(), + ); let layout = &self.pipeline.layout().set_layouts()[0]; let descriptor_set = DescriptorSet::new( - vulkan_context.descriptor_set_allocator.clone(), + vulkan_context.descriptor_set_allocator().clone(), layout.clone(), [WriteDescriptorSet::buffer(0, uniform_buffer)], [], @@ -131,18 +138,15 @@ impl Scene { fn get_uniform_buffer( &self, - vulkan_context: &VulkanContext, - window_render_context: &WindowRenderContext, + memory_allocator: &Arc, + aspect_ratio: f32, ) -> Subbuffer { - let swapchain = &window_render_context.swapchain; let elapsed = self.rotation_start.elapsed(); let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; let rotation = Mat3::from_rotation_y(rotation as f32); // NOTE: This teapot was meant for OpenGL where the origin is at the lower left // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. - let aspect_ratio = swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; - let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0); let view = Mat4::look_at_rh( Vec3::new(0.3, 0.3, 1.0), @@ -158,7 +162,7 @@ impl Scene { }; Buffer::from_data( - vulkan_context.memory_allocator.clone(), + memory_allocator.clone(), BufferCreateInfo { usage: BufferUsage::UNIFORM_BUFFER, ..Default::default() diff --git a/src/render/vulkan_context.rs b/src/render/vulkan_context.rs index 332dbcf..3d91ec3 100644 --- a/src/render/vulkan_context.rs +++ b/src/render/vulkan_context.rs @@ -1,213 +1,45 @@ -use std::{any::Any, sync::Arc}; +use std::sync::Arc; use vulkano::{ - Version, VulkanLibrary, - command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, - allocator::StandardCommandBufferAllocator, - }, + command_buffer::allocator::StandardCommandBufferAllocator, descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{ - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, - QueueFlags, - physical::{PhysicalDevice, PhysicalDeviceType}, - }, - instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, - memory::allocator::StandardMemoryAllocator, - swapchain::Surface, -}; -use winit::{ - event_loop::EventLoop, - raw_window_handle::{HasDisplayHandle, HasWindowHandle}, }; +use vulkano_util::context::VulkanoContext; pub struct VulkanContext { - instance: Arc, - pub device: Arc, - pub graphics_queue: Arc, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, + vulkano_context: VulkanoContext, + command_buffer_allocator: Arc, + descriptor_set_allocator: Arc, } -impl From<&EventLoop<()>> for VulkanContext { - fn from(event_loop: &EventLoop<()>) -> Self { - let library = load_library(); - - let enabled_extensions = Surface::required_extensions(event_loop).unwrap(); - log::debug!("Surface required extensions: {enabled_extensions:?}"); - - let instance = create_instance(library.clone(), enabled_extensions); - - let (device, mut queues) = pick_graphics_device(&instance, event_loop); - let graphics_queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - +impl VulkanContext { + pub fn new(vulkano_context: VulkanoContext) -> Self { let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), + vulkano_context.device().clone(), Default::default(), )); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), + vulkano_context.device().clone(), Default::default(), )); Self { - instance, - device, - graphics_queue, - memory_allocator, + vulkano_context, command_buffer_allocator, descriptor_set_allocator, } } -} -impl VulkanContext { - pub fn create_surface( - &self, - window: Arc, - ) -> Arc { - Surface::from_window(self.instance.clone(), window).unwrap() + pub fn vulkano_context(&self) -> &VulkanoContext { + &self.vulkano_context } - pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { - AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap() - } -} - -fn load_library() -> Arc { - let library = VulkanLibrary::new().unwrap(); - - log::debug!("Available layer:"); - for layer in library.layer_properties().unwrap() { - log::debug!( - "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", - layer.name(), - layer.description(), - layer.implementation_version(), - layer.vulkan_version() - ); + pub fn command_buffer_allocator(&self) -> &Arc { + &self.command_buffer_allocator } - library -} - -fn create_instance( - library: Arc, - required_extensions: InstanceExtensions, -) -> Arc { - Instance::new( - library, - InstanceCreateInfo { - // Enable enumerating devices that use non-conformant Vulkan implementations. - // (e.g. MoltenVK) - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], - ..Default::default() - }, - ) - .unwrap() -} - -fn find_physical_device_queue_family_indexes( - physical_device: &Arc, - event_loop: &EventLoop<()>, -) -> Option { - let mut graphic_queue_family_index = None; - - for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { - if queue_family_property - .queue_flags - .intersects(QueueFlags::GRAPHICS) - && physical_device - .presentation_support(i as u32, event_loop) - .unwrap() - { - graphic_queue_family_index = Some(i as u32); - } + pub fn descriptor_set_allocator(&self) -> &Arc { + &self.descriptor_set_allocator } - - graphic_queue_family_index -} - -fn pick_physical_device_and_queue_family_indexes( - instance: &Arc, - event_loop: &EventLoop<()>, - device_extensions: &DeviceExtensions, -) -> Option<(Arc, u32)> { - instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) - .filter(|p| p.supported_extensions().contains(device_extensions)) - .filter_map(|p| { - find_physical_device_queue_family_indexes(&p, event_loop) - .and_then(|indexes| Some((p, indexes))) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) -} - -fn pick_graphics_device( - instance: &Arc, - event_loop: &EventLoop<()>, -) -> ( - Arc, - impl ExactSizeIterator> + use<>, -) { - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, graphics_family_index) = - pick_physical_device_and_queue_family_indexes(instance, event_loop, &device_extensions) - .unwrap(); - - log::debug!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - if physical_device.api_version() < Version::V1_3 { - device_extensions.khr_dynamic_rendering = true; - } - - log::debug!("Using device extensions: {:#?}", device_extensions); - - Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index: graphics_family_index, - ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - ..Default::default() - }, - ) - .unwrap() } diff --git a/src/render/window_render_context.rs b/src/render/window_render_context.rs deleted file mode 100644 index 54120d0..0000000 --- a/src/render/window_render_context.rs +++ /dev/null @@ -1,102 +0,0 @@ -use std::sync::Arc; -use vulkano::device::Device; -use vulkano::image::view::ImageView; -use vulkano::image::{Image, ImageUsage}; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; -use vulkano::sync::GpuFuture; -use vulkano::{Validated, VulkanError, sync}; -use winit::window::Window; - -pub struct WindowRenderContext { - pub window: Arc, - pub swapchain: Arc, - pub attachment_image_views: Vec>, - pub viewport: Viewport, - pub recreate_swapchain: bool, - pub previous_frame_end: Option>, -} - -impl WindowRenderContext { - pub fn new(window: Arc, surface: Arc, device: &Arc) -> Self { - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - - let (image_format, _) = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - - ..Default::default() - }, - ) - .unwrap() - }; - - let attachment_image_views = window_size_dependent_setup(&images); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let recreate_swapchain = false; - let previous_frame_end = Some(sync::now(device.clone()).boxed()); - - Self { - window, - swapchain, - attachment_image_views, - viewport, - recreate_swapchain, - previous_frame_end, - } - } - - pub fn update_swapchain(&mut self) -> Result<(), Validated> { - if !self.recreate_swapchain { - return Ok(()); - } - - let window_size = self.window.inner_size(); - let (new_swapchain, new_images) = self.swapchain.recreate(SwapchainCreateInfo { - image_extent: window_size.into(), - ..self.swapchain.create_info() - })?; - - self.swapchain = new_swapchain; - self.attachment_image_views = window_size_dependent_setup(&new_images); - self.viewport.extent = window_size.into(); - self.recreate_swapchain = false; - - Ok(()) - } -} - -fn window_size_dependent_setup(images: &[Arc]) -> Vec> { - images - .iter() - .map(|image| ImageView::new_default(image.clone()).unwrap()) - .collect::>() -} From 6dae0339dbfd97257941ef5b79f81f2ec7a17254 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 25 May 2025 21:10:58 +0200 Subject: [PATCH 62/91] Fix warning --- src/render/app.rs | 7 +++---- src/render/pipelines/triangle_pipeline.rs | 1 - src/render/scene.rs | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/render/app.rs b/src/render/app.rs index ebcf8be..69db339 100644 --- a/src/render/app.rs +++ b/src/render/app.rs @@ -4,9 +4,8 @@ use vulkano::command_buffer::{ }; use vulkano::pipeline::graphics::viewport::Viewport; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{PresentMode, SwapchainPresentInfo}; +use vulkano::swapchain::PresentMode; use vulkano::sync::GpuFuture; -use vulkano::{Validated, VulkanError, sync}; use vulkano_util::context::VulkanoContext; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; @@ -50,7 +49,7 @@ impl ApplicationHandler for App { self.scene = Some( Scene::load( &self.vulkan_context, - &self.vulkano_windows.get_primary_renderer_mut().unwrap(), + self.vulkano_windows.get_primary_renderer_mut().unwrap(), ) .unwrap(), ); @@ -102,7 +101,7 @@ impl ApplicationHandler for App { if let Some(scene) = self.scene.as_ref() { scene - .render(&self.vulkan_context, &renderer, &mut builder) + .render(&self.vulkan_context, renderer, &mut builder) .unwrap(); } diff --git a/src/render/pipelines/triangle_pipeline.rs b/src/render/pipelines/triangle_pipeline.rs index b5f91b2..9fb3629 100644 --- a/src/render/pipelines/triangle_pipeline.rs +++ b/src/render/pipelines/triangle_pipeline.rs @@ -19,7 +19,6 @@ use vulkano::pipeline::{ DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, }; use vulkano::shader::{EntryPoint, ShaderStages}; -use vulkano::swapchain::Swapchain; use crate::render::vertex::Vertex2D; diff --git a/src/render/scene.rs b/src/render/scene.rs index 82ae981..36ba182 100644 --- a/src/render/scene.rs +++ b/src/render/scene.rs @@ -83,12 +83,12 @@ impl Scene { vulkano_window_renderer: &VulkanoWindowRenderer, ) -> Result> { let pipeline = create_triangle_pipeline( - &vulkano_context.vulkano_context().device(), + vulkano_context.vulkano_context().device(), vulkano_window_renderer.swapchain_format(), )?; let vertex_buffer = Vertex2D::create_buffer( Vec::from_iter(VERTICES), - &vulkano_context.vulkano_context().memory_allocator(), + vulkano_context.vulkano_context().memory_allocator(), )?; Ok(Scene { @@ -108,7 +108,7 @@ impl Scene { let instance_count = vertex_count / 3; let uniform_buffer = self.get_uniform_buffer( - &vulkan_context.vulkano_context().memory_allocator(), + vulkan_context.vulkano_context().memory_allocator(), vulkano_window_renderer.aspect_ratio(), ); let layout = &self.pipeline.layout().set_layouts()[0]; From 2c3392c3ea15f53c5d9932630e55116ab7ed791f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 25 May 2025 22:36:27 +0200 Subject: [PATCH 63/91] add EGUI --- Cargo.lock | 1158 ++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- src/render/app.rs | 58 ++- 3 files changed, 1201 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43de2a6..d461d0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +[[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.12" @@ -25,7 +31,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.3.3", "once_cell", "version_check", "zerocopy", @@ -40,6 +46,12 @@ dependencies = [ "memchr", ] +[[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.6.0" @@ -123,6 +135,43 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arboard" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", + "parking_lot", + "percent-encoding", + "windows-sys 0.59.0", + "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" @@ -162,6 +211,35 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "av1-grain" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +dependencies = [ + "arrayvec", +] + +[[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" @@ -174,6 +252,12 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + [[package]] name = "block2" version = "0.5.1" @@ -183,6 +267,12 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + [[package]] name = "bumpalo" version = "3.17.0" @@ -209,6 +299,12 @@ dependencies = [ "syn", ] +[[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.10.1" @@ -258,6 +354,16 @@ 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" @@ -270,6 +376,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[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.54" @@ -279,6 +394,12 @@ dependencies = [ "cc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.3" @@ -314,6 +435,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -327,7 +458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "core-graphics-types", "foreign-types", "libc", @@ -340,10 +471,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "libc", ] +[[package]] +name = "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-queue" version = "0.3.12" @@ -387,6 +546,17 @@ dependencies = [ "objc2 0.6.1", ] +[[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" @@ -408,6 +578,80 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "ecolor" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc4feb366740ded31a004a0e4452fbf84e80ef432ecf8314c485210229672fd1" +dependencies = [ + "bytemuck", + "emath", +] + +[[package]] +name = "egui" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd34cec49ab55d85ebf70139cb1ccd29c977ef6b6ba4fe85489d6877ee9ef3" +dependencies = [ + "ahash", + "bitflags 2.9.1", + "emath", + "epaint", + "log", + "nohash-hasher", + "profiling", +] + +[[package]] +name = "egui-winit" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d9dfbb78fe4eb9c3a39ad528b90ee5915c252e77bbab9d4ebc576541ab67e13" +dependencies = [ + "ahash", + "arboard", + "bytemuck", + "egui", + "log", + "profiling", + "raw-window-handle", + "smithay-clipboard", + "web-time", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_winit_vulkano" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6abd5e69939cd416853fc3ec69c0e08721b175d03b508d5574849885c7e85a6" +dependencies = [ + "ahash", + "egui", + "egui-winit", + "image", + "vulkano", + "vulkano-shaders", + "winit", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "emath" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e4cadcff7a5353ba72b7fea76bf2122b5ebdbc68e8155aa56dfdea90083fe1b" +dependencies = [ + "bytemuck", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -431,6 +675,30 @@ dependencies = [ "log", ] +[[package]] +name = "epaint" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fcc0f5a7c613afd2dee5e4b30c3e6acafb8ad6f0edb06068811f708a67c562" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "epaint_default_fonts", + "log", + "nohash-hasher", + "parking_lot", + "profiling", +] + +[[package]] +name = "epaint_default_fonts" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7e7a64c02cf7a5b51e745a9e45f60660a286f151c238b9d397b3e923f5082f" + [[package]] name = "equivalent" version = "1.0.2" @@ -447,6 +715,46 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[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", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[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.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -480,6 +788,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -490,6 +807,17 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.3.3" @@ -499,7 +827,17 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", ] [[package]] @@ -531,12 +869,173 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[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 = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +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 = "indexmap" version = "2.9.0" @@ -547,12 +1046,32 @@ dependencies = [ "hashbrown", ] +[[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 = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[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.15" @@ -611,10 +1130,16 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom", + "getrandom 0.3.3", "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.77" @@ -625,12 +1150,28 @@ dependencies = [ "wasm-bindgen", ] +[[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.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "libfuzzer-sys" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libloading" version = "0.8.7" @@ -658,6 +1199,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" version = "0.4.12" @@ -674,6 +1221,25 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[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" @@ -695,6 +1261,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "ndk" version = "0.9.0" @@ -725,6 +1301,18 @@ 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" @@ -735,6 +1323,62 @@ dependencies = [ "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 = "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-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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_enum" version = "0.7.3" @@ -797,6 +1441,18 @@ dependencies = [ "objc2-quartz-core 0.2.2", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-core-graphics", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -844,6 +1500,19 @@ dependencies = [ "objc2 0.6.1", ] +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.1", + "dispatch2", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-io-surface", +] + [[package]] name = "objc2-core-image" version = "0.2.2" @@ -898,6 +1567,17 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-core-foundation", +] + [[package]] name = "objc2-link-presentation" version = "0.2.2" @@ -906,7 +1586,7 @@ checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ "block2", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", ] @@ -1061,6 +1741,12 @@ dependencies = [ "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" @@ -1099,6 +1785,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.7.4" @@ -1129,6 +1828,24 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -1147,6 +1864,40 @@ 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 = "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.37.5" @@ -1171,6 +1922,86 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[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 0.2.16", +] + +[[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 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -1189,6 +2020,26 @@ dependencies = [ "objc2-quartz-core 0.3.1", ] +[[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.4.1" @@ -1236,6 +2087,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[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" @@ -1250,6 +2107,7 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", + "egui_winit_vulkano", "env_logger", "glam", "log", @@ -1351,6 +2209,15 @@ dependencies = [ "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" @@ -1378,6 +2245,21 @@ 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" @@ -1424,6 +2306,17 @@ dependencies = [ "xkeysym", ] +[[package]] +name = "smithay-clipboard" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" +dependencies = [ + "libc", + "smithay-client-toolkit", + "wayland-backend", +] + [[package]] name = "smol_str" version = "0.2.2" @@ -1433,6 +2326,12 @@ dependencies = [ "serde", ] +[[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" @@ -1450,6 +2349,36 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +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 0.5.0", + "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" @@ -1500,6 +2429,17 @@ 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 = "tiny-skia" version = "0.11.4" @@ -1525,11 +2465,36 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -1538,6 +2503,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -1576,12 +2543,46 @@ 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 = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[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 = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -1608,7 +2609,7 @@ dependencies = [ "crossbeam-queue", "foldhash", "half", - "heck", + "heck 0.4.1", "indexmap", "libloading", "nom", @@ -1648,7 +2649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf501461be7cef2893c0e62c50945add9763cc482051d29053f6157089d5ea9" dependencies = [ "foldhash", - "heck", + "heck 0.4.1", "proc-macro2", "quote", "shaderc", @@ -1677,6 +2678,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" @@ -1886,6 +2893,29 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webbrowser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5df295f8451142f1856b1bd86a606dfe9587d439bc036e319c827700dbd555e" +dependencies = [ + "core-foundation 0.10.0", + "home", + "jni", + "log", + "ndk-context", + "objc2 0.6.1", + "objc2-foundation 0.3.1", + "url", + "web-sys", +] + +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + [[package]] name = "winapi-util" version = "0.1.9" @@ -2115,7 +3145,7 @@ dependencies = [ "calloop", "cfg_aliases", "concurrent-queue", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "cursor-icon", "dpi", @@ -2124,7 +3154,7 @@ dependencies = [ "memmap2", "ndk", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", @@ -2170,6 +3200,12 @@ dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "x11-dl" version = "2.21.0" @@ -2239,6 +3275,30 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.25" @@ -2258,3 +3318,81 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +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 a79f536..372b1a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,11 +13,11 @@ winit = { version = "0.30", features = ["rwh_06"] } vulkano = "0.35" vulkano-shaders = "0.35" vulkano-util = "0.35" +egui_winit_vulkano = { version = "0.28" } # Math glam = { version = "0.30" } - # Log and tracing log = "0.4" env_logger = "0.11" diff --git a/src/render/app.rs b/src/render/app.rs index 69db339..ee33768 100644 --- a/src/render/app.rs +++ b/src/render/app.rs @@ -1,4 +1,7 @@ +use std::collections::HashMap; + use crate::render::scene::Scene; +use egui_winit_vulkano::{Gui, GuiConfig, egui}; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, }; @@ -18,7 +21,9 @@ use super::vulkan_context::VulkanContext; pub struct App { vulkan_context: VulkanContext, vulkano_windows: VulkanoWindows, + gui: HashMap, scene: Option, + clear_color: [f32; 3], } impl From for App { @@ -26,14 +31,16 @@ impl From for App { Self { vulkan_context: VulkanContext::new(vulkano_context), vulkano_windows: VulkanoWindows::default(), + gui: HashMap::new(), scene: None, + clear_color: [0.0, 0.0, 0.0], } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - self.vulkano_windows.create_window( + let window_id = self.vulkano_windows.create_window( event_loop, self.vulkan_context.vulkano_context(), &WindowDescriptor { @@ -46,6 +53,21 @@ impl ApplicationHandler for App { |_| {}, ); + let gui = { + let renderer = self.vulkano_windows.get_renderer_mut(window_id).unwrap(); + Gui::new( + event_loop, + renderer.surface(), + renderer.graphics_queue(), + renderer.swapchain_format(), + GuiConfig { + is_overlay: true, + ..Default::default() + }, + ) + }; + self.gui.insert(window_id, gui); + self.scene = Some( Scene::load( &self.vulkan_context, @@ -55,14 +77,23 @@ impl ApplicationHandler for App { ); } - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { + let renderer = self.vulkano_windows.get_renderer_mut(id).unwrap(); + let gui = self.gui.get_mut(&id).unwrap(); + gui.update(&event); + match event { WindowEvent::CloseRequested => { log::debug!("The close button was pressed; stopping"); event_loop.exit(); } + WindowEvent::Resized(_) => { + renderer.resize(); + } + WindowEvent::ScaleFactorChanged { .. } => { + renderer.resize(); + } WindowEvent::RedrawRequested => { - let renderer = self.vulkano_windows.get_primary_renderer_mut().unwrap(); let acquire_future = renderer.acquire(None, |_| {}).unwrap(); let mut builder = AutoCommandBufferBuilder::primary( @@ -87,7 +118,7 @@ impl ApplicationHandler for App { color_attachments: vec![Some(RenderingAttachmentInfo { load_op: AttachmentLoadOp::Clear, store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + clear_value: Some(self.clear_color.into()), ..RenderingAttachmentInfo::image_view( renderer.swapchain_image_view().clone(), ) @@ -109,7 +140,7 @@ impl ApplicationHandler for App { let command_buffer = builder.build().unwrap(); - let future = acquire_future + let render_future = acquire_future .then_execute( self.vulkan_context .vulkano_context() @@ -119,7 +150,22 @@ impl ApplicationHandler for App { ) .unwrap(); - renderer.present(future.boxed(), true); + gui.immediate_ui(|gui| { + let ctx = gui.context(); + + egui::Window::new("Informations") + .vscroll(true) + .show(&ctx, |ui| { + ui.label(format!("Format: {:?}", renderer.swapchain_format())); + ui.label(format!("Resolution: {:?}", renderer.resolution())); + ui.color_edit_button_rgb(&mut self.clear_color); + }); + }); + + let render_future = + gui.draw_on_image(render_future, renderer.swapchain_image_view()); + + renderer.present(render_future.boxed(), true); } _ => {} } From c4c691c4dde0ec5fa4059287bc58ba328e11a37d Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 25 May 2025 23:48:02 +0200 Subject: [PATCH 64/91] Begin implement input management --- src/render/app.rs | 10 ++++- src/render/input.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++ src/render/mod.rs | 1 + 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/render/input.rs diff --git a/src/render/app.rs b/src/render/app.rs index ee33768..bbd4a22 100644 --- a/src/render/app.rs +++ b/src/render/app.rs @@ -16,6 +16,7 @@ use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; +use super::input::InputState; use super::vulkan_context::VulkanContext; pub struct App { @@ -24,6 +25,7 @@ pub struct App { gui: HashMap, scene: Option, clear_color: [f32; 3], + input_state: InputState, } impl From for App { @@ -34,6 +36,7 @@ impl From for App { gui: HashMap::new(), scene: None, clear_color: [0.0, 0.0, 0.0], + input_state: InputState::default(), } } } @@ -80,7 +83,10 @@ impl ApplicationHandler for App { fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { let renderer = self.vulkano_windows.get_renderer_mut(id).unwrap(); let gui = self.gui.get_mut(&id).unwrap(); - gui.update(&event); + + if !gui.update(&event) { + self.input_state.process_event(&event); + } match event { WindowEvent::CloseRequested => { @@ -94,6 +100,8 @@ impl ApplicationHandler for App { renderer.resize(); } WindowEvent::RedrawRequested => { + self.input_state.update(); + let acquire_future = renderer.acquire(None, |_| {}).unwrap(); let mut builder = AutoCommandBufferBuilder::primary( diff --git a/src/render/input.rs b/src/render/input.rs new file mode 100644 index 0000000..2b1075e --- /dev/null +++ b/src/render/input.rs @@ -0,0 +1,89 @@ +use std::collections::HashMap; + +use winit::{ + dpi::PhysicalPosition, + event::{ElementState, KeyEvent, WindowEvent}, + keyboard::PhysicalKey, +}; + +#[derive(Debug, Default)] +pub enum KeyState { + #[default] + Pressed, + Released, + Held, +} + +#[derive(Debug, Default)] +pub struct MouseState { + old_position: glam::Vec2, + pub position: glam::Vec2, + pub delta: glam::Vec2, +} + +#[derive(Debug, Default)] +pub struct InputState { + pub key_states: HashMap, + pub mouse_state: MouseState, +} + +impl InputState { + pub fn process_event(&mut self, event: &WindowEvent) { + match event { + WindowEvent::KeyboardInput { event, .. } => { + self.update_key_state(event.physical_key, event); + } + WindowEvent::CursorMoved { position, .. } => { + self.update_mouse_position(position); + } + _ => {} + } + } + + /// Updates deltas before running update + pub fn update(&mut self) { + self.mouse_state.delta = self.mouse_state.position - self.mouse_state.old_position; + self.mouse_state.old_position = self.mouse_state.position; + } + + pub fn get_key_state(&self, key: PhysicalKey) -> &KeyState { + self.key_states.get(&key).unwrap_or(&KeyState::Released) + } + + pub fn get_mouse_state(&self) -> &MouseState { + &self.mouse_state + } + + fn update_key_state(&mut self, key: PhysicalKey, event: &KeyEvent) { + let key_state = self.key_states.get(&event.physical_key); + let new_key_state = match key_state { + Some(key_state) => match event.state { + ElementState::Pressed => match key_state { + KeyState::Released => Some(KeyState::Pressed), + KeyState::Pressed => Some(KeyState::Held), + KeyState::Held => None, + }, + ElementState::Released => match key_state { + KeyState::Released => None, + _ => Some(KeyState::Released), + }, + }, + None => match event.state { + ElementState::Pressed => Some(KeyState::Pressed), + ElementState::Released => Some(KeyState::Released), + }, + }; + if let Some(new_key_state) = new_key_state { + log::trace!( + "New key state {:?} for key {:?}", + new_key_state, + event.physical_key + ); + self.key_states.insert(key, new_key_state); + } + } + + fn update_mouse_position(&mut self, position: &PhysicalPosition) { + self.mouse_state.position = glam::Vec2::new(position.x as f32, position.y as f32); + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs index d93a2e0..164a07e 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,4 +1,5 @@ pub mod app; +pub mod input; pub mod pipelines; pub mod scene; pub mod vertex; From 1babc5bfebb97eaa7d85aa4f7ef005d4597d3814 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 26 May 2025 00:04:46 +0200 Subject: [PATCH 65/91] Add debug in gui --- src/render/app.rs | 20 ++++++++++++++++++-- src/render/input.rs | 7 +------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/render/app.rs b/src/render/app.rs index bbd4a22..5515e97 100644 --- a/src/render/app.rs +++ b/src/render/app.rs @@ -14,9 +14,10 @@ use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; +use winit::keyboard::{KeyCode, PhysicalKey}; use winit::window::WindowId; -use super::input::InputState; +use super::input::{InputState, KeyState}; use super::vulkan_context::VulkanContext; pub struct App { @@ -164,9 +165,24 @@ impl ApplicationHandler for App { egui::Window::new("Informations") .vscroll(true) .show(&ctx, |ui| { - ui.label(format!("Format: {:?}", renderer.swapchain_format())); ui.label(format!("Resolution: {:?}", renderer.resolution())); ui.color_edit_button_rgb(&mut self.clear_color); + ui.label(format!( + "Mouse position: {:?}", + self.input_state.get_mouse_state().position + )); + ui.label(format!( + "Mouse delta: {:?}", + self.input_state.get_mouse_state().delta + )); + + for (key, state) in + self.input_state.key_states.iter().filter(|(_, state)| { + *state == &KeyState::Pressed || *state == &KeyState::Held + }) + { + ui.label(format!("{:?} State: {:?}", key, state)); + } }); }); diff --git a/src/render/input.rs b/src/render/input.rs index 2b1075e..04ca813 100644 --- a/src/render/input.rs +++ b/src/render/input.rs @@ -6,7 +6,7 @@ use winit::{ keyboard::PhysicalKey, }; -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub enum KeyState { #[default] Pressed, @@ -74,11 +74,6 @@ impl InputState { }, }; if let Some(new_key_state) = new_key_state { - log::trace!( - "New key state {:?} for key {:?}", - new_key_state, - event.physical_key - ); self.key_states.insert(key, new_key_state); } } From e58c22b531d3d096cb5b2dcc22706bba73a92e0b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 26 May 2025 13:29:38 +0200 Subject: [PATCH 66/91] remove held state in input. - Winit not return event if multiple key as repeat event --- src/render/app.rs | 14 +++++++------- src/render/input.rs | 27 +++++++++------------------ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/render/app.rs b/src/render/app.rs index 5515e97..ee88731 100644 --- a/src/render/app.rs +++ b/src/render/app.rs @@ -12,12 +12,11 @@ use vulkano::sync::GpuFuture; use vulkano_util::context::VulkanoContext; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; -use winit::event::WindowEvent; +use winit::event::{ElementState, WindowEvent}; use winit::event_loop::ActiveEventLoop; -use winit::keyboard::{KeyCode, PhysicalKey}; use winit::window::WindowId; -use super::input::{InputState, KeyState}; +use super::input::InputState; use super::vulkan_context::VulkanContext; pub struct App { @@ -176,10 +175,11 @@ impl ApplicationHandler for App { self.input_state.get_mouse_state().delta )); - for (key, state) in - self.input_state.key_states.iter().filter(|(_, state)| { - *state == &KeyState::Pressed || *state == &KeyState::Held - }) + for (key, state) in self + .input_state + .key_states + .iter() + .filter(|(_, state)| *state == &ElementState::Pressed) { ui.label(format!("{:?} State: {:?}", key, state)); } diff --git a/src/render/input.rs b/src/render/input.rs index 04ca813..59e8398 100644 --- a/src/render/input.rs +++ b/src/render/input.rs @@ -6,14 +6,6 @@ use winit::{ keyboard::PhysicalKey, }; -#[derive(Debug, Default, PartialEq)] -pub enum KeyState { - #[default] - Pressed, - Released, - Held, -} - #[derive(Debug, Default)] pub struct MouseState { old_position: glam::Vec2, @@ -23,7 +15,7 @@ pub struct MouseState { #[derive(Debug, Default)] pub struct InputState { - pub key_states: HashMap, + pub key_states: HashMap, pub mouse_state: MouseState, } @@ -46,8 +38,8 @@ impl InputState { self.mouse_state.old_position = self.mouse_state.position; } - pub fn get_key_state(&self, key: PhysicalKey) -> &KeyState { - self.key_states.get(&key).unwrap_or(&KeyState::Released) + pub fn get_key_state(&self, key: PhysicalKey) -> &ElementState { + self.key_states.get(&key).unwrap_or(&ElementState::Released) } pub fn get_mouse_state(&self) -> &MouseState { @@ -59,18 +51,17 @@ impl InputState { let new_key_state = match key_state { Some(key_state) => match event.state { ElementState::Pressed => match key_state { - KeyState::Released => Some(KeyState::Pressed), - KeyState::Pressed => Some(KeyState::Held), - KeyState::Held => None, + ElementState::Released => Some(ElementState::Pressed), + ElementState::Pressed => None, }, ElementState::Released => match key_state { - KeyState::Released => None, - _ => Some(KeyState::Released), + ElementState::Released => None, + ElementState::Pressed => Some(ElementState::Released), }, }, None => match event.state { - ElementState::Pressed => Some(KeyState::Pressed), - ElementState::Released => Some(KeyState::Released), + ElementState::Pressed => Some(ElementState::Pressed), + ElementState::Released => Some(ElementState::Released), }, }; if let Some(new_key_state) = new_key_state { From 5b74eef561bb13edad6b9c3436dbea938c5058b4 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 26 May 2025 16:46:26 +0200 Subject: [PATCH 67/91] Push break job work [not work] --- src/{render => core}/input.rs | 0 src/core/mod.rs | 4 ++ src/{ => core}/render/app.rs | 21 +++++----- src/{ => core}/render/mod.rs | 2 - src/{ => core}/render/pipelines/mod.rs | 0 .../render/pipelines/triangle_pipeline.rs | 2 +- src/{ => core}/render/vertex.rs | 0 src/{ => core}/render/vulkan_context.rs | 0 src/core/scene.rs | 29 +++++++++++++ src/core/timer.rs | 30 ++++++++++++++ src/{render/scene.rs => game/main_scene.rs} | 41 ++++++++++++++++++- src/game/mod.rs | 1 + src/main.rs | 6 ++- 13 files changed, 118 insertions(+), 18 deletions(-) rename src/{render => core}/input.rs (100%) create mode 100644 src/core/mod.rs rename src/{ => core}/render/app.rs (95%) rename src/{ => core}/render/mod.rs (70%) rename src/{ => core}/render/pipelines/mod.rs (100%) rename src/{ => core}/render/pipelines/triangle_pipeline.rs (98%) rename src/{ => core}/render/vertex.rs (100%) rename src/{ => core}/render/vulkan_context.rs (100%) create mode 100644 src/core/scene.rs create mode 100644 src/core/timer.rs rename src/{render/scene.rs => game/main_scene.rs} (86%) create mode 100644 src/game/mod.rs diff --git a/src/render/input.rs b/src/core/input.rs similarity index 100% rename from src/render/input.rs rename to src/core/input.rs diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..ddaf320 --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,4 @@ +pub mod input; +pub mod render; +pub mod scene; +pub mod timer; diff --git a/src/render/app.rs b/src/core/render/app.rs similarity index 95% rename from src/render/app.rs rename to src/core/render/app.rs index ee88731..9553a5a 100644 --- a/src/render/app.rs +++ b/src/core/render/app.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; -use crate::render::scene::Scene; +use crate::core::input::InputState; +use crate::core::scene::SceneManager; +use crate::core::timer::Timer; +use crate::game::main_scene::MainScene; use egui_winit_vulkano::{Gui, GuiConfig, egui}; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, @@ -16,16 +19,16 @@ use winit::event::{ElementState, WindowEvent}; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; -use super::input::InputState; use super::vulkan_context::VulkanContext; pub struct App { vulkan_context: VulkanContext, vulkano_windows: VulkanoWindows, gui: HashMap, - scene: Option, clear_color: [f32; 3], input_state: InputState, + scene_manager: SceneManager, + timer: Timer, } impl From for App { @@ -34,9 +37,10 @@ impl From for App { vulkan_context: VulkanContext::new(vulkano_context), vulkano_windows: VulkanoWindows::default(), gui: HashMap::new(), - scene: None, clear_color: [0.0, 0.0, 0.0], input_state: InputState::default(), + scene_manager: SceneManager::new(), + timer: Timer::new(), } } } @@ -71,13 +75,8 @@ impl ApplicationHandler for App { }; self.gui.insert(window_id, gui); - self.scene = Some( - Scene::load( - &self.vulkan_context, - self.vulkano_windows.get_primary_renderer_mut().unwrap(), - ) - .unwrap(), - ); + self.scene_manager + .load_scene(Box::new(MainScene::default())); } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { diff --git a/src/render/mod.rs b/src/core/render/mod.rs similarity index 70% rename from src/render/mod.rs rename to src/core/render/mod.rs index 164a07e..d98c419 100644 --- a/src/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,6 +1,4 @@ pub mod app; -pub mod input; pub mod pipelines; -pub mod scene; pub mod vertex; pub mod vulkan_context; diff --git a/src/render/pipelines/mod.rs b/src/core/render/pipelines/mod.rs similarity index 100% rename from src/render/pipelines/mod.rs rename to src/core/render/pipelines/mod.rs diff --git a/src/render/pipelines/triangle_pipeline.rs b/src/core/render/pipelines/triangle_pipeline.rs similarity index 98% rename from src/render/pipelines/triangle_pipeline.rs rename to src/core/render/pipelines/triangle_pipeline.rs index 9fb3629..62bd8b0 100644 --- a/src/render/pipelines/triangle_pipeline.rs +++ b/src/core/render/pipelines/triangle_pipeline.rs @@ -20,7 +20,7 @@ use vulkano::pipeline::{ }; use vulkano::shader::{EntryPoint, ShaderStages}; -use crate::render::vertex::Vertex2D; +use crate::core::render::vertex::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/render/vertex.rs b/src/core/render/vertex.rs similarity index 100% rename from src/render/vertex.rs rename to src/core/render/vertex.rs diff --git a/src/render/vulkan_context.rs b/src/core/render/vulkan_context.rs similarity index 100% rename from src/render/vulkan_context.rs rename to src/core/render/vulkan_context.rs diff --git a/src/core/scene.rs b/src/core/scene.rs new file mode 100644 index 0000000..2286820 --- /dev/null +++ b/src/core/scene.rs @@ -0,0 +1,29 @@ +use vulkano_util::renderer::VulkanoWindowRenderer; + +use super::{input::InputState, render::vulkan_context::VulkanContext, timer::Timer}; + +pub trait Scene { + fn load(&mut self, app: &mut App); + fn update(&mut self, app: &mut App); + fn render(&self); + fn unload(&mut self); +} + +pub struct SceneManager { + current_scene: Option>, +} + +impl SceneManager { + pub fn new() -> Self { + Self { + current_scene: None, + } + } + + pub fn load_scene(&mut self, scene: Box) { + if let Some(current_scene) = self.current_scene.as_mut() { + current_scene.unload(); + } + self.current_scene = Some(scene); + } +} diff --git a/src/core/timer.rs b/src/core/timer.rs new file mode 100644 index 0000000..eacf423 --- /dev/null +++ b/src/core/timer.rs @@ -0,0 +1,30 @@ +pub struct Timer { + start_time: std::time::Instant, + last_time: std::time::Instant, + current_time: std::time::Instant, + delta_time: f32, +} + +impl Timer { + pub fn new() -> Self { + Self { + start_time: std::time::Instant::now(), + last_time: std::time::Instant::now(), + current_time: std::time::Instant::now(), + delta_time: 0.0, + } + } + + pub fn update(&mut self) { + self.current_time = std::time::Instant::now(); + self.delta_time = self + .current_time + .duration_since(self.last_time) + .as_secs_f32(); + self.last_time = self.current_time; + } + + pub fn get_delta_time(&self) -> f32 { + self.delta_time + } +} diff --git a/src/render/scene.rs b/src/game/main_scene.rs similarity index 86% rename from src/render/scene.rs rename to src/game/main_scene.rs index 36ba182..4135c38 100644 --- a/src/render/scene.rs +++ b/src/game/main_scene.rs @@ -1,3 +1,4 @@ +use crate::core::scene::Scene; use crate::render::pipelines::triangle_pipeline::shaders::vs; use glam::{Mat3, Mat4, Vec3}; use std::error::Error; @@ -70,14 +71,50 @@ const VERTICES: [Vertex2D; 12] = [ }, ]; -pub struct Scene { +pub struct MainSceneState { pipeline: Arc, vertex_buffer: Subbuffer<[Vertex2D]>, rotation_start: Instant, } -impl Scene { +#[derive(Default)] +pub struct MainScene { + state: Option, +} + +impl Scene for MainScene { + fn load(&mut self, app: &mut App) { + let pipeline = create_triangle_pipeline( + app.vulkan_context.vulkano_context().device(), + app.vulkano_windows.swapchain_format(), + )?; + let vertex_buffer = Vertex2D::create_buffer( + Vec::from_iter(VERTICES), + vulkano_context.vulkano_context().memory_allocator(), + )?; + + self.state = Some(MainSceneState { + pipeline, + vertex_buffer, + rotation_start: Instant::now(), + }) + } + + fn update(&mut self, app: &mut App) { + todo!() + } + + fn render(&self) { + todo!() + } + + fn unload(&mut self) { + todo!() + } +} + +impl MainScene { pub fn load( vulkano_context: &VulkanContext, vulkano_window_renderer: &VulkanoWindowRenderer, diff --git a/src/game/mod.rs b/src/game/mod.rs new file mode 100644 index 0000000..53eb070 --- /dev/null +++ b/src/game/mod.rs @@ -0,0 +1 @@ +pub mod main_scene; diff --git a/src/main.rs b/src/main.rs index 4ef9dfb..52ba1f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,9 @@ use vulkano::device::{DeviceExtensions, DeviceFeatures}; use vulkano_util::context::{VulkanoConfig, VulkanoContext}; use winit::event_loop::{ControlFlow, EventLoop}; -mod render; +mod core; +mod game; + fn main() { env_logger::init(); @@ -28,7 +30,7 @@ fn main() { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let mut app = render::app::App::from(vulkano_context); + let mut app = core::render::app::App::from(vulkano_context); match event_loop.run_app(&mut app) { Ok(_) => {} From 7401a9b5f3ae0375f53a45b89a663cb66222af25 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 26 May 2025 19:55:34 +0200 Subject: [PATCH 68/91] First scene refactor working --- src/core/render/app.rs | 98 +++++++++++++------- src/core/render/mod.rs | 1 + src/core/render/render_context.rs | 70 +++++++++++++++ src/core/scene.rs | 41 +++++++-- src/core/timer.rs | 6 +- src/game/main_scene.rs | 145 +++++++++++++++--------------- 6 files changed, 251 insertions(+), 110 deletions(-) create mode 100644 src/core/render/render_context.rs diff --git a/src/core/render/app.rs b/src/core/render/app.rs index 9553a5a..6e08f72 100644 --- a/src/core/render/app.rs +++ b/src/core/render/app.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::sync::Arc; use crate::core::input::InputState; use crate::core::scene::SceneManager; @@ -13,17 +14,19 @@ use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; use vulkano::swapchain::PresentMode; use vulkano::sync::GpuFuture; use vulkano_util::context::VulkanoContext; +use vulkano_util::renderer::VulkanoWindowRenderer; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; use winit::event::{ElementState, WindowEvent}; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; +use super::render_context::RenderContext; use super::vulkan_context::VulkanContext; pub struct App { - vulkan_context: VulkanContext, - vulkano_windows: VulkanoWindows, + vulkan_context: Arc, + vulkano_windows: Arc, gui: HashMap, clear_color: [f32; 3], input_state: InputState, @@ -31,11 +34,29 @@ pub struct App { timer: Timer, } +impl From<(&VulkanContext, &VulkanoWindowRenderer)> for RenderContext { + fn from((vulkan_context, renderer): (&VulkanContext, &VulkanoWindowRenderer)) -> Self { + RenderContext { + window_size: renderer.resolution(), + aspect_ratio: renderer.aspect_ratio(), + instance: vulkan_context.vulkano_context().instance().clone(), + device: vulkan_context.vulkano_context().device().clone(), + graphics_queue: vulkan_context.vulkano_context().graphics_queue().clone(), + compute_queue: vulkan_context.vulkano_context().compute_queue().clone(), + transfer_queue: vulkan_context.vulkano_context().transfer_queue().cloned(), + memory_allocator: vulkan_context.vulkano_context().memory_allocator().clone(), + command_buffer_allocator: vulkan_context.command_buffer_allocator().clone(), + descriptor_set_allocator: vulkan_context.descriptor_set_allocator().clone(), + swapchain_format: renderer.swapchain_format(), + } + } +} + impl From for App { fn from(vulkano_context: VulkanoContext) -> Self { Self { - vulkan_context: VulkanContext::new(vulkano_context), - vulkano_windows: VulkanoWindows::default(), + vulkan_context: Arc::new(VulkanContext::new(vulkano_context)), + vulkano_windows: Arc::new(VulkanoWindows::default()), gui: HashMap::new(), clear_color: [0.0, 0.0, 0.0], input_state: InputState::default(), @@ -47,41 +68,47 @@ impl From for App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window_id = self.vulkano_windows.create_window( - event_loop, - self.vulkan_context.vulkano_context(), - &WindowDescriptor { - title: "Rust ASH Test".to_string(), - width: 800.0, - height: 600.0, - present_mode: PresentMode::Fifo, - ..Default::default() - }, - |_| {}, - ); - - let gui = { - let renderer = self.vulkano_windows.get_renderer_mut(window_id).unwrap(); - Gui::new( + if let Some(vulkano_windows) = Arc::get_mut(&mut self.vulkano_windows) { + let window_id = vulkano_windows.create_window( event_loop, - renderer.surface(), - renderer.graphics_queue(), - renderer.swapchain_format(), - GuiConfig { - is_overlay: true, + self.vulkan_context.vulkano_context(), + &WindowDescriptor { + title: "Rust ASH Test".to_string(), + width: 800.0, + height: 600.0, + present_mode: PresentMode::Fifo, ..Default::default() }, - ) - }; - self.gui.insert(window_id, gui); + |_| {}, + ); + + let gui = { + let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); + Gui::new( + event_loop, + renderer.surface(), + renderer.graphics_queue(), + renderer.swapchain_format(), + GuiConfig { + is_overlay: true, + ..Default::default() + }, + ) + }; + self.gui.insert(window_id, gui); + } self.scene_manager .load_scene(Box::new(MainScene::default())); } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { - let renderer = self.vulkano_windows.get_renderer_mut(id).unwrap(); + let renderer = Arc::get_mut(&mut self.vulkano_windows) + .unwrap() + .get_renderer_mut(id) + .unwrap(); let gui = self.gui.get_mut(&id).unwrap(); + let render_context = RenderContext::from((self.vulkan_context.as_ref(), &*renderer)); if !gui.update(&event) { self.input_state.process_event(&event); @@ -100,6 +127,11 @@ impl ApplicationHandler for App { } WindowEvent::RedrawRequested => { self.input_state.update(); + self.timer.update(); + self.scene_manager.load_scene_if_not_loaded(&render_context); + if let Some(scene) = self.scene_manager.current_scene_mut() { + scene.update(&render_context, &self.input_state, &self.timer); + } let acquire_future = renderer.acquire(None, |_| {}).unwrap(); @@ -137,10 +169,8 @@ impl ApplicationHandler for App { .unwrap(); } - if let Some(scene) = self.scene.as_ref() { - scene - .render(&self.vulkan_context, renderer, &mut builder) - .unwrap(); + if let Some(scene) = self.scene_manager.current_scene() { + scene.render(&render_context, &mut builder); } builder.end_rendering().unwrap(); @@ -174,6 +204,8 @@ impl ApplicationHandler for App { self.input_state.get_mouse_state().delta )); + ui.label(format!("Delta time: {:?}", self.timer.delta_time())); + for (key, state) in self .input_state .key_states diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index d98c419..a86d640 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,4 +1,5 @@ pub mod app; pub mod pipelines; +pub mod render_context; pub mod vertex; pub mod vulkan_context; diff --git a/src/core/render/render_context.rs b/src/core/render/render_context.rs new file mode 100644 index 0000000..d1d2be4 --- /dev/null +++ b/src/core/render/render_context.rs @@ -0,0 +1,70 @@ +use std::sync::Arc; + +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + format::Format, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; + +pub struct RenderContext { + pub(super) instance: Arc, + pub(super) device: Arc, + pub(super) graphics_queue: Arc, + pub(super) compute_queue: Arc, + pub(super) transfer_queue: Option>, + pub(super) memory_allocator: Arc, + pub(super) command_buffer_allocator: Arc, + pub(super) descriptor_set_allocator: Arc, + pub(super) window_size: [f32; 2], + pub(super) aspect_ratio: f32, + pub(super) swapchain_format: Format, +} + +impl RenderContext { + pub fn instance(&self) -> &Arc { + &self.instance + } + + pub fn device(&self) -> &Arc { + &self.device + } + + pub fn graphics_queue(&self) -> &Arc { + &self.graphics_queue + } + + pub fn compute_queue(&self) -> &Arc { + &self.compute_queue + } + + pub fn transfer_queue(&self) -> Option<&Arc> { + self.transfer_queue.as_ref() + } + + pub fn memory_allocator(&self) -> &Arc { + &self.memory_allocator + } + + pub fn command_buffer_allocator(&self) -> &Arc { + &self.command_buffer_allocator + } + + pub fn descriptor_set_allocator(&self) -> &Arc { + &self.descriptor_set_allocator + } + + pub fn window_size(&self) -> &[f32; 2] { + &self.window_size + } + + pub fn aspect_ratio(&self) -> f32 { + self.aspect_ratio + } + + pub fn swapchain_format(&self) -> Format { + self.swapchain_format + } +} diff --git a/src/core/scene.rs b/src/core/scene.rs index 2286820..e8040f8 100644 --- a/src/core/scene.rs +++ b/src/core/scene.rs @@ -1,11 +1,16 @@ -use vulkano_util::renderer::VulkanoWindowRenderer; +use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; -use super::{input::InputState, render::vulkan_context::VulkanContext, timer::Timer}; +use super::{input::InputState, render::render_context::RenderContext, timer::Timer}; pub trait Scene { - fn load(&mut self, app: &mut App); - fn update(&mut self, app: &mut App); - fn render(&self); + fn loaded(&self) -> bool; + fn load(&mut self, render_context: &RenderContext); + fn update(&mut self, render_context: &RenderContext, input_state: &InputState, timer: &Timer); + fn render( + &self, + render_context: &RenderContext, + builder: &mut AutoCommandBufferBuilder, + ); fn unload(&mut self); } @@ -20,10 +25,36 @@ impl SceneManager { } } + pub fn load_scene_if_not_loaded(&mut self, render_context: &RenderContext) { + if let Some(current_scene) = self.current_scene.as_mut() { + if !current_scene.loaded() { + current_scene.load(render_context); + } + } + } + pub fn load_scene(&mut self, scene: Box) { if let Some(current_scene) = self.current_scene.as_mut() { current_scene.unload(); } self.current_scene = Some(scene); } + + pub fn current_scene(&self) -> Option<&Box> { + if let Some(current_scene) = self.current_scene.as_ref() { + if current_scene.loaded() { + return Some(current_scene); + } + } + None + } + + pub fn current_scene_mut(&mut self) -> Option<&mut Box> { + if let Some(current_scene) = self.current_scene.as_mut() { + if current_scene.loaded() { + return Some(current_scene); + } + } + None + } } diff --git a/src/core/timer.rs b/src/core/timer.rs index eacf423..3245a4c 100644 --- a/src/core/timer.rs +++ b/src/core/timer.rs @@ -24,7 +24,11 @@ impl Timer { self.last_time = self.current_time; } - pub fn get_delta_time(&self) -> f32 { + pub fn delta_time(&self) -> f32 { self.delta_time } + + pub fn start_time(&self) -> std::time::Instant { + self.start_time + } } diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 4135c38..c0e13be 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,5 +1,8 @@ +use crate::core::input::InputState; +use crate::core::render::pipelines::triangle_pipeline::shaders::vs; +use crate::core::render::render_context::RenderContext; use crate::core::scene::Scene; -use crate::render::pipelines::triangle_pipeline::shaders::vs; +use crate::core::timer::Timer; use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; @@ -10,11 +13,13 @@ use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano_util::renderer::VulkanoWindowRenderer; +use winit::event::ElementState; +use winit::keyboard::{KeyCode, PhysicalKey}; -use crate::render::pipelines::triangle_pipeline::create_triangle_pipeline; -use crate::render::vertex::Vertex2D; +use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline; +use crate::core::render::vertex::Vertex2D; -use super::vulkan_context::VulkanContext; +use crate::core::render::vulkan_context::VulkanContext; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche @@ -74,8 +79,8 @@ const VERTICES: [Vertex2D; 12] = [ pub struct MainSceneState { pipeline: Arc, vertex_buffer: Subbuffer<[Vertex2D]>, - - rotation_start: Instant, + uniform_buffer: Subbuffer, + rotation: f32, } #[derive(Default)] @@ -84,103 +89,101 @@ pub struct MainScene { } impl Scene for MainScene { - fn load(&mut self, app: &mut App) { - let pipeline = create_triangle_pipeline( - app.vulkan_context.vulkano_context().device(), - app.vulkano_windows.swapchain_format(), - )?; - let vertex_buffer = Vertex2D::create_buffer( - Vec::from_iter(VERTICES), - vulkano_context.vulkano_context().memory_allocator(), - )?; + fn loaded(&self) -> bool { + self.state.is_some() + } + + fn load(&mut self, render_context: &RenderContext) { + let pipeline = + create_triangle_pipeline(render_context.device(), render_context.swapchain_format()) + .unwrap(); + let vertex_buffer = + Vertex2D::create_buffer(Vec::from_iter(VERTICES), render_context.memory_allocator()) + .unwrap(); + + let uniform_buffer = MainScene::get_uniform_buffer( + 0.0, + render_context.memory_allocator(), + render_context.aspect_ratio(), + ); self.state = Some(MainSceneState { pipeline, vertex_buffer, - rotation_start: Instant::now(), + uniform_buffer, + rotation: 0.0, }) } - fn update(&mut self, app: &mut App) { - todo!() + fn update(&mut self, render_context: &RenderContext, input_state: &InputState, timer: &Timer) { + let state = self.state.as_mut().unwrap(); + let delta_rotation = if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyA)) + == &ElementState::Pressed + { + timer.delta_time() * 5.0 + } else if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyD)) + == &ElementState::Pressed + { + timer.delta_time() * -5.0 + } else { + timer.delta_time() * 0.0 + }; + state.rotation += delta_rotation; + state.uniform_buffer = MainScene::get_uniform_buffer( + state.rotation, + render_context.memory_allocator(), + render_context.aspect_ratio(), + ); } - fn render(&self) { - todo!() - } - - fn unload(&mut self) { - todo!() - } -} - -impl MainScene { - pub fn load( - vulkano_context: &VulkanContext, - vulkano_window_renderer: &VulkanoWindowRenderer, - ) -> Result> { - let pipeline = create_triangle_pipeline( - vulkano_context.vulkano_context().device(), - vulkano_window_renderer.swapchain_format(), - )?; - let vertex_buffer = Vertex2D::create_buffer( - Vec::from_iter(VERTICES), - vulkano_context.vulkano_context().memory_allocator(), - )?; - - Ok(Scene { - pipeline, - vertex_buffer, - rotation_start: Instant::now(), - }) - } - - pub fn render( + fn render( &self, - vulkan_context: &VulkanContext, - vulkano_window_renderer: &VulkanoWindowRenderer, + render_context: &RenderContext, builder: &mut AutoCommandBufferBuilder, - ) -> Result<(), Box> { - let vertex_count = self.vertex_buffer.len() as u32; + ) { + let vertex_count = self.state.as_ref().unwrap().vertex_buffer.len() as u32; let instance_count = vertex_count / 3; - let uniform_buffer = self.get_uniform_buffer( - vulkan_context.vulkano_context().memory_allocator(), - vulkano_window_renderer.aspect_ratio(), - ); - let layout = &self.pipeline.layout().set_layouts()[0]; + let layout = &self.state.as_ref().unwrap().pipeline.layout().set_layouts()[0]; let descriptor_set = DescriptorSet::new( - vulkan_context.descriptor_set_allocator().clone(), + render_context.descriptor_set_allocator().clone(), layout.clone(), - [WriteDescriptorSet::buffer(0, uniform_buffer)], + [WriteDescriptorSet::buffer( + 0, + self.state.as_ref().unwrap().uniform_buffer.clone(), + )], [], ) .unwrap(); unsafe { builder - .bind_pipeline_graphics(self.pipeline.clone())? + .bind_pipeline_graphics(self.state.as_ref().unwrap().pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - self.pipeline.layout().clone(), + self.state.as_ref().unwrap().pipeline.layout().clone(), 0, descriptor_set, - )? - .bind_vertex_buffers(0, self.vertex_buffer.clone())? - .draw(vertex_count, instance_count, 0, 0)?; + ) + .unwrap() + .bind_vertex_buffers(0, self.state.as_ref().unwrap().vertex_buffer.clone()) + .unwrap() + .draw(vertex_count, instance_count, 0, 0) + .unwrap(); } - - Ok(()) } + fn unload(&mut self) {} +} + +impl MainScene { fn get_uniform_buffer( - &self, + rotation: f32, memory_allocator: &Arc, aspect_ratio: f32, ) -> Subbuffer { - let elapsed = self.rotation_start.elapsed(); - let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; - let rotation = Mat3::from_rotation_y(rotation as f32); + let rotation = Mat3::from_rotation_y(rotation); // NOTE: This teapot was meant for OpenGL where the origin is at the lower left // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. From 1976a8b53eb0c40f44dd9a593c40b4dd2018d1b4 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 26 May 2025 22:53:32 +0200 Subject: [PATCH 69/91] Refactor camera code --- res/shaders/vertex.vert | 19 ++- src/core/render/mod.rs | 2 +- .../render/pipelines/triangle_pipeline.rs | 4 +- src/core/render/primitives/camera.rs | 48 ++++++ src/core/render/primitives/mod.rs | 4 + src/core/render/primitives/mvp.rs | 45 ++++++ src/core/render/primitives/transform.rs | 37 +++++ src/core/render/{ => primitives}/vertex.rs | 0 src/game/main_scene.rs | 139 ++++++++---------- 9 files changed, 219 insertions(+), 79 deletions(-) create mode 100644 src/core/render/primitives/camera.rs create mode 100644 src/core/render/primitives/mod.rs create mode 100644 src/core/render/primitives/mvp.rs create mode 100644 src/core/render/primitives/transform.rs rename src/core/render/{ => primitives}/vertex.rs (100%) diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index 65b0acf..bb1c261 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1 +1,18 @@ -#version 450 layout (location = 0) in vec2 position; layout (location = 1) in vec3 color; layout (location = 0) out vec3 fragColor; layout (set = 0, binding = 0) uniform MVPData { mat4 world; mat4 view; mat4 projection; } uniforms; void main() { mat4 worldview = uniforms.view * uniforms.world; gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0); fragColor = color; } \ No newline at end of file +#version 450 + +layout (location = 0) in vec2 position; +layout (location = 1) in vec3 color; + +layout (location = 0) out vec3 fragColor; + +layout (set = 0, binding = 0) uniform MVP { + mat4 world; + mat4 view; + mat4 projection; +} uniforms; + +void main() { + mat4 worldview = uniforms.view * uniforms.world; + gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0); + fragColor = color; +} diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index a86d640..a074771 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,5 +1,5 @@ pub mod app; pub mod pipelines; +pub mod primitives; pub mod render_context; -pub mod vertex; pub mod vulkan_context; diff --git a/src/core/render/pipelines/triangle_pipeline.rs b/src/core/render/pipelines/triangle_pipeline.rs index 62bd8b0..b7ffce4 100644 --- a/src/core/render/pipelines/triangle_pipeline.rs +++ b/src/core/render/pipelines/triangle_pipeline.rs @@ -20,13 +20,14 @@ use vulkano::pipeline::{ }; use vulkano::shader::{EntryPoint, ShaderStages}; -use crate::core::render::vertex::Vertex2D; +use crate::core::render::primitives::vertex::Vertex2D; pub mod shaders { pub mod vs { vulkano_shaders::shader! { ty: "vertex", path: r"res/shaders/vertex.vert", + generate_structs: false, } } @@ -34,6 +35,7 @@ pub mod shaders { vulkano_shaders::shader! { ty: "fragment", path: r"res/shaders/vertex.frag", + generate_structs: false, } } } diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs new file mode 100644 index 0000000..efd61e5 --- /dev/null +++ b/src/core/render/primitives/camera.rs @@ -0,0 +1,48 @@ +use std::sync::Arc; + +use glam::Mat4; +use vulkano::{ + Validated, + buffer::{AllocateBufferError, Subbuffer}, + memory::allocator::StandardMemoryAllocator, +}; + +use super::{mvp::MVP, transform::Transform}; + +#[derive(Default)] +pub struct Camera { + view: Mat4, + projection: Mat4, + + transform: Transform, +} + +impl Camera { + pub fn new(view: Mat4, projection: Mat4) -> Self { + Self { + view, + projection, + transform: Transform::default(), + } + } + + pub fn get_transform(&self) -> &Transform { + &self.transform + } + + pub fn get_transform_mut(&mut self) -> &mut Transform { + &mut self.transform + } + + pub fn set_projection(&mut self, projection: Mat4) { + self.projection = projection; + } + + pub fn create_buffer( + &self, + memory_allocator: &Arc, + ) -> Result, Validated> { + MVP::new(&self.transform.get_mat4(), &self.view, &self.projection) + .into_buffer(memory_allocator) + } +} diff --git a/src/core/render/primitives/mod.rs b/src/core/render/primitives/mod.rs new file mode 100644 index 0000000..fd0b762 --- /dev/null +++ b/src/core/render/primitives/mod.rs @@ -0,0 +1,4 @@ +pub mod camera; +mod mvp; +pub mod transform; +pub mod vertex; diff --git a/src/core/render/primitives/mvp.rs b/src/core/render/primitives/mvp.rs new file mode 100644 index 0000000..fb73ad3 --- /dev/null +++ b/src/core/render/primitives/mvp.rs @@ -0,0 +1,45 @@ +use std::sync::Arc; + +use glam::Mat4; +use vulkano::Validated; +use vulkano::buffer::{ + AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, +}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; + +#[derive(BufferContents, Clone, Copy)] +#[repr(C)] +pub struct MVP { + world: [[f32; 4]; 4], + view: [[f32; 4]; 4], + projection: [[f32; 4]; 4], +} + +impl MVP { + pub fn new(world: &Mat4, view: &Mat4, projection: &Mat4) -> Self { + Self { + world: world.to_cols_array_2d(), + view: view.to_cols_array_2d(), + projection: projection.to_cols_array_2d(), + } + } + + pub fn into_buffer( + self, + memory_allocator: &Arc, + ) -> Result, Validated> { + Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::UNIFORM_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + [self], + ) + } +} diff --git a/src/core/render/primitives/transform.rs b/src/core/render/primitives/transform.rs new file mode 100644 index 0000000..88e6422 --- /dev/null +++ b/src/core/render/primitives/transform.rs @@ -0,0 +1,37 @@ +use glam::{Mat4, Quat, Vec3}; + +pub struct Transform { + position: Vec3, + rotation: Quat, + scale: Vec3, +} + +impl Default for Transform { + fn default() -> Self { + Self { + position: Vec3::default(), + rotation: Quat::default(), + scale: Vec3::ONE, + } + } +} + +impl Transform { + pub fn rotate(&mut self, rotation: Quat) { + self.rotation = self.rotation * rotation; + } + + pub fn translate(&mut self, translation: Vec3) { + self.position += translation; + } + + pub fn scale(&mut self, scale: Vec3) { + self.scale *= scale; + } + + pub fn get_mat4(&self) -> Mat4 { + Mat4::from_translation(self.position) + * Mat4::from_quat(self.rotation) + * Mat4::from_scale(self.scale) + } +} diff --git a/src/core/render/vertex.rs b/src/core/render/primitives/vertex.rs similarity index 100% rename from src/core/render/vertex.rs rename to src/core/render/primitives/vertex.rs diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index c0e13be..54ca089 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,25 +1,19 @@ use crate::core::input::InputState; -use crate::core::render::pipelines::triangle_pipeline::shaders::vs; +use crate::core::render::primitives::camera::Camera; +use crate::core::render::primitives::vertex::Vertex2D; use crate::core::render::render_context::RenderContext; use crate::core::scene::Scene; use crate::core::timer::Timer; -use glam::{Mat3, Mat4, Vec3}; -use std::error::Error; +use glam::{Mat4, Quat, Vec3}; use std::sync::Arc; -use std::time::Instant; -use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; +use vulkano::buffer::Subbuffer; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; -use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use vulkano_util::renderer::VulkanoWindowRenderer; use winit::event::ElementState; use winit::keyboard::{KeyCode, PhysicalKey}; use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline; -use crate::core::render::vertex::Vertex2D; - -use crate::core::render::vulkan_context::VulkanContext; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche @@ -79,8 +73,7 @@ const VERTICES: [Vertex2D; 12] = [ pub struct MainSceneState { pipeline: Arc, vertex_buffer: Subbuffer<[Vertex2D]>, - uniform_buffer: Subbuffer, - rotation: f32, + camera: Camera, } #[derive(Default)] @@ -101,39 +94,72 @@ impl Scene for MainScene { Vertex2D::create_buffer(Vec::from_iter(VERTICES), render_context.memory_allocator()) .unwrap(); - let uniform_buffer = MainScene::get_uniform_buffer( - 0.0, - render_context.memory_allocator(), - render_context.aspect_ratio(), + let camera = Camera::new( + Mat4::look_at_rh( + Vec3::new(0.3, 0.3, 1.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, -1.0, 0.0), + ), + Mat4::perspective_rh_gl( + std::f32::consts::FRAC_PI_2, + render_context.aspect_ratio(), + 0.01, + 100.0, + ), ); self.state = Some(MainSceneState { pipeline, vertex_buffer, - uniform_buffer, - rotation: 0.0, + camera, }) } fn update(&mut self, render_context: &RenderContext, input_state: &InputState, timer: &Timer) { let state = self.state.as_mut().unwrap(); - let delta_rotation = if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyA)) + + let speed = 50.0 * timer.delta_time(); + + let mut rot = Quat::default(); + rot *= Quat::from_rotation_y(input_state.mouse_state.delta.x * speed.to_radians()); + rot *= Quat::from_rotation_x(input_state.mouse_state.delta.y * speed.to_radians()); + state.camera.get_transform_mut().rotate(rot); + + let translation_x = if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyA)) == &ElementState::Pressed { - timer.delta_time() * 5.0 + timer.delta_time() * speed } else if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyD)) == &ElementState::Pressed { - timer.delta_time() * -5.0 + timer.delta_time() * -speed } else { timer.delta_time() * 0.0 }; - state.rotation += delta_rotation; - state.uniform_buffer = MainScene::get_uniform_buffer( - state.rotation, - render_context.memory_allocator(), + + let translation_z = if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyW)) + == &ElementState::Pressed + { + timer.delta_time() * speed + } else if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyS)) + == &ElementState::Pressed + { + timer.delta_time() * -speed + } else { + timer.delta_time() * 0.0 + }; + + state + .camera + .get_transform_mut() + .translate(Vec3::new(translation_x, 0.0, translation_z)); + + state.camera.set_projection(Mat4::perspective_rh_gl( + std::f32::consts::FRAC_PI_2, render_context.aspect_ratio(), - ); + 0.01, + 100.0, + )); } fn render( @@ -141,33 +167,35 @@ impl Scene for MainScene { render_context: &RenderContext, builder: &mut AutoCommandBufferBuilder, ) { - let vertex_count = self.state.as_ref().unwrap().vertex_buffer.len() as u32; + let state = self.state.as_ref().unwrap(); + let vertex_count = state.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; - let layout = &self.state.as_ref().unwrap().pipeline.layout().set_layouts()[0]; + let layout = &state.pipeline.layout().set_layouts()[0]; + let uniform_buffer = state + .camera + .create_buffer(render_context.memory_allocator()) + .unwrap(); let descriptor_set = DescriptorSet::new( render_context.descriptor_set_allocator().clone(), layout.clone(), - [WriteDescriptorSet::buffer( - 0, - self.state.as_ref().unwrap().uniform_buffer.clone(), - )], + [WriteDescriptorSet::buffer(0, uniform_buffer)], [], ) .unwrap(); unsafe { builder - .bind_pipeline_graphics(self.state.as_ref().unwrap().pipeline.clone()) + .bind_pipeline_graphics(state.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - self.state.as_ref().unwrap().pipeline.layout().clone(), + state.pipeline.layout().clone(), 0, descriptor_set, ) .unwrap() - .bind_vertex_buffers(0, self.state.as_ref().unwrap().vertex_buffer.clone()) + .bind_vertex_buffers(0, state.vertex_buffer.clone()) .unwrap() .draw(vertex_count, instance_count, 0, 0) .unwrap(); @@ -176,44 +204,3 @@ impl Scene for MainScene { fn unload(&mut self) {} } - -impl MainScene { - fn get_uniform_buffer( - rotation: f32, - memory_allocator: &Arc, - aspect_ratio: f32, - ) -> Subbuffer { - let rotation = Mat3::from_rotation_y(rotation); - - // NOTE: This teapot was meant for OpenGL where the origin is at the lower left - // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. - let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0); - let view = Mat4::look_at_rh( - Vec3::new(0.3, 0.3, 1.0), - Vec3::new(0.0, 0.0, 0.0), - Vec3::new(0.0, -1.0, 0.0), - ); - let scale = Mat4::from_scale(Vec3::splat(1.0)); - - let uniform_data = vs::MVPData { - world: Mat4::from_mat3(rotation).to_cols_array_2d(), - view: (view * scale).to_cols_array_2d(), - projection: proj.to_cols_array_2d(), - }; - - Buffer::from_data( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::UNIFORM_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - uniform_data, - ) - .unwrap() - } -} From 8c42e7b1399e85ec50e390fed805901e9d802ed3 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 27 May 2025 17:13:22 +0200 Subject: [PATCH 70/91] Refactor input --- src/core/{render => }/app.rs | 68 +++++++----------- src/core/input.rs | 75 -------------------- src/core/input/keyboard_state.rs | 46 +++++++++++++ src/core/input/mod.rs | 64 +++++++++++++++++ src/core/input/mouse_state.rs | 22 ++++++ src/core/input/virtual_binding.rs | 24 +++++++ src/core/input/virtual_input.rs | 110 ++++++++++++++++++++++++++++++ src/core/input/virtual_state.rs | 49 +++++++++++++ src/core/mod.rs | 1 + src/core/render/mod.rs | 1 - src/core/render/render_context.rs | 50 +++++++++++--- src/core/scene.rs | 9 ++- src/game/main_scene.rs | 43 +++++------- src/main.rs | 36 +++++++++- 14 files changed, 439 insertions(+), 159 deletions(-) rename src/core/{render => }/app.rs (76%) delete mode 100644 src/core/input.rs create mode 100644 src/core/input/keyboard_state.rs create mode 100644 src/core/input/mod.rs create mode 100644 src/core/input/mouse_state.rs create mode 100644 src/core/input/virtual_binding.rs create mode 100644 src/core/input/virtual_input.rs create mode 100644 src/core/input/virtual_state.rs diff --git a/src/core/render/app.rs b/src/core/app.rs similarity index 76% rename from src/core/render/app.rs rename to src/core/app.rs index 6e08f72..3a7145b 100644 --- a/src/core/render/app.rs +++ b/src/core/app.rs @@ -1,7 +1,9 @@ use std::collections::HashMap; use std::sync::Arc; -use crate::core::input::InputState; +use super::render::render_context::RenderContext; +use super::render::vulkan_context::VulkanContext; +use crate::core::input::InputManager; use crate::core::scene::SceneManager; use crate::core::timer::Timer; use crate::game::main_scene::MainScene; @@ -17,49 +19,46 @@ use vulkano_util::context::VulkanoContext; use vulkano_util::renderer::VulkanoWindowRenderer; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; -use winit::event::{ElementState, WindowEvent}; +use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; -use super::render_context::RenderContext; -use super::vulkan_context::VulkanContext; - pub struct App { vulkan_context: Arc, vulkano_windows: Arc, gui: HashMap, clear_color: [f32; 3], - input_state: InputState, + input_manager: InputManager, scene_manager: SceneManager, timer: Timer, } impl From<(&VulkanContext, &VulkanoWindowRenderer)> for RenderContext { fn from((vulkan_context, renderer): (&VulkanContext, &VulkanoWindowRenderer)) -> Self { - RenderContext { - window_size: renderer.resolution(), - aspect_ratio: renderer.aspect_ratio(), - instance: vulkan_context.vulkano_context().instance().clone(), - device: vulkan_context.vulkano_context().device().clone(), - graphics_queue: vulkan_context.vulkano_context().graphics_queue().clone(), - compute_queue: vulkan_context.vulkano_context().compute_queue().clone(), - transfer_queue: vulkan_context.vulkano_context().transfer_queue().cloned(), - memory_allocator: vulkan_context.vulkano_context().memory_allocator().clone(), - command_buffer_allocator: vulkan_context.command_buffer_allocator().clone(), - descriptor_set_allocator: vulkan_context.descriptor_set_allocator().clone(), - swapchain_format: renderer.swapchain_format(), - } + RenderContext::new( + vulkan_context.vulkano_context().instance().clone(), + vulkan_context.vulkano_context().device().clone(), + vulkan_context.vulkano_context().graphics_queue().clone(), + vulkan_context.vulkano_context().compute_queue().clone(), + vulkan_context.vulkano_context().transfer_queue().cloned(), + vulkan_context.vulkano_context().memory_allocator().clone(), + vulkan_context.command_buffer_allocator().clone(), + vulkan_context.descriptor_set_allocator().clone(), + renderer.resolution(), + renderer.aspect_ratio(), + renderer.swapchain_format(), + ) } } -impl From for App { - fn from(vulkano_context: VulkanoContext) -> Self { +impl App { + pub fn new(vulkano_context: VulkanoContext, input_manager: InputManager) -> Self { Self { vulkan_context: Arc::new(VulkanContext::new(vulkano_context)), vulkano_windows: Arc::new(VulkanoWindows::default()), gui: HashMap::new(), clear_color: [0.0, 0.0, 0.0], - input_state: InputState::default(), + input_manager, scene_manager: SceneManager::new(), timer: Timer::new(), } @@ -111,7 +110,7 @@ impl ApplicationHandler for App { let render_context = RenderContext::from((self.vulkan_context.as_ref(), &*renderer)); if !gui.update(&event) { - self.input_state.process_event(&event); + self.input_manager.process_window_event(&event); } match event { @@ -126,11 +125,11 @@ impl ApplicationHandler for App { renderer.resize(); } WindowEvent::RedrawRequested => { - self.input_state.update(); + self.input_manager.update(); self.timer.update(); self.scene_manager.load_scene_if_not_loaded(&render_context); if let Some(scene) = self.scene_manager.current_scene_mut() { - scene.update(&render_context, &self.input_state, &self.timer); + scene.update(&render_context, &self.input_manager, &self.timer); } let acquire_future = renderer.acquire(None, |_| {}).unwrap(); @@ -195,25 +194,10 @@ impl ApplicationHandler for App { .show(&ctx, |ui| { ui.label(format!("Resolution: {:?}", renderer.resolution())); ui.color_edit_button_rgb(&mut self.clear_color); - ui.label(format!( - "Mouse position: {:?}", - self.input_state.get_mouse_state().position - )); - ui.label(format!( - "Mouse delta: {:?}", - self.input_state.get_mouse_state().delta - )); + + ui.label(format!("{:#?}", self.input_manager.get_virtual_input())); ui.label(format!("Delta time: {:?}", self.timer.delta_time())); - - for (key, state) in self - .input_state - .key_states - .iter() - .filter(|(_, state)| *state == &ElementState::Pressed) - { - ui.label(format!("{:?} State: {:?}", key, state)); - } }); }); diff --git a/src/core/input.rs b/src/core/input.rs deleted file mode 100644 index 59e8398..0000000 --- a/src/core/input.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::collections::HashMap; - -use winit::{ - dpi::PhysicalPosition, - event::{ElementState, KeyEvent, WindowEvent}, - keyboard::PhysicalKey, -}; - -#[derive(Debug, Default)] -pub struct MouseState { - old_position: glam::Vec2, - pub position: glam::Vec2, - pub delta: glam::Vec2, -} - -#[derive(Debug, Default)] -pub struct InputState { - pub key_states: HashMap, - pub mouse_state: MouseState, -} - -impl InputState { - pub fn process_event(&mut self, event: &WindowEvent) { - match event { - WindowEvent::KeyboardInput { event, .. } => { - self.update_key_state(event.physical_key, event); - } - WindowEvent::CursorMoved { position, .. } => { - self.update_mouse_position(position); - } - _ => {} - } - } - - /// Updates deltas before running update - pub fn update(&mut self) { - self.mouse_state.delta = self.mouse_state.position - self.mouse_state.old_position; - self.mouse_state.old_position = self.mouse_state.position; - } - - pub fn get_key_state(&self, key: PhysicalKey) -> &ElementState { - self.key_states.get(&key).unwrap_or(&ElementState::Released) - } - - pub fn get_mouse_state(&self) -> &MouseState { - &self.mouse_state - } - - fn update_key_state(&mut self, key: PhysicalKey, event: &KeyEvent) { - let key_state = self.key_states.get(&event.physical_key); - let new_key_state = match key_state { - Some(key_state) => match event.state { - ElementState::Pressed => match key_state { - ElementState::Released => Some(ElementState::Pressed), - ElementState::Pressed => None, - }, - ElementState::Released => match key_state { - ElementState::Released => None, - ElementState::Pressed => Some(ElementState::Released), - }, - }, - None => match event.state { - ElementState::Pressed => Some(ElementState::Pressed), - ElementState::Released => Some(ElementState::Released), - }, - }; - if let Some(new_key_state) = new_key_state { - self.key_states.insert(key, new_key_state); - } - } - - fn update_mouse_position(&mut self, position: &PhysicalPosition) { - self.mouse_state.position = glam::Vec2::new(position.x as f32, position.y as f32); - } -} diff --git a/src/core/input/keyboard_state.rs b/src/core/input/keyboard_state.rs new file mode 100644 index 0000000..c7b9b3e --- /dev/null +++ b/src/core/input/keyboard_state.rs @@ -0,0 +1,46 @@ +use egui_winit_vulkano::egui::ahash::HashMap; +use winit::{ + event::{ElementState, KeyEvent}, + keyboard::PhysicalKey, +}; + +use super::virtual_input::VirtualInput; + +#[derive(Debug, Default)] +pub struct KeyboardState { + key_states: HashMap, +} + +impl KeyboardState { + pub fn get_key_states(&self) -> &HashMap { + &self.key_states + } + + pub fn get_key_state(&self, key: PhysicalKey) -> &ElementState { + self.key_states.get(&key).unwrap_or(&ElementState::Released) + } + + pub fn process_window_event(&mut self, event: &KeyEvent, virtual_input: &mut VirtualInput) { + let key_state = self.key_states.get(&event.physical_key); + let new_key_state = match key_state { + Some(key_state) => match event.state { + ElementState::Pressed => match key_state { + ElementState::Released => Some(ElementState::Pressed), + ElementState::Pressed => None, + }, + ElementState::Released => match key_state { + ElementState::Released => None, + ElementState::Pressed => Some(ElementState::Released), + }, + }, + None => match event.state { + ElementState::Pressed => Some(ElementState::Pressed), + ElementState::Released => Some(ElementState::Released), + }, + }; + if let Some(new_key_state) = new_key_state { + self.key_states.insert(event.physical_key, new_key_state); + virtual_input.update_key_binding(event.physical_key, new_key_state); + } + } +} diff --git a/src/core/input/mod.rs b/src/core/input/mod.rs new file mode 100644 index 0000000..f29b963 --- /dev/null +++ b/src/core/input/mod.rs @@ -0,0 +1,64 @@ +use std::collections::HashMap; + +use keyboard_state::KeyboardState; +use mouse_state::MouseState; +use virtual_binding::VirtualBinding; +use virtual_input::VirtualInput; +use winit::event::WindowEvent; + +pub mod keyboard_state; +pub mod mouse_state; +pub mod virtual_binding; +pub mod virtual_input; +pub mod virtual_state; + +#[derive(Default)] +pub struct InputManager { + keyboard_state: KeyboardState, + mouse_state: MouseState, + virtual_input: VirtualInput, +} + +impl InputManager { + pub fn process_window_event(&mut self, event: &WindowEvent) { + match event { + WindowEvent::KeyboardInput { event, .. } => { + self.keyboard_state + .process_window_event(event, &mut self.virtual_input); + } + WindowEvent::CursorMoved { position, .. } => { + self.mouse_state.process_window_event(position); + } + _ => {} + } + } + + /// Updates deltas before running update + pub fn update(&mut self) { + self.mouse_state.update(&mut self.virtual_input); + } + + pub fn get_mouse_state(&self) -> &MouseState { + &self.mouse_state + } + + pub fn get_keyboard_state(&self) -> &KeyboardState { + &self.keyboard_state + } + + pub fn get_virtual_input(&self) -> &VirtualInput { + &self.virtual_input + } + + pub fn add_virtual_binding(&mut self, value_name: String, binding: VirtualBinding) { + self.virtual_input.add_binding(value_name, binding); + } + + pub fn add_virtual_bindings(&mut self, value_name: String, bindings: Vec) { + self.virtual_input.add_bindings(value_name, bindings); + } + + pub fn get_virtual_input_state(&self, value_name: &str) -> f32 { + self.virtual_input.get_state(value_name) + } +} diff --git a/src/core/input/mouse_state.rs b/src/core/input/mouse_state.rs new file mode 100644 index 0000000..8cdce4b --- /dev/null +++ b/src/core/input/mouse_state.rs @@ -0,0 +1,22 @@ +use winit::dpi::PhysicalPosition; + +use super::virtual_input::VirtualInput; + +#[derive(Debug, Default)] +pub struct MouseState { + old_position: glam::Vec2, + pub position: glam::Vec2, + pub delta: glam::Vec2, +} + +impl MouseState { + pub fn process_window_event(&mut self, position: &PhysicalPosition) { + self.position = glam::Vec2::new(position.x as f32, position.y as f32); + } + + pub fn update(&mut self, virtual_input: &mut VirtualInput) { + self.delta = self.position - self.old_position; + self.old_position = self.position; + virtual_input.update_mouse_binding(&self.delta); + } +} diff --git a/src/core/input/virtual_binding.rs b/src/core/input/virtual_binding.rs new file mode 100644 index 0000000..9209a78 --- /dev/null +++ b/src/core/input/virtual_binding.rs @@ -0,0 +1,24 @@ +use winit::{event::AxisId, keyboard::PhysicalKey}; + +#[derive(Clone)] +pub enum AxisDirection { + Positive, + Negative, +} + +impl From<&AxisDirection> for f32 { + fn from(direction: &AxisDirection) -> Self { + match direction { + AxisDirection::Positive => 1.0, + AxisDirection::Negative => -1.0, + } + } +} + +#[derive(Clone)] +pub enum VirtualBinding { + Keyboard(PhysicalKey, AxisDirection), + Axis(AxisId, AxisDirection, f32), // f32 deadzone + MouseX(AxisDirection), + MouseY(AxisDirection), +} diff --git a/src/core/input/virtual_input.rs b/src/core/input/virtual_input.rs new file mode 100644 index 0000000..f1ec4a8 --- /dev/null +++ b/src/core/input/virtual_input.rs @@ -0,0 +1,110 @@ +use std::{collections::HashMap, sync::Arc}; + +use egui_winit_vulkano::egui::mutex::Mutex; +use winit::{event::ElementState, keyboard::PhysicalKey}; + +use super::{ + virtual_binding::VirtualBinding, + virtual_state::{VirtualBindingState, VirtualInputState}, +}; + +#[derive(Default)] +pub struct VirtualInput { + states: HashMap>>, + states_by_key: HashMap>>>, + mouse_states: Vec>>, +} + +impl std::fmt::Debug for VirtualInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("VirtualInput"); + + for (name, state) in &self.states { + debug.field(name, &state.lock().value); + } + + debug.finish() + } +} + +impl VirtualInput { + pub fn get_state(&self, value_name: &str) -> f32 { + self.states + .get(value_name) + .and_then(|state| Some(state.lock().value)) + .unwrap_or(0.0) + } + + pub fn add_bindings(&mut self, value_name: String, new_bindings: Vec) { + add_bindings( + &mut self.states, + &mut self.states_by_key, + &mut self.mouse_states, + value_name, + new_bindings, + ); + } + + pub fn add_binding(&mut self, value_name: String, binding: VirtualBinding) { + self.add_bindings(value_name, vec![binding]); + } + + pub(super) fn update_key_binding(&mut self, key: PhysicalKey, key_state: ElementState) { + let states = self.states_by_key.get_mut(&key); + + if let Some(states) = states { + for state in states { + let mut state = state.lock(); + state.update_from_key(key, key_state); + } + } else { + log::trace!("{}", self.states_by_key.keys().len()); + log::warn!("No states found for key: {key:?}"); + } + } + + pub(super) fn update_mouse_binding(&mut self, delta: &glam::Vec2) { + for state in &mut self.mouse_states { + let mut state = state.lock(); + state.update_from_mouse(delta); + } + } +} + +fn add_bindings( + states: &mut HashMap>>, + states_by_key: &mut HashMap>>>, + mouse_states: &mut Vec>>, + value_name: String, + new_bindings: Vec, +) { + let state = states + .entry(value_name) + .or_insert(Arc::new(Mutex::new(VirtualInputState { + value: 0.0, + bindings: Vec::new(), + }))); + + for binding in &new_bindings { + match binding { + VirtualBinding::Keyboard(key, _) => { + states_by_key + .entry(*key) + .or_insert(Vec::new()) + .push(state.clone()); + } + VirtualBinding::MouseX(_) | VirtualBinding::MouseY(_) => { + mouse_states.push(state.clone()); + } + _ => {} + } + } + + let mut state = state.lock(); + state + .bindings + .extend(new_bindings.iter().map(|b| VirtualBindingState { + value: 0.0, + binding: b.clone(), + })); +} diff --git a/src/core/input/virtual_state.rs b/src/core/input/virtual_state.rs new file mode 100644 index 0000000..47f8b0d --- /dev/null +++ b/src/core/input/virtual_state.rs @@ -0,0 +1,49 @@ +use winit::{event::ElementState, keyboard::PhysicalKey}; + +use super::virtual_binding::VirtualBinding; + +pub struct VirtualBindingState { + pub value: f32, + pub binding: VirtualBinding, +} + +pub struct VirtualInputState { + pub value: f32, + pub bindings: Vec, +} + +impl VirtualInputState { + pub fn update_from_key(&mut self, key: PhysicalKey, key_state: ElementState) { + let mut new_value = 0.0; + for binding in &mut self.bindings { + if let VirtualBinding::Keyboard(binding_key, direction) = &binding.binding { + if binding_key == &key { + if key_state == ElementState::Pressed { + binding.value += f32::from(direction); + } else { + binding.value = 0.0; + } + } + } + new_value += binding.value; + } + self.value = new_value; + } + + pub fn update_from_mouse(&mut self, delta: &glam::Vec2) { + let mut new_value = 0.0; + for binding in &mut self.bindings { + match &binding.binding { + VirtualBinding::MouseX(direction) => { + binding.value = f32::from(direction) * delta.x; + } + VirtualBinding::MouseY(direction) => { + binding.value = f32::from(direction) * delta.y; + } + _ => {} + } + new_value += binding.value; + } + self.value = new_value; + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs index ddaf320..237496d 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,3 +1,4 @@ +pub mod app; pub mod input; pub mod render; pub mod scene; diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index a074771..c550198 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,4 +1,3 @@ -pub mod app; pub mod pipelines; pub mod primitives; pub mod render_context; diff --git a/src/core/render/render_context.rs b/src/core/render/render_context.rs index d1d2be4..e9e34a1 100644 --- a/src/core/render/render_context.rs +++ b/src/core/render/render_context.rs @@ -10,20 +10,48 @@ use vulkano::{ }; pub struct RenderContext { - pub(super) instance: Arc, - pub(super) device: Arc, - pub(super) graphics_queue: Arc, - pub(super) compute_queue: Arc, - pub(super) transfer_queue: Option>, - pub(super) memory_allocator: Arc, - pub(super) command_buffer_allocator: Arc, - pub(super) descriptor_set_allocator: Arc, - pub(super) window_size: [f32; 2], - pub(super) aspect_ratio: f32, - pub(super) swapchain_format: Format, + instance: Arc, + device: Arc, + graphics_queue: Arc, + compute_queue: Arc, + transfer_queue: Option>, + memory_allocator: Arc, + command_buffer_allocator: Arc, + descriptor_set_allocator: Arc, + window_size: [f32; 2], + aspect_ratio: f32, + swapchain_format: Format, } impl RenderContext { + pub fn new( + instance: Arc, + device: Arc, + graphics_queue: Arc, + compute_queue: Arc, + transfer_queue: Option>, + memory_allocator: Arc, + command_buffer_allocator: Arc, + descriptor_set_allocator: Arc, + window_size: [f32; 2], + aspect_ratio: f32, + swapchain_format: Format, + ) -> Self { + Self { + instance, + device, + graphics_queue, + compute_queue, + transfer_queue, + memory_allocator, + command_buffer_allocator, + descriptor_set_allocator, + window_size, + aspect_ratio, + swapchain_format, + } + } + pub fn instance(&self) -> &Arc { &self.instance } diff --git a/src/core/scene.rs b/src/core/scene.rs index e8040f8..9717117 100644 --- a/src/core/scene.rs +++ b/src/core/scene.rs @@ -1,11 +1,16 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; -use super::{input::InputState, render::render_context::RenderContext, timer::Timer}; +use super::{input::InputManager, render::render_context::RenderContext, timer::Timer}; pub trait Scene { fn loaded(&self) -> bool; fn load(&mut self, render_context: &RenderContext); - fn update(&mut self, render_context: &RenderContext, input_state: &InputState, timer: &Timer); + fn update( + &mut self, + render_context: &RenderContext, + input_manager: &InputManager, + timer: &Timer, + ); fn render( &self, render_context: &RenderContext, diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 54ca089..a759478 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,4 +1,4 @@ -use crate::core::input::InputState; +use crate::core::input::InputManager; use crate::core::render::primitives::camera::Camera; use crate::core::render::primitives::vertex::Vertex2D; use crate::core::render::render_context::RenderContext; @@ -115,39 +115,30 @@ impl Scene for MainScene { }) } - fn update(&mut self, render_context: &RenderContext, input_state: &InputState, timer: &Timer) { + fn update( + &mut self, + render_context: &RenderContext, + input_manager: &InputManager, + timer: &Timer, + ) { let state = self.state.as_mut().unwrap(); let speed = 50.0 * timer.delta_time(); let mut rot = Quat::default(); - rot *= Quat::from_rotation_y(input_state.mouse_state.delta.x * speed.to_radians()); - rot *= Quat::from_rotation_x(input_state.mouse_state.delta.y * speed.to_radians()); + rot *= Quat::from_rotation_y( + input_manager.get_virtual_input_state("mouse_x") * speed.to_radians(), + ); + rot *= Quat::from_rotation_x( + input_manager.get_virtual_input_state("mouse_y") * speed.to_radians(), + ); state.camera.get_transform_mut().rotate(rot); - let translation_x = if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyA)) - == &ElementState::Pressed - { - timer.delta_time() * speed - } else if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyD)) - == &ElementState::Pressed - { - timer.delta_time() * -speed - } else { - timer.delta_time() * 0.0 - }; + let translation_x = + input_manager.get_virtual_input_state("move_right") * timer.delta_time() * speed; - let translation_z = if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyW)) - == &ElementState::Pressed - { - timer.delta_time() * speed - } else if input_state.get_key_state(PhysicalKey::Code(KeyCode::KeyS)) - == &ElementState::Pressed - { - timer.delta_time() * -speed - } else { - timer.delta_time() * 0.0 - }; + let translation_z = + input_manager.get_virtual_input_state("move_forward") * timer.delta_time() * speed; state .camera diff --git a/src/main.rs b/src/main.rs index 52ba1f6..d671188 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,14 @@ +use core::input::{ + InputManager, + virtual_binding::{AxisDirection, VirtualBinding}, +}; + use vulkano::device::{DeviceExtensions, DeviceFeatures}; use vulkano_util::context::{VulkanoConfig, VulkanoContext}; -use winit::event_loop::{ControlFlow, EventLoop}; +use winit::{ + event_loop::{ControlFlow, EventLoop}, + keyboard::{KeyCode, PhysicalKey}, +}; mod core; mod game; @@ -8,6 +16,30 @@ mod game; fn main() { env_logger::init(); + let mut input_manager = InputManager::default(); + input_manager.add_virtual_bindings( + "move_forward".to_string(), + vec![ + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Positive), + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Negative), + ], + ); + input_manager.add_virtual_bindings( + "move_right".to_string(), + vec![ + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Positive), + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Negative), + ], + ); + input_manager.add_virtual_bindings( + "mouse_x".to_string(), + vec![VirtualBinding::MouseX(AxisDirection::Positive)], + ); + input_manager.add_virtual_bindings( + "mouse_y".to_string(), + vec![VirtualBinding::MouseY(AxisDirection::Positive)], + ); + let device_extensions = DeviceExtensions { khr_swapchain: true, ..Default::default() @@ -30,7 +62,7 @@ fn main() { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let mut app = core::render::app::App::from(vulkano_context); + let mut app = core::app::App::new(vulkano_context, input_manager); match event_loop.run_app(&mut app) { Ok(_) => {} From b0f82b071461f6efd9f33c4af4eefde663f653a1 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 27 May 2025 19:11:28 +0200 Subject: [PATCH 71/91] input: Add support for Axis, Mouse Button, MouseWheel --- src/core/app.rs | 2 +- src/core/input/keyboard_state.rs | 41 +++------ src/core/input/mod.rs | 95 ++++++++++++++------- src/core/input/mouse_state.rs | 41 +++++++-- src/core/input/virtual_binding.rs | 16 ++-- src/core/input/virtual_input.rs | 136 +++++++++++++++++++----------- src/core/input/virtual_state.rs | 58 ++++++++++++- src/game/main_scene.rs | 5 +- src/main.rs | 74 ++++++++++------ 9 files changed, 308 insertions(+), 160 deletions(-) diff --git a/src/core/app.rs b/src/core/app.rs index 3a7145b..d14b4c5 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -195,7 +195,7 @@ impl ApplicationHandler for App { ui.label(format!("Resolution: {:?}", renderer.resolution())); ui.color_edit_button_rgb(&mut self.clear_color); - ui.label(format!("{:#?}", self.input_manager.get_virtual_input())); + ui.label(format!("{:#?}", self.input_manager)); ui.label(format!("Delta time: {:?}", self.timer.delta_time())); }); diff --git a/src/core/input/keyboard_state.rs b/src/core/input/keyboard_state.rs index c7b9b3e..f35e448 100644 --- a/src/core/input/keyboard_state.rs +++ b/src/core/input/keyboard_state.rs @@ -1,10 +1,10 @@ use egui_winit_vulkano::egui::ahash::HashMap; use winit::{ - event::{ElementState, KeyEvent}, + event::{ElementState, WindowEvent}, keyboard::PhysicalKey, }; -use super::virtual_input::VirtualInput; +use super::{process_new_element_state, virtual_input::VirtualInput}; #[derive(Debug, Default)] pub struct KeyboardState { @@ -12,35 +12,14 @@ pub struct KeyboardState { } impl KeyboardState { - pub fn get_key_states(&self) -> &HashMap { - &self.key_states - } - - pub fn get_key_state(&self, key: PhysicalKey) -> &ElementState { - self.key_states.get(&key).unwrap_or(&ElementState::Released) - } - - pub fn process_window_event(&mut self, event: &KeyEvent, virtual_input: &mut VirtualInput) { - let key_state = self.key_states.get(&event.physical_key); - let new_key_state = match key_state { - Some(key_state) => match event.state { - ElementState::Pressed => match key_state { - ElementState::Released => Some(ElementState::Pressed), - ElementState::Pressed => None, - }, - ElementState::Released => match key_state { - ElementState::Released => None, - ElementState::Pressed => Some(ElementState::Released), - }, - }, - None => match event.state { - ElementState::Pressed => Some(ElementState::Pressed), - ElementState::Released => Some(ElementState::Released), - }, - }; - if let Some(new_key_state) = new_key_state { - self.key_states.insert(event.physical_key, new_key_state); - virtual_input.update_key_binding(event.physical_key, new_key_state); + pub fn process_window_event(&mut self, event: &WindowEvent, virtual_input: &mut VirtualInput) { + if let WindowEvent::KeyboardInput { event, .. } = event { + let key_state = self.key_states.get(&event.physical_key); + let new_key_state = process_new_element_state(key_state, event.state); + if let Some(new_key_state) = new_key_state { + self.key_states.insert(event.physical_key, new_key_state); + virtual_input.update_key_binding(event.physical_key, new_key_state); + } } } } diff --git a/src/core/input/mod.rs b/src/core/input/mod.rs index f29b963..00ebe18 100644 --- a/src/core/input/mod.rs +++ b/src/core/input/mod.rs @@ -2,15 +2,15 @@ use std::collections::HashMap; use keyboard_state::KeyboardState; use mouse_state::MouseState; -use virtual_binding::VirtualBinding; use virtual_input::VirtualInput; -use winit::event::WindowEvent; +use winit::event::{ElementState, WindowEvent}; -pub mod keyboard_state; -pub mod mouse_state; -pub mod virtual_binding; -pub mod virtual_input; -pub mod virtual_state; +mod keyboard_state; +mod mouse_state; +mod virtual_binding; +mod virtual_input; +mod virtual_state; +pub use virtual_binding::{AxisDirection, VirtualBinding}; #[derive(Default)] pub struct InputManager { @@ -19,17 +19,34 @@ pub struct InputManager { virtual_input: VirtualInput, } +impl std::fmt::Debug for InputManager { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InputManager") + .field("virtual_input", &self.virtual_input) + .finish() + } +} + impl InputManager { + pub fn new(input_mapping: HashMap>) -> Self { + let mut input_manager = InputManager::default(); + for (value_name, bindings) in input_mapping { + input_manager.add_virtual_bindings(value_name, bindings); + } + input_manager + } + pub fn process_window_event(&mut self, event: &WindowEvent) { match event { - WindowEvent::KeyboardInput { event, .. } => { + WindowEvent::AxisMotion { axis, value, .. } => { + self.virtual_input.update_axis_binding(*axis, *value as f32); + } + _ => { self.keyboard_state .process_window_event(event, &mut self.virtual_input); + self.mouse_state + .process_window_event(event, &mut self.virtual_input); } - WindowEvent::CursorMoved { position, .. } => { - self.mouse_state.process_window_event(position); - } - _ => {} } } @@ -38,27 +55,41 @@ impl InputManager { self.mouse_state.update(&mut self.virtual_input); } - pub fn get_mouse_state(&self) -> &MouseState { - &self.mouse_state - } - - pub fn get_keyboard_state(&self) -> &KeyboardState { - &self.keyboard_state - } - - pub fn get_virtual_input(&self) -> &VirtualInput { - &self.virtual_input - } - - pub fn add_virtual_binding(&mut self, value_name: String, binding: VirtualBinding) { - self.virtual_input.add_binding(value_name, binding); - } - - pub fn add_virtual_bindings(&mut self, value_name: String, bindings: Vec) { - self.virtual_input.add_bindings(value_name, bindings); - } - pub fn get_virtual_input_state(&self, value_name: &str) -> f32 { self.virtual_input.get_state(value_name) } + + fn add_virtual_bindings(&mut self, value_name: String, bindings: Vec) { + self.virtual_input.add_bindings(value_name, bindings); + } +} + +/// Maps the old element state to the new element state. +/// if is changed, returns Some(new_state), otherwise returns None +#[inline] +fn process_new_element_state( + old: Option<&ElementState>, + new: ElementState, +) -> Option { + match old { + Some(old) => match new { + ElementState::Pressed => match old { + ElementState::Released => Some(ElementState::Pressed), + ElementState::Pressed => None, + }, + ElementState::Released => match old { + ElementState::Released => None, + ElementState::Pressed => Some(ElementState::Released), + }, + }, + None => match new { + ElementState::Pressed => Some(ElementState::Pressed), + ElementState::Released => Some(ElementState::Released), + }, + } +} + +#[inline] +fn process_axis_deadzone(value: f32, deadzone: f32) -> f32 { + if value.abs() < deadzone { 0.0 } else { value } } diff --git a/src/core/input/mouse_state.rs b/src/core/input/mouse_state.rs index 8cdce4b..084f43c 100644 --- a/src/core/input/mouse_state.rs +++ b/src/core/input/mouse_state.rs @@ -1,22 +1,49 @@ -use winit::dpi::PhysicalPosition; +use std::collections::HashMap; -use super::virtual_input::VirtualInput; +use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent}; + +use super::{process_new_element_state, virtual_input::VirtualInput}; #[derive(Debug, Default)] pub struct MouseState { old_position: glam::Vec2, - pub position: glam::Vec2, - pub delta: glam::Vec2, + position: glam::Vec2, + delta: glam::Vec2, + wheel_delta: glam::Vec2, + mouse_button_state: HashMap, } impl MouseState { - pub fn process_window_event(&mut self, position: &PhysicalPosition) { - self.position = glam::Vec2::new(position.x as f32, position.y as f32); + pub fn process_window_event(&mut self, event: &WindowEvent, virtual_input: &mut VirtualInput) { + match event { + WindowEvent::CursorMoved { position, .. } => { + self.position = glam::Vec2::new(position.x as f32, position.y as f32); + } + WindowEvent::MouseWheel { delta, .. } => { + self.wheel_delta += match delta { + MouseScrollDelta::PixelDelta(position) => { + glam::Vec2::new(position.x as f32, position.y as f32) + } + MouseScrollDelta::LineDelta(x, y) => glam::Vec2::new(*x as f32, *y as f32), + }; + } + WindowEvent::MouseInput { button, state, .. } => { + let key_state = self.mouse_button_state.get(button); + let new_key_state = process_new_element_state(key_state, *state); + if let Some(new_key_state) = new_key_state { + self.mouse_button_state.insert(*button, new_key_state); + virtual_input.update_mouse_button_binding(*button, new_key_state); + } + } + _ => {} + } } pub fn update(&mut self, virtual_input: &mut VirtualInput) { self.delta = self.position - self.old_position; self.old_position = self.position; - virtual_input.update_mouse_binding(&self.delta); + virtual_input.update_mouse_move_binding(&self.delta); + virtual_input.update_mouse_wheel_binding(&self.wheel_delta); + self.wheel_delta = glam::Vec2::ZERO; } } diff --git a/src/core/input/virtual_binding.rs b/src/core/input/virtual_binding.rs index 9209a78..c11dd48 100644 --- a/src/core/input/virtual_binding.rs +++ b/src/core/input/virtual_binding.rs @@ -1,16 +1,19 @@ -use winit::{event::AxisId, keyboard::PhysicalKey}; +use winit::{ + event::{AxisId, MouseButton}, + keyboard::PhysicalKey, +}; #[derive(Clone)] pub enum AxisDirection { - Positive, - Negative, + Normal, + Invert, } impl From<&AxisDirection> for f32 { fn from(direction: &AxisDirection) -> Self { match direction { - AxisDirection::Positive => 1.0, - AxisDirection::Negative => -1.0, + AxisDirection::Normal => 1.0, + AxisDirection::Invert => -1.0, } } } @@ -21,4 +24,7 @@ pub enum VirtualBinding { Axis(AxisId, AxisDirection, f32), // f32 deadzone MouseX(AxisDirection), MouseY(AxisDirection), + MouseWheelX(AxisDirection), + MouseWheelY(AxisDirection), + MouseButton(MouseButton, AxisDirection), } diff --git a/src/core/input/virtual_input.rs b/src/core/input/virtual_input.rs index f1ec4a8..875d66c 100644 --- a/src/core/input/virtual_input.rs +++ b/src/core/input/virtual_input.rs @@ -1,7 +1,10 @@ use std::{collections::HashMap, sync::Arc}; use egui_winit_vulkano::egui::mutex::Mutex; -use winit::{event::ElementState, keyboard::PhysicalKey}; +use winit::{ + event::{AxisId, ElementState, MouseButton}, + keyboard::PhysicalKey, +}; use super::{ virtual_binding::VirtualBinding, @@ -10,9 +13,15 @@ use super::{ #[derive(Default)] pub struct VirtualInput { + // Global states states: HashMap>>, + + // Per kind of input states to keep complexity low during state updates states_by_key: HashMap>>>, - mouse_states: Vec>>, + mouse_move_states: Vec>>, + mouse_wheel_states: Vec>>, + mouse_button_states: HashMap>>>, + axis_states: HashMap>>>, } impl std::fmt::Debug for VirtualInput { @@ -36,17 +45,50 @@ impl VirtualInput { } pub fn add_bindings(&mut self, value_name: String, new_bindings: Vec) { - add_bindings( - &mut self.states, - &mut self.states_by_key, - &mut self.mouse_states, - value_name, - new_bindings, - ); - } + let state = + self.states + .entry(value_name) + .or_insert(Arc::new(Mutex::new(VirtualInputState { + value: 0.0, + bindings: Vec::new(), + }))); - pub fn add_binding(&mut self, value_name: String, binding: VirtualBinding) { - self.add_bindings(value_name, vec![binding]); + for binding in &new_bindings { + match binding { + VirtualBinding::Keyboard(key, _) => { + self.states_by_key + .entry(*key) + .or_insert(Vec::new()) + .push(state.clone()); + } + VirtualBinding::MouseX(_) | VirtualBinding::MouseY(_) => { + self.mouse_move_states.push(state.clone()); + } + VirtualBinding::MouseButton(button, _) => { + self.mouse_button_states + .entry(*button) + .or_insert(Vec::new()) + .push(state.clone()); + } + VirtualBinding::MouseWheelX(_) | VirtualBinding::MouseWheelY(_) => { + self.mouse_wheel_states.push(state.clone()); + } + VirtualBinding::Axis(axis, _, _) => { + self.axis_states + .entry(*axis) + .or_insert(Vec::new()) + .push(state.clone()); + } + } + } + + state + .lock() + .bindings + .extend(new_bindings.iter().map(|b| VirtualBindingState { + value: 0.0, + binding: b.clone(), + })); } pub(super) fn update_key_binding(&mut self, key: PhysicalKey, key_state: ElementState) { @@ -57,54 +99,46 @@ impl VirtualInput { let mut state = state.lock(); state.update_from_key(key, key_state); } - } else { - log::trace!("{}", self.states_by_key.keys().len()); - log::warn!("No states found for key: {key:?}"); } } - pub(super) fn update_mouse_binding(&mut self, delta: &glam::Vec2) { - for state in &mut self.mouse_states { + pub(super) fn update_mouse_move_binding(&mut self, delta: &glam::Vec2) { + for state in &mut self.mouse_move_states { let mut state = state.lock(); state.update_from_mouse(delta); } } -} -fn add_bindings( - states: &mut HashMap>>, - states_by_key: &mut HashMap>>>, - mouse_states: &mut Vec>>, - value_name: String, - new_bindings: Vec, -) { - let state = states - .entry(value_name) - .or_insert(Arc::new(Mutex::new(VirtualInputState { - value: 0.0, - bindings: Vec::new(), - }))); - - for binding in &new_bindings { - match binding { - VirtualBinding::Keyboard(key, _) => { - states_by_key - .entry(*key) - .or_insert(Vec::new()) - .push(state.clone()); - } - VirtualBinding::MouseX(_) | VirtualBinding::MouseY(_) => { - mouse_states.push(state.clone()); - } - _ => {} + pub(super) fn update_mouse_wheel_binding(&mut self, delta: &glam::Vec2) { + for state in &mut self.mouse_wheel_states { + let mut state = state.lock(); + state.update_from_mouse_wheel(delta); } } - let mut state = state.lock(); - state - .bindings - .extend(new_bindings.iter().map(|b| VirtualBindingState { - value: 0.0, - binding: b.clone(), - })); + pub(super) fn update_mouse_button_binding( + &mut self, + button: MouseButton, + button_state: ElementState, + ) { + let states = self.mouse_button_states.get_mut(&button); + + if let Some(states) = states { + for state in states { + let mut state = state.lock(); + state.update_from_mouse_button(button, button_state); + } + } + } + + pub(super) fn update_axis_binding(&mut self, axis: AxisId, axis_state: f32) { + let states = self.axis_states.get_mut(&axis); + + if let Some(states) = states { + for state in states { + let mut state = state.lock(); + state.update_from_axis(axis, axis_state); + } + } + } } diff --git a/src/core/input/virtual_state.rs b/src/core/input/virtual_state.rs index 47f8b0d..93cf28f 100644 --- a/src/core/input/virtual_state.rs +++ b/src/core/input/virtual_state.rs @@ -1,6 +1,9 @@ -use winit::{event::ElementState, keyboard::PhysicalKey}; +use winit::{ + event::{AxisId, ElementState, MouseButton}, + keyboard::PhysicalKey, +}; -use super::virtual_binding::VirtualBinding; +use super::{process_axis_deadzone, virtual_binding::VirtualBinding}; pub struct VirtualBindingState { pub value: f32, @@ -46,4 +49,55 @@ impl VirtualInputState { } self.value = new_value; } + + pub fn update_from_mouse_wheel(&mut self, delta: &glam::Vec2) { + let mut new_value = 0.0; + for binding in &mut self.bindings { + match &binding.binding { + VirtualBinding::MouseWheelX(direction) => { + binding.value = f32::from(direction) * delta.x; + } + VirtualBinding::MouseWheelY(direction) => { + binding.value = f32::from(direction) * delta.y; + } + _ => {} + } + new_value += binding.value; + } + self.value = new_value; + } + + pub fn update_from_mouse_button(&mut self, button: MouseButton, button_state: ElementState) { + let mut new_value = 0.0; + for binding in &mut self.bindings { + match &binding.binding { + VirtualBinding::MouseButton(binding_button, direction) => { + if binding_button == &button { + if button_state == ElementState::Pressed { + binding.value = f32::from(direction); + } else { + binding.value = 0.0; + } + } + } + _ => {} + } + new_value += binding.value; + } + self.value = new_value; + } + + pub fn update_from_axis(&mut self, axis: AxisId, axis_state: f32) { + let mut new_value = 0.0; + for binding in &mut self.bindings { + if let VirtualBinding::Axis(binding_axis, direction, deadzone) = &binding.binding { + if binding_axis == &axis { + binding.value = + f32::from(direction) * process_axis_deadzone(axis_state, *deadzone); + } + } + new_value += binding.value; + } + self.value = new_value; + } } diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index a759478..1f27f0b 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,4 +1,5 @@ use crate::core::input::InputManager; +use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline; use crate::core::render::primitives::camera::Camera; use crate::core::render::primitives::vertex::Vertex2D; use crate::core::render::render_context::RenderContext; @@ -10,10 +11,6 @@ use vulkano::buffer::Subbuffer; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use winit::event::ElementState; -use winit::keyboard::{KeyCode, PhysicalKey}; - -use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche diff --git a/src/main.rs b/src/main.rs index d671188..6a4984b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,10 @@ -use core::input::{ - InputManager, - virtual_binding::{AxisDirection, VirtualBinding}, -}; +use core::input::{AxisDirection, InputManager, VirtualBinding}; +use std::collections::HashMap; use vulkano::device::{DeviceExtensions, DeviceFeatures}; use vulkano_util::context::{VulkanoConfig, VulkanoContext}; use winit::{ + event::MouseButton, event_loop::{ControlFlow, EventLoop}, keyboard::{KeyCode, PhysicalKey}, }; @@ -16,29 +15,50 @@ mod game; fn main() { env_logger::init(); - let mut input_manager = InputManager::default(); - input_manager.add_virtual_bindings( - "move_forward".to_string(), - vec![ - VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Positive), - VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Negative), - ], - ); - input_manager.add_virtual_bindings( - "move_right".to_string(), - vec![ - VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Positive), - VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Negative), - ], - ); - input_manager.add_virtual_bindings( - "mouse_x".to_string(), - vec![VirtualBinding::MouseX(AxisDirection::Positive)], - ); - input_manager.add_virtual_bindings( - "mouse_y".to_string(), - vec![VirtualBinding::MouseY(AxisDirection::Positive)], - ); + let input_manager = InputManager::new(HashMap::from([ + ( + "move_forward".to_string(), + vec![ + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Normal), + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Invert), + VirtualBinding::Axis(0, AxisDirection::Normal, 1.0), + ], + ), + ( + "move_right".to_string(), + vec![ + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Normal), + VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Invert), + VirtualBinding::Axis(1, AxisDirection::Normal, 1.0), + ], + ), + ( + "mouse_x".to_string(), + vec![VirtualBinding::MouseX(AxisDirection::Normal)], + ), + ( + "mouse_y".to_string(), + vec![VirtualBinding::MouseY(AxisDirection::Normal)], + ), + ( + "mouse_wheel".to_string(), + vec![VirtualBinding::MouseWheelY(AxisDirection::Normal)], + ), + ( + "mouse_left".to_string(), + vec![VirtualBinding::MouseButton( + MouseButton::Left, + AxisDirection::Normal, + )], + ), + ( + "mouse_right".to_string(), + vec![VirtualBinding::MouseButton( + MouseButton::Right, + AxisDirection::Normal, + )], + ), + ])); let device_extensions = DeviceExtensions { khr_swapchain: true, From 5b0ab192078b1357acea720ebc858bcdf5c9eece Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 27 May 2025 19:44:21 +0200 Subject: [PATCH 72/91] input: Refactor all previous states --- src/core/input/cache.rs | 86 ++++++++++++++++++++++++++++++++ src/core/input/keyboard_state.rs | 25 ---------- src/core/input/mod.rs | 86 ++++++++++++++++---------------- src/core/input/mouse_state.rs | 49 ------------------ src/core/input/virtual_state.rs | 7 ++- 5 files changed, 135 insertions(+), 118 deletions(-) create mode 100644 src/core/input/cache.rs delete mode 100644 src/core/input/keyboard_state.rs delete mode 100644 src/core/input/mouse_state.rs diff --git a/src/core/input/cache.rs b/src/core/input/cache.rs new file mode 100644 index 0000000..b9d562c --- /dev/null +++ b/src/core/input/cache.rs @@ -0,0 +1,86 @@ +use std::{ + collections::HashMap, + hash::Hash, + ops::{Add, AddAssign, Sub}, +}; + +use winit::event::ElementState; + +pub struct CachedElementState { + cache: HashMap, +} + +impl Default for CachedElementState { + fn default() -> Self { + Self { + cache: HashMap::new(), + } + } +} + +impl CachedElementState { + pub fn set_key_state(&mut self, key: K, state: ElementState) -> Option { + let key_state = self.cache.get(&key); + let new_key_state = match key_state { + Some(old) => match state { + ElementState::Pressed => match old { + ElementState::Released => Some(ElementState::Pressed), + ElementState::Pressed => None, + }, + ElementState::Released => match old { + ElementState::Released => None, + ElementState::Pressed => Some(ElementState::Released), + }, + }, + None => match state { + ElementState::Pressed => Some(ElementState::Pressed), + ElementState::Released => Some(ElementState::Released), + }, + }; + if let Some(new_key_state) = new_key_state { + self.cache.insert(key, new_key_state); + } + new_key_state + } +} + +#[derive(Default)] +pub struct CachedMovement +where + T: Sub + Add + Default + Copy, +{ + pub old_value: Option, + pub value: T, +} + +impl CachedMovement +where + T: Sub + Add + Default + Copy, +{ + pub fn set_value(&mut self, value: T) { + self.value = value; + } + + pub fn reset(&mut self) -> T { + match self.old_value.as_ref() { + Some(old_value) => { + let diff = self.value - *old_value; + self.old_value = Some(self.value); + diff + } + None => { + self.old_value = Some(self.value); + T::default() + } + } + } +} + +impl AddAssign for CachedMovement +where + T: Add + Sub + Default + Copy, +{ + fn add_assign(&mut self, rhs: T) { + self.value = self.value + rhs; + } +} diff --git a/src/core/input/keyboard_state.rs b/src/core/input/keyboard_state.rs deleted file mode 100644 index f35e448..0000000 --- a/src/core/input/keyboard_state.rs +++ /dev/null @@ -1,25 +0,0 @@ -use egui_winit_vulkano::egui::ahash::HashMap; -use winit::{ - event::{ElementState, WindowEvent}, - keyboard::PhysicalKey, -}; - -use super::{process_new_element_state, virtual_input::VirtualInput}; - -#[derive(Debug, Default)] -pub struct KeyboardState { - key_states: HashMap, -} - -impl KeyboardState { - pub fn process_window_event(&mut self, event: &WindowEvent, virtual_input: &mut VirtualInput) { - if let WindowEvent::KeyboardInput { event, .. } = event { - let key_state = self.key_states.get(&event.physical_key); - let new_key_state = process_new_element_state(key_state, event.state); - if let Some(new_key_state) = new_key_state { - self.key_states.insert(event.physical_key, new_key_state); - virtual_input.update_key_binding(event.physical_key, new_key_state); - } - } - } -} diff --git a/src/core/input/mod.rs b/src/core/input/mod.rs index 00ebe18..08c1862 100644 --- a/src/core/input/mod.rs +++ b/src/core/input/mod.rs @@ -1,12 +1,13 @@ use std::collections::HashMap; -use keyboard_state::KeyboardState; -use mouse_state::MouseState; +use cache::{CachedElementState, CachedMovement}; use virtual_input::VirtualInput; -use winit::event::{ElementState, WindowEvent}; +use winit::{ + event::{MouseButton, MouseScrollDelta, WindowEvent}, + keyboard::PhysicalKey, +}; -mod keyboard_state; -mod mouse_state; +mod cache; mod virtual_binding; mod virtual_input; mod virtual_state; @@ -14,8 +15,10 @@ pub use virtual_binding::{AxisDirection, VirtualBinding}; #[derive(Default)] pub struct InputManager { - keyboard_state: KeyboardState, - mouse_state: MouseState, + keys_state: CachedElementState, + mouse_buttons_state: CachedElementState, + mouse_position_delta: CachedMovement, + mouse_wheel_delta: CachedMovement, virtual_input: VirtualInput, } @@ -41,18 +44,45 @@ impl InputManager { WindowEvent::AxisMotion { axis, value, .. } => { self.virtual_input.update_axis_binding(*axis, *value as f32); } - _ => { - self.keyboard_state - .process_window_event(event, &mut self.virtual_input); - self.mouse_state - .process_window_event(event, &mut self.virtual_input); + WindowEvent::KeyboardInput { event, .. } => { + let new_key_state = self + .keys_state + .set_key_state(event.physical_key, event.state); + if let Some(new_key_state) = new_key_state { + self.virtual_input + .update_key_binding(event.physical_key, new_key_state); + } } + WindowEvent::CursorMoved { position, .. } => { + self.mouse_position_delta + .set_value(glam::Vec2::new(position.x as f32, position.y as f32)); + } + WindowEvent::MouseInput { button, state, .. } => { + let new_mouse_button_state = + self.mouse_buttons_state.set_key_state(*button, *state); + if let Some(new_mouse_button_state) = new_mouse_button_state { + self.virtual_input + .update_mouse_button_binding(*button, new_mouse_button_state); + } + } + WindowEvent::MouseWheel { delta, .. } => { + self.mouse_wheel_delta += match delta { + MouseScrollDelta::PixelDelta(position) => { + glam::Vec2::new(position.x as f32, position.y as f32) + } + MouseScrollDelta::LineDelta(x, y) => glam::Vec2::new(*x as f32, *y as f32), + }; + } + _ => {} } } /// Updates deltas before running update pub fn update(&mut self) { - self.mouse_state.update(&mut self.virtual_input); + self.virtual_input + .update_mouse_move_binding(&self.mouse_position_delta.reset()); + self.virtual_input + .update_mouse_wheel_binding(&self.mouse_wheel_delta.reset()); } pub fn get_virtual_input_state(&self, value_name: &str) -> f32 { @@ -63,33 +93,3 @@ impl InputManager { self.virtual_input.add_bindings(value_name, bindings); } } - -/// Maps the old element state to the new element state. -/// if is changed, returns Some(new_state), otherwise returns None -#[inline] -fn process_new_element_state( - old: Option<&ElementState>, - new: ElementState, -) -> Option { - match old { - Some(old) => match new { - ElementState::Pressed => match old { - ElementState::Released => Some(ElementState::Pressed), - ElementState::Pressed => None, - }, - ElementState::Released => match old { - ElementState::Released => None, - ElementState::Pressed => Some(ElementState::Released), - }, - }, - None => match new { - ElementState::Pressed => Some(ElementState::Pressed), - ElementState::Released => Some(ElementState::Released), - }, - } -} - -#[inline] -fn process_axis_deadzone(value: f32, deadzone: f32) -> f32 { - if value.abs() < deadzone { 0.0 } else { value } -} diff --git a/src/core/input/mouse_state.rs b/src/core/input/mouse_state.rs deleted file mode 100644 index 084f43c..0000000 --- a/src/core/input/mouse_state.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::collections::HashMap; - -use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent}; - -use super::{process_new_element_state, virtual_input::VirtualInput}; - -#[derive(Debug, Default)] -pub struct MouseState { - old_position: glam::Vec2, - position: glam::Vec2, - delta: glam::Vec2, - wheel_delta: glam::Vec2, - mouse_button_state: HashMap, -} - -impl MouseState { - pub fn process_window_event(&mut self, event: &WindowEvent, virtual_input: &mut VirtualInput) { - match event { - WindowEvent::CursorMoved { position, .. } => { - self.position = glam::Vec2::new(position.x as f32, position.y as f32); - } - WindowEvent::MouseWheel { delta, .. } => { - self.wheel_delta += match delta { - MouseScrollDelta::PixelDelta(position) => { - glam::Vec2::new(position.x as f32, position.y as f32) - } - MouseScrollDelta::LineDelta(x, y) => glam::Vec2::new(*x as f32, *y as f32), - }; - } - WindowEvent::MouseInput { button, state, .. } => { - let key_state = self.mouse_button_state.get(button); - let new_key_state = process_new_element_state(key_state, *state); - if let Some(new_key_state) = new_key_state { - self.mouse_button_state.insert(*button, new_key_state); - virtual_input.update_mouse_button_binding(*button, new_key_state); - } - } - _ => {} - } - } - - pub fn update(&mut self, virtual_input: &mut VirtualInput) { - self.delta = self.position - self.old_position; - self.old_position = self.position; - virtual_input.update_mouse_move_binding(&self.delta); - virtual_input.update_mouse_wheel_binding(&self.wheel_delta); - self.wheel_delta = glam::Vec2::ZERO; - } -} diff --git a/src/core/input/virtual_state.rs b/src/core/input/virtual_state.rs index 93cf28f..779b8ea 100644 --- a/src/core/input/virtual_state.rs +++ b/src/core/input/virtual_state.rs @@ -3,7 +3,7 @@ use winit::{ keyboard::PhysicalKey, }; -use super::{process_axis_deadzone, virtual_binding::VirtualBinding}; +use super::virtual_binding::VirtualBinding; pub struct VirtualBindingState { pub value: f32, @@ -101,3 +101,8 @@ impl VirtualInputState { self.value = new_value; } } + +#[inline] +fn process_axis_deadzone(value: f32, deadzone: f32) -> f32 { + if value.abs() < deadzone { 0.0 } else { value } +} From 29a4da5666c6ef8ea020afa93618af939df4b088 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 27 May 2025 22:25:17 +0200 Subject: [PATCH 73/91] texture: First image load --- Cargo.lock | 1 + Cargo.toml | 2 + res/shaders/vertex.frag | 9 +- res/shaders/vertex.vert | 6 +- res/textures/wooden-crate.jpg | Bin 0 -> 49767 bytes src/core/render/mod.rs | 1 + .../render/pipelines/triangle_pipeline.rs | 46 +++++-- src/core/render/primitives/vertex.rs | 4 +- src/core/render/texture.rs | 113 ++++++++++++++++++ src/game/main_scene.rs | 106 ++++++++-------- src/main.rs | 4 +- 11 files changed, 220 insertions(+), 72 deletions(-) create mode 100644 res/textures/wooden-crate.jpg create mode 100644 src/core/render/texture.rs diff --git a/Cargo.lock b/Cargo.lock index d461d0b..54175c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2110,6 +2110,7 @@ dependencies = [ "egui_winit_vulkano", "env_logger", "glam", + "image", "log", "thiserror 2.0.12", "vulkano", diff --git a/Cargo.toml b/Cargo.toml index 372b1a5..50be745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ vulkano-shaders = "0.35" vulkano-util = "0.35" egui_winit_vulkano = { version = "0.28" } +image = { version = "0.25", features = ["png", "jpeg"] } + # Math glam = { version = "0.30" } diff --git a/res/shaders/vertex.frag b/res/shaders/vertex.frag index 720d192..67831a9 100644 --- a/res/shaders/vertex.frag +++ b/res/shaders/vertex.frag @@ -1,9 +1,12 @@ #version 450 -layout (location = 0) in vec3 color; +layout (location = 0) in vec2 tex_coords; layout (location = 0) out vec4 f_color; +layout(set = 1, binding = 0) uniform sampler mySampler; +layout(set = 1, binding = 1) uniform texture2D myTexture; + void main() { - f_color = vec4(color, 1.0); -} \ No newline at end of file + f_color = texture(sampler2D(myTexture, mySampler), tex_coords); +} diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index bb1c261..0445e54 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1,9 +1,9 @@ #version 450 layout (location = 0) in vec2 position; -layout (location = 1) in vec3 color; +layout (location = 1) in vec2 uv; -layout (location = 0) out vec3 fragColor; +layout (location = 0) out vec2 fragUv; layout (set = 0, binding = 0) uniform MVP { mat4 world; @@ -14,5 +14,5 @@ layout (set = 0, binding = 0) uniform MVP { void main() { mat4 worldview = uniforms.view * uniforms.world; gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0); - fragColor = color; + fragUv = uv; } diff --git a/res/textures/wooden-crate.jpg b/res/textures/wooden-crate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1c87341ca55543742aeefa9145fea732c166468 GIT binary patch literal 49767 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::new(); - let mut descriptor_set_layout_binding = - DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer); - descriptor_set_layout_binding.stages = ShaderStages::VERTEX; - bindings.insert(0, descriptor_set_layout_binding); + let vertex_bindings = BTreeMap::::from_iter([( + 0, + DescriptorSetLayoutBinding { + stages: ShaderStages::VERTEX, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer) + }, + )]); + let fragment_bindings = BTreeMap::::from_iter([ + ( + 0, + DescriptorSetLayoutBinding { + stages: ShaderStages::FRAGMENT, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler) + }, + ), + ( + 1, + DescriptorSetLayoutBinding { + stages: ShaderStages::FRAGMENT, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage) + }, + ), + ]); - let descriptor_set_layout = DescriptorSetLayoutCreateInfo { - bindings, + let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: vertex_bindings, + ..Default::default() + }; + + let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: fragment_bindings, ..Default::default() }; let create_info = PipelineDescriptorSetLayoutCreateInfo { - set_layouts: vec![descriptor_set_layout], + set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout], flags: PipelineLayoutCreateFlags::default(), push_constant_ranges: vec![], } @@ -83,7 +106,10 @@ pub fn create_triangle_pipeline( GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), viewport_state: Some(ViewportState::default()), rasterization_state: Some(RasterizationState::default()), multisample_state: Some(MultisampleState::default()), diff --git a/src/core/render/primitives/vertex.rs b/src/core/render/primitives/vertex.rs index 9bf133e..b35588c 100644 --- a/src/core/render/primitives/vertex.rs +++ b/src/core/render/primitives/vertex.rs @@ -12,8 +12,8 @@ pub struct Vertex2D { #[format(R32G32_SFLOAT)] pub position: [f32; 2], - #[format(R32G32B32_SFLOAT)] - pub color: [f32; 3], + #[format(R32G32_SFLOAT)] + pub uv: [f32; 2], } impl Vertex2D { diff --git a/src/core/render/texture.rs b/src/core/render/texture.rs new file mode 100644 index 0000000..680ddbf --- /dev/null +++ b/src/core/render/texture.rs @@ -0,0 +1,113 @@ +use std::{path::Path, sync::Arc}; + +use anyhow::Error; +use image::{DynamicImage, EncodableLayout}; +use vulkano::{ + buffer::{Buffer, BufferCreateInfo, BufferUsage}, + command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer}, + device::Device, + format::Format, + image::{ + Image, ImageCreateInfo, ImageType, ImageUsage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + }, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, +}; + +pub struct Texture { + texture: Arc, + sampler: Arc, +} + +impl Texture { + fn new(texture: Arc, sampler: Arc) -> Self { + Self { texture, sampler } + } + + pub fn from_file( + device: &Arc, + memory_allocator: &Arc, + builder: &mut AutoCommandBufferBuilder, + path: &str, + ) -> Result { + let image = image::open(path)?; + Self::from_dynamic_image(device, memory_allocator, builder, image) + } + + pub fn from_bytes( + device: &Arc, + memory_allocator: &Arc, + builder: &mut AutoCommandBufferBuilder, + bytes: &[u8], + ) -> Result { + let image = image::load_from_memory(bytes)?; + Self::from_dynamic_image(device, memory_allocator, builder, image) + } + + pub fn from_dynamic_image( + device: &Arc, + memory_allocator: &Arc, + builder: &mut AutoCommandBufferBuilder, + image: DynamicImage, + ) -> Result { + let image_data = image.to_rgba8(); + let image_dimensions = image_data.dimensions(); + + let upload_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + image_data.to_vec(), + )?; + + let image = Image::new( + memory_allocator.clone(), + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_SRGB, + extent: [image_dimensions.0 as u32, image_dimensions.1 as u32, 1], + array_layers: 1, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + )?; + + builder.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + ))?; + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + )?; + + let image_view = ImageView::new_default(image)?; + + log::trace!("Texture loaded with dimensions {:?}", image_dimensions); + + Ok(Self::new(image_view, sampler)) + } + + pub fn get_texture(&self) -> &Arc { + &self.texture + } + + pub fn get_sampler(&self) -> &Arc { + &self.sampler + } +} diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 1f27f0b..c5a56a0 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -3,67 +3,35 @@ use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline; use crate::core::render::primitives::camera::Camera; use crate::core::render::primitives::vertex::Vertex2D; use crate::core::render::render_context::RenderContext; +use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::timer::Timer; use glam::{Mat4, Quat, Vec3}; use std::sync::Arc; use vulkano::buffer::Subbuffer; -use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; +use vulkano::command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + PrimaryCommandBufferAbstract, +}; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -const VERTICES: [Vertex2D; 12] = [ - // Triangle en haut à gauche +const VERTICES: [Vertex2D; 4] = [ Vertex2D { - position: [-0.5, -0.75], - color: [1.0, 0.0, 0.0], + position: [-0.5, -0.5], + uv: [0.0, 0.0], }, Vertex2D { - position: [-0.75, -0.25], - color: [0.0, 1.0, 0.0], + position: [-0.5, 0.5], + uv: [0.0, 1.0], }, Vertex2D { - position: [-0.25, -0.25], - color: [0.0, 0.0, 1.0], - }, - // Triangle en bas à gauche - Vertex2D { - position: [-0.5, 0.25], - color: [0.5, 0.5, 0.5], + position: [0.5, -0.5], + uv: [1.0, 0.0], }, Vertex2D { - position: [-0.75, 0.75], - color: [0.2, 0.8, 0.2], - }, - Vertex2D { - position: [-0.25, 0.75], - color: [0.8, 0.2, 0.2], - }, - // Triangle en haut à droite - Vertex2D { - position: [0.5, -0.75], - color: [1.0, 1.0, 0.0], - }, - Vertex2D { - position: [0.25, -0.25], - color: [0.0, 1.0, 1.0], - }, - Vertex2D { - position: [0.75, -0.25], - color: [1.0, 0.0, 1.0], - }, - // Triangle en bas à droite - Vertex2D { - position: [0.5, 0.25], - color: [0.1, 0.5, 0.8], - }, - Vertex2D { - position: [0.25, 0.75], - color: [0.8, 0.6, 0.1], - }, - Vertex2D { - position: [0.75, 0.75], - color: [0.3, 0.4, 0.6], + position: [0.5, 0.5], + uv: [1.0, 1.0], }, ]; @@ -71,6 +39,7 @@ pub struct MainSceneState { pipeline: Arc, vertex_buffer: Subbuffer<[Vertex2D]>, camera: Camera, + texture: Texture, } #[derive(Default)] @@ -105,11 +74,33 @@ impl Scene for MainScene { ), ); + let mut uploads = AutoCommandBufferBuilder::primary( + render_context.command_buffer_allocator().clone(), + render_context.graphics_queue().queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); + + let texture = Texture::from_file( + render_context.device(), + render_context.memory_allocator(), + &mut uploads, + "res/textures/wooden-crate.jpg", + ) + .unwrap(); + + let _ = uploads + .build() + .unwrap() + .execute(render_context.graphics_queue().clone()) + .unwrap(); + self.state = Some(MainSceneState { pipeline, vertex_buffer, camera, - }) + texture, + }); } fn update( @@ -157,21 +148,32 @@ impl Scene for MainScene { ) { let state = self.state.as_ref().unwrap(); let vertex_count = state.vertex_buffer.len() as u32; - let instance_count = vertex_count / 3; + let instance_count = vertex_count / 4; - let layout = &state.pipeline.layout().set_layouts()[0]; + let layouts = state.pipeline.layout().set_layouts(); let uniform_buffer = state .camera .create_buffer(render_context.memory_allocator()) .unwrap(); - let descriptor_set = DescriptorSet::new( + let uniform_descriptor_set = DescriptorSet::new( render_context.descriptor_set_allocator().clone(), - layout.clone(), + layouts[0].clone(), [WriteDescriptorSet::buffer(0, uniform_buffer)], [], ) .unwrap(); + let texture_descriptor_set = DescriptorSet::new( + render_context.descriptor_set_allocator().clone(), + layouts[1].clone(), + [ + WriteDescriptorSet::sampler(0, state.texture.get_sampler().clone()), + WriteDescriptorSet::image_view(1, state.texture.get_texture().clone()), + ], + [], + ) + .unwrap(); + unsafe { builder .bind_pipeline_graphics(state.pipeline.clone()) @@ -180,7 +182,7 @@ impl Scene for MainScene { PipelineBindPoint::Graphics, state.pipeline.layout().clone(), 0, - descriptor_set, + vec![uniform_descriptor_set, texture_descriptor_set], ) .unwrap() .bind_vertex_buffers(0, state.vertex_buffer.clone()) diff --git a/src/main.rs b/src/main.rs index 6a4984b..7a4284c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ fn main() { vec![ VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Normal), VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Invert), - VirtualBinding::Axis(0, AxisDirection::Normal, 1.0), + VirtualBinding::Axis(0, AxisDirection::Normal, 0.0), ], ), ( @@ -29,7 +29,7 @@ fn main() { vec![ VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Normal), VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Invert), - VirtualBinding::Axis(1, AxisDirection::Normal, 1.0), + VirtualBinding::Axis(1, AxisDirection::Normal, 0.0), ], ), ( From a0fce9c08e9edb0bb92eee078044e54d3556f907 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 27 May 2025 22:32:44 +0200 Subject: [PATCH 74/91] texture: Avoid clone image --- src/core/render/texture.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/render/texture.rs b/src/core/render/texture.rs index 680ddbf..17e9247 100644 --- a/src/core/render/texture.rs +++ b/src/core/render/texture.rs @@ -54,7 +54,7 @@ impl Texture { let image_data = image.to_rgba8(); let image_dimensions = image_data.dimensions(); - let upload_buffer = Buffer::from_iter( + let upload_buffer = Buffer::new_slice::( memory_allocator.clone(), BufferCreateInfo { usage: BufferUsage::TRANSFER_SRC, @@ -65,9 +65,14 @@ impl Texture { | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - image_data.to_vec(), + image_data.len() as u64, )?; + { + let buffer_data = &mut *upload_buffer.write()?; + buffer_data.copy_from_slice(image_data.as_bytes()); + } + let image = Image::new( memory_allocator.clone(), ImageCreateInfo { From 09bfe6fb488fc35ef70a1293af94d02b44f1ee7c Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 27 May 2025 23:31:23 +0200 Subject: [PATCH 75/91] camera: Try fix camera --- src/core/render/primitives/camera.rs | 45 ++++++++++++++---- src/game/main_scene.rs | 68 ++++++++++++++++++---------- 2 files changed, 79 insertions(+), 34 deletions(-) diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs index efd61e5..16ae79b 100644 --- a/src/core/render/primitives/camera.rs +++ b/src/core/render/primitives/camera.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use glam::Mat4; +use glam::{EulerRot, Mat4, Quat, Vec3}; use vulkano::{ Validated, buffer::{AllocateBufferError, Subbuffer}, @@ -14,7 +14,8 @@ pub struct Camera { view: Mat4, projection: Mat4, - transform: Transform, + position: Vec3, + rotation: Vec3, } impl Camera { @@ -22,27 +23,53 @@ impl Camera { Self { view, projection, - transform: Transform::default(), + position: Vec3::ZERO, + rotation: Vec3::ZERO, } } - pub fn get_transform(&self) -> &Transform { - &self.transform + pub fn rotate(&mut self, rotation: Vec3) { + self.rotation += rotation; } - pub fn get_transform_mut(&mut self) -> &mut Transform { - &mut self.transform + pub fn translate(&mut self, translation: Vec3) { + self.position += translation; + } + + pub fn set_view(&mut self, view: Mat4) { + self.view = view; } pub fn set_projection(&mut self, projection: Mat4) { self.projection = projection; } + pub fn get_rotation(&self) -> Vec3 { + self.rotation + } + + pub fn set_rotation(&mut self, rotation: Vec3) { + self.rotation = rotation; + } + + pub fn get_position(&self) -> Vec3 { + self.position + } + + pub fn set_position(&mut self, position: Vec3) { + self.position = position; + } + pub fn create_buffer( &self, memory_allocator: &Arc, ) -> Result, Validated> { - MVP::new(&self.transform.get_mat4(), &self.view, &self.projection) - .into_buffer(memory_allocator) + let mut world_matrix = Mat4::IDENTITY; + world_matrix *= Mat4::from_quat(Quat::from_rotation_z(self.rotation.z)); + world_matrix *= Mat4::from_quat(Quat::from_rotation_y(self.rotation.y)); + world_matrix *= Mat4::from_quat(Quat::from_rotation_x(self.rotation.x)); + world_matrix *= Mat4::from_translation(-self.position); + + MVP::new(&world_matrix, &self.view, &self.projection).into_buffer(memory_allocator) } } diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index c5a56a0..3b0ed93 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -6,7 +6,7 @@ use crate::core::render::render_context::RenderContext; use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::timer::Timer; -use glam::{Mat4, Quat, Vec3}; +use glam::{Mat4, Vec3}; use std::sync::Arc; use vulkano::buffer::Subbuffer; use vulkano::command_buffer::{ @@ -18,20 +18,20 @@ use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; const VERTICES: [Vertex2D; 4] = [ Vertex2D { - position: [-0.5, -0.5], + position: [0.0, 0.0], uv: [0.0, 0.0], }, Vertex2D { - position: [-0.5, 0.5], - uv: [0.0, 1.0], + position: [0.0, 5.0], + uv: [0.0, 0.5], }, Vertex2D { - position: [0.5, -0.5], + position: [10.0, 0.0], uv: [1.0, 0.0], }, Vertex2D { - position: [0.5, 0.5], - uv: [1.0, 1.0], + position: [10.0, 5.0], + uv: [1.0, 0.5], }, ]; @@ -40,6 +40,7 @@ pub struct MainSceneState { vertex_buffer: Subbuffer<[Vertex2D]>, camera: Camera, texture: Texture, + speed: f32, } #[derive(Default)] @@ -60,7 +61,7 @@ impl Scene for MainScene { Vertex2D::create_buffer(Vec::from_iter(VERTICES), render_context.memory_allocator()) .unwrap(); - let camera = Camera::new( + let mut camera = Camera::new( Mat4::look_at_rh( Vec3::new(0.3, 0.3, 1.0), Vec3::new(0.0, 0.0, 0.0), @@ -73,6 +74,7 @@ impl Scene for MainScene { 100.0, ), ); + camera.set_position(Vec3::new(-10.0, 0.0, -10.0)); let mut uploads = AutoCommandBufferBuilder::primary( render_context.command_buffer_allocator().clone(), @@ -100,6 +102,7 @@ impl Scene for MainScene { vertex_buffer, camera, texture, + speed: 50.0, }); } @@ -111,27 +114,42 @@ impl Scene for MainScene { ) { let state = self.state.as_mut().unwrap(); - let speed = 50.0 * timer.delta_time(); + state.speed += input_manager.get_virtual_input_state("mouse_wheel") * 10.0; - let mut rot = Quat::default(); - rot *= Quat::from_rotation_y( - input_manager.get_virtual_input_state("mouse_x") * speed.to_radians(), - ); - rot *= Quat::from_rotation_x( - input_manager.get_virtual_input_state("mouse_y") * speed.to_radians(), - ); - state.camera.get_transform_mut().rotate(rot); + let speed = state.speed * timer.delta_time(); - let translation_x = - input_manager.get_virtual_input_state("move_right") * timer.delta_time() * speed; + state.camera.rotate(Vec3::new( + (input_manager.get_virtual_input_state("mouse_y") * 50.0 * timer.delta_time()) + .to_radians(), + (input_manager.get_virtual_input_state("mouse_x") * 50.0 * timer.delta_time()) + .to_radians(), + 0.0, + )); - let translation_z = - input_manager.get_virtual_input_state("move_forward") * timer.delta_time() * speed; + if state.camera.get_rotation().x > 89.0 { + state + .camera + .set_rotation(Vec3::new(89.0, state.camera.get_rotation().y, 0.0)); + } - state - .camera - .get_transform_mut() - .translate(Vec3::new(translation_x, 0.0, translation_z)); + if state.camera.get_rotation().x < -89.0 { + state + .camera + .set_rotation(Vec3::new(-89.0, state.camera.get_rotation().y, 0.0)); + } + + let rotation = state.camera.get_rotation(); + let mut translation = Vec3::ZERO; + + let tx = input_manager.get_virtual_input_state("move_right") * timer.delta_time() * speed; + translation.x += tx * (rotation.y).to_radians().cos(); + translation.z += tx * (rotation.y).to_radians().sin(); + + let ty = input_manager.get_virtual_input_state("move_forward") * timer.delta_time() * speed; + translation.x += ty * (rotation.y + 90.0).to_radians().cos(); + translation.z += ty * (rotation.y + 90.0).to_radians().sin(); + + state.camera.translate(translation); state.camera.set_projection(Mat4::perspective_rh_gl( std::f32::consts::FRAC_PI_2, From fbb1493b45783e73166392034e6a52eff0a9a7a9 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 28 May 2025 13:41:13 +0200 Subject: [PATCH 76/91] Fixes --- src/core/app.rs | 13 +++- src/core/input/mod.rs | 15 ++-- .../render/pipelines/triangle_pipeline.rs | 1 + src/core/render/primitives/camera.rs | 74 +++++++++++-------- src/game/main_scene.rs | 41 +--------- 5 files changed, 68 insertions(+), 76 deletions(-) diff --git a/src/core/app.rs b/src/core/app.rs index d14b4c5..e05d971 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -19,7 +19,7 @@ use vulkano_util::context::VulkanoContext; use vulkano_util::renderer::VulkanoWindowRenderer; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; -use winit::event::WindowEvent; +use winit::event::{DeviceEvent, WindowEvent}; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; @@ -76,6 +76,8 @@ impl ApplicationHandler for App { width: 800.0, height: 600.0, present_mode: PresentMode::Fifo, + cursor_visible: false, + cursor_locked: true, ..Default::default() }, |_| {}, @@ -101,6 +103,15 @@ impl ApplicationHandler for App { .load_scene(Box::new(MainScene::default())); } + fn device_event( + &mut self, + _event_loop: &ActiveEventLoop, + _device_id: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) { + self.input_manager.process_device_event(&event); + } + fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { let renderer = Arc::get_mut(&mut self.vulkano_windows) .unwrap() diff --git a/src/core/input/mod.rs b/src/core/input/mod.rs index 08c1862..227a5a1 100644 --- a/src/core/input/mod.rs +++ b/src/core/input/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use cache::{CachedElementState, CachedMovement}; use virtual_input::VirtualInput; use winit::{ - event::{MouseButton, MouseScrollDelta, WindowEvent}, + event::{DeviceEvent, MouseButton, MouseScrollDelta, WindowEvent}, keyboard::PhysicalKey, }; @@ -39,6 +39,15 @@ impl InputManager { input_manager } + pub fn process_device_event(&mut self, event: &DeviceEvent) { + match event { + DeviceEvent::MouseMotion { delta, .. } => { + self.mouse_position_delta += glam::Vec2::new(delta.0 as f32, delta.1 as f32); + } + _ => {} + } + } + pub fn process_window_event(&mut self, event: &WindowEvent) { match event { WindowEvent::AxisMotion { axis, value, .. } => { @@ -53,10 +62,6 @@ impl InputManager { .update_key_binding(event.physical_key, new_key_state); } } - WindowEvent::CursorMoved { position, .. } => { - self.mouse_position_delta - .set_value(glam::Vec2::new(position.x as f32, position.y as f32)); - } WindowEvent::MouseInput { button, state, .. } => { let new_mouse_button_state = self.mouse_buttons_state.set_key_state(*button, *state); diff --git a/src/core/render/pipelines/triangle_pipeline.rs b/src/core/render/pipelines/triangle_pipeline.rs index 43d00d5..8893f7e 100644 --- a/src/core/render/pipelines/triangle_pipeline.rs +++ b/src/core/render/pipelines/triangle_pipeline.rs @@ -46,6 +46,7 @@ pub fn create_triangle_pipeline( ) -> Result, Box> { let (vs, fs) = load_shaders(device)?; let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; + log::trace!("vertex_input_state: {:#?}", vertex_input_state); let stages = [ PipelineShaderStageCreateInfo::new(vs), diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs index 16ae79b..c322c7c 100644 --- a/src/core/render/primitives/camera.rs +++ b/src/core/render/primitives/camera.rs @@ -1,13 +1,15 @@ -use std::sync::Arc; +use std::{f32::consts::PI, sync::Arc}; -use glam::{EulerRot, Mat4, Quat, Vec3}; +use glam::{Mat4, Quat, Vec3}; use vulkano::{ Validated, buffer::{AllocateBufferError, Subbuffer}, memory::allocator::StandardMemoryAllocator, }; -use super::{mvp::MVP, transform::Transform}; +use crate::core::{input::InputManager, timer::Timer}; + +use super::mvp::MVP; #[derive(Default)] pub struct Camera { @@ -28,47 +30,57 @@ impl Camera { } } - pub fn rotate(&mut self, rotation: Vec3) { - self.rotation += rotation; - } + pub fn update( + &mut self, + input_manager: &InputManager, + timer: &Timer, + movement_speed: f32, + camera_sensitivity: f32, + ) { + // Process camera rotation + let camera_delta = camera_sensitivity * timer.delta_time(); + self.rotation += Vec3::new( + -(input_manager.get_virtual_input_state("mouse_y") * camera_delta).to_radians(), + (input_manager.get_virtual_input_state("mouse_x") * camera_delta).to_radians(), + 0.0, + ); - pub fn translate(&mut self, translation: Vec3) { - self.position += translation; - } + if self.rotation.x > 90.0 { + self.rotation = Vec3::new(90.0, self.rotation.y, 0.0); + } - pub fn set_view(&mut self, view: Mat4) { - self.view = view; + if self.rotation.x < -90.0 { + self.rotation = Vec3::new(-90.0, self.rotation.y, 0.0); + } + + let movement_delta = movement_speed * timer.delta_time(); + + let tx = input_manager.get_virtual_input_state("move_right") * movement_delta; + let tz = input_manager.get_virtual_input_state("move_forward") * movement_delta; + + self.position.x += tx * (self.rotation.y).cos(); + self.position.z += tx * (self.rotation.y).sin(); + + self.position.x += tz * (self.rotation.y + PI / 2.0).cos(); + self.position.z += tz * (self.rotation.y + PI / 2.0).sin(); } pub fn set_projection(&mut self, projection: Mat4) { self.projection = projection; } - pub fn get_rotation(&self) -> Vec3 { - self.rotation - } - - pub fn set_rotation(&mut self, rotation: Vec3) { - self.rotation = rotation; - } - - pub fn get_position(&self) -> Vec3 { - self.position - } - - pub fn set_position(&mut self, position: Vec3) { - self.position = position; - } - pub fn create_buffer( &self, memory_allocator: &Arc, ) -> Result, Validated> { let mut world_matrix = Mat4::IDENTITY; - world_matrix *= Mat4::from_quat(Quat::from_rotation_z(self.rotation.z)); - world_matrix *= Mat4::from_quat(Quat::from_rotation_y(self.rotation.y)); - world_matrix *= Mat4::from_quat(Quat::from_rotation_x(self.rotation.x)); - world_matrix *= Mat4::from_translation(-self.position); + world_matrix *= Mat4::from_quat(Quat::from_euler( + glam::EulerRot::XYX, + self.rotation.x, + self.rotation.y, + self.rotation.z, + )); + world_matrix *= Mat4::from_translation(self.position); MVP::new(&world_matrix, &self.view, &self.projection).into_buffer(memory_allocator) } diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 3b0ed93..38c5bc5 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -61,7 +61,7 @@ impl Scene for MainScene { Vertex2D::create_buffer(Vec::from_iter(VERTICES), render_context.memory_allocator()) .unwrap(); - let mut camera = Camera::new( + let camera = Camera::new( Mat4::look_at_rh( Vec3::new(0.3, 0.3, 1.0), Vec3::new(0.0, 0.0, 0.0), @@ -74,7 +74,6 @@ impl Scene for MainScene { 100.0, ), ); - camera.set_position(Vec3::new(-10.0, 0.0, -10.0)); let mut uploads = AutoCommandBufferBuilder::primary( render_context.command_buffer_allocator().clone(), @@ -113,43 +112,7 @@ impl Scene for MainScene { timer: &Timer, ) { let state = self.state.as_mut().unwrap(); - - state.speed += input_manager.get_virtual_input_state("mouse_wheel") * 10.0; - - let speed = state.speed * timer.delta_time(); - - state.camera.rotate(Vec3::new( - (input_manager.get_virtual_input_state("mouse_y") * 50.0 * timer.delta_time()) - .to_radians(), - (input_manager.get_virtual_input_state("mouse_x") * 50.0 * timer.delta_time()) - .to_radians(), - 0.0, - )); - - if state.camera.get_rotation().x > 89.0 { - state - .camera - .set_rotation(Vec3::new(89.0, state.camera.get_rotation().y, 0.0)); - } - - if state.camera.get_rotation().x < -89.0 { - state - .camera - .set_rotation(Vec3::new(-89.0, state.camera.get_rotation().y, 0.0)); - } - - let rotation = state.camera.get_rotation(); - let mut translation = Vec3::ZERO; - - let tx = input_manager.get_virtual_input_state("move_right") * timer.delta_time() * speed; - translation.x += tx * (rotation.y).to_radians().cos(); - translation.z += tx * (rotation.y).to_radians().sin(); - - let ty = input_manager.get_virtual_input_state("move_forward") * timer.delta_time() * speed; - translation.x += ty * (rotation.y + 90.0).to_radians().cos(); - translation.z += ty * (rotation.y + 90.0).to_radians().sin(); - - state.camera.translate(translation); + state.camera.update(input_manager, timer, state.speed, 10.0); state.camera.set_projection(Mat4::perspective_rh_gl( std::f32::consts::FRAC_PI_2, From 122f577a26db1273481171f946cadf63f1757538 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 28 May 2025 13:47:10 +0200 Subject: [PATCH 77/91] texture: Let compiler type inference --- src/core/render/texture.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/render/texture.rs b/src/core/render/texture.rs index 17e9247..ddf88e5 100644 --- a/src/core/render/texture.rs +++ b/src/core/render/texture.rs @@ -54,7 +54,7 @@ impl Texture { let image_data = image.to_rgba8(); let image_dimensions = image_data.dimensions(); - let upload_buffer = Buffer::new_slice::( + let upload_buffer = Buffer::new_slice( memory_allocator.clone(), BufferCreateInfo { usage: BufferUsage::TRANSFER_SRC, @@ -70,7 +70,7 @@ impl Texture { { let buffer_data = &mut *upload_buffer.write()?; - buffer_data.copy_from_slice(image_data.as_bytes()); + buffer_data.copy_from_slice(image_data.as_raw()); } let image = Image::new( From 131811a539723873ab4ff78da75c2f1e53e64b0d Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 28 May 2025 22:39:56 +0200 Subject: [PATCH 78/91] pipeline: Refactor square pipeline + add support of indexes --- src/core/app.rs | 2 +- src/core/render/mod.rs | 1 - src/core/render/pipelines/mod.rs | 1 - .../render/pipelines/triangle_pipeline.rs | 140 ---------- src/core/render/primitives/mod.rs | 2 +- src/game/assets/mod.rs | 1 + src/game/assets/square.rs | 247 ++++++++++++++++++ src/game/main_scene.rs | 92 ++----- src/game/mod.rs | 1 + 9 files changed, 270 insertions(+), 217 deletions(-) delete mode 100644 src/core/render/pipelines/mod.rs delete mode 100644 src/core/render/pipelines/triangle_pipeline.rs create mode 100644 src/game/assets/mod.rs create mode 100644 src/game/assets/square.rs diff --git a/src/core/app.rs b/src/core/app.rs index e05d971..bcdb82d 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -19,7 +19,7 @@ use vulkano_util::context::VulkanoContext; use vulkano_util::renderer::VulkanoWindowRenderer; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; -use winit::event::{DeviceEvent, WindowEvent}; +use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index 0a718e4..a50c5da 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,4 +1,3 @@ -pub mod pipelines; pub mod primitives; pub mod render_context; pub mod texture; diff --git a/src/core/render/pipelines/mod.rs b/src/core/render/pipelines/mod.rs deleted file mode 100644 index e5f30a7..0000000 --- a/src/core/render/pipelines/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod triangle_pipeline; diff --git a/src/core/render/pipelines/triangle_pipeline.rs b/src/core/render/pipelines/triangle_pipeline.rs deleted file mode 100644 index 8893f7e..0000000 --- a/src/core/render/pipelines/triangle_pipeline.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::collections::BTreeMap; -use std::error::Error; -use std::sync::Arc; -use vulkano::descriptor_set::layout::{ - DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, -}; -use vulkano::device::Device; -use vulkano::format::Format; -use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; -use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; -use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology}; -use vulkano::pipeline::graphics::multisample::MultisampleState; -use vulkano::pipeline::graphics::rasterization::RasterizationState; -use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; -use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; -use vulkano::pipeline::graphics::viewport::ViewportState; -use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; -use vulkano::pipeline::{ - DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, -}; -use vulkano::shader::{EntryPoint, ShaderStages}; - -use crate::core::render::primitives::vertex::Vertex2D; - -pub mod shaders { - pub mod vs { - vulkano_shaders::shader! { - ty: "vertex", - path: r"res/shaders/vertex.vert", - generate_structs: false, - } - } - - pub mod fs { - vulkano_shaders::shader! { - ty: "fragment", - path: r"res/shaders/vertex.frag", - generate_structs: false, - } - } -} - -pub fn create_triangle_pipeline( - device: &Arc, - swapchain_format: Format, -) -> Result, Box> { - let (vs, fs) = load_shaders(device)?; - let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; - log::trace!("vertex_input_state: {:#?}", vertex_input_state); - - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - - let vertex_bindings = BTreeMap::::from_iter([( - 0, - DescriptorSetLayoutBinding { - stages: ShaderStages::VERTEX, - ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer) - }, - )]); - let fragment_bindings = BTreeMap::::from_iter([ - ( - 0, - DescriptorSetLayoutBinding { - stages: ShaderStages::FRAGMENT, - ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler) - }, - ), - ( - 1, - DescriptorSetLayoutBinding { - stages: ShaderStages::FRAGMENT, - ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage) - }, - ), - ]); - - let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo { - bindings: vertex_bindings, - ..Default::default() - }; - - let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo { - bindings: fragment_bindings, - ..Default::default() - }; - - let create_info = PipelineDescriptorSetLayoutCreateInfo { - set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout], - flags: PipelineLayoutCreateFlags::default(), - push_constant_ranges: vec![], - } - .into_pipeline_layout_create_info(device.clone())?; - - let layout = PipelineLayout::new(device.clone(), create_info)?; - - let subpass = PipelineRenderingCreateInfo { - color_attachment_formats: vec![Some(swapchain_format)], - ..Default::default() - }; - - let pipeline = GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.color_attachment_formats.len() as u32, - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - )?; - - Ok(pipeline) -} - -fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> { - let vs = shaders::vs::load(device.clone())? - .entry_point("main") - .ok_or("Failed find main entry point of vertex shader".to_string())?; - - let fs = shaders::fs::load(device.clone())? - .entry_point("main") - .ok_or("Failed find main entry point of fragment shader".to_string())?; - - Ok((vs, fs)) -} diff --git a/src/core/render/primitives/mod.rs b/src/core/render/primitives/mod.rs index fd0b762..606c12d 100644 --- a/src/core/render/primitives/mod.rs +++ b/src/core/render/primitives/mod.rs @@ -1,4 +1,4 @@ pub mod camera; -mod mvp; +pub mod mvp; pub mod transform; pub mod vertex; diff --git a/src/game/assets/mod.rs b/src/game/assets/mod.rs new file mode 100644 index 0000000..d793a66 --- /dev/null +++ b/src/game/assets/mod.rs @@ -0,0 +1 @@ +pub mod square; diff --git a/src/game/assets/square.rs b/src/game/assets/square.rs new file mode 100644 index 0000000..3dba163 --- /dev/null +++ b/src/game/assets/square.rs @@ -0,0 +1,247 @@ +use std::{collections::BTreeMap, error::Error, sync::Arc}; + +use vulkano::{ + buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}, + command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}, + descriptor_set::{ + DescriptorSet, WriteDescriptorSet, + allocator::StandardDescriptorSetAllocator, + layout::{DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType}, + }, + device::Device, + format::Format, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, + pipeline::{ + DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, + PipelineShaderStageCreateInfo, + graphics::{ + GraphicsPipelineCreateInfo, + color_blend::{ColorBlendAttachmentState, ColorBlendState}, + input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, + subpass::PipelineRenderingCreateInfo, + vertex_input::{Vertex, VertexDefinition}, + viewport::ViewportState, + }, + layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}, + }, + shader::ShaderStages, +}; + +use crate::core::render::{ + primitives::{mvp::MVP, vertex::Vertex2D}, + texture::Texture, +}; + +const VERTICES: [Vertex2D; 4] = [ + Vertex2D { + position: [0.0, 0.0], + uv: [0.0, 0.0], + }, + Vertex2D { + position: [0.0, 5.0], + uv: [0.0, 0.5], + }, + Vertex2D { + position: [10.0, 0.0], + uv: [1.0, 0.0], + }, + Vertex2D { + position: [10.0, 5.0], + uv: [1.0, 0.5], + }, +]; + +const INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3]; + +pub mod shaders { + pub mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/vertex.vert", + generate_structs: false, + } + } + + pub mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/vertex.frag", + generate_structs: false, + } + } +} + +pub struct Square { + vertex_buffer: Subbuffer<[Vertex2D]>, + index_buffer: Subbuffer<[u32]>, + pipeline: Arc, +} + +impl Square { + pub fn new( + device: &Arc, + memory_allocator: &Arc, + swapchain_format: Format, + ) -> Result> { + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + Vec::from_iter(VERTICES), + )?; + let index_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::INDEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + Vec::from_iter(INDICES), + )?; + + let vs = shaders::vs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of vertex shader".to_string())?; + + let fs = shaders::fs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of fragment shader".to_string())?; + + let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; + + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + let vertex_bindings = BTreeMap::::from_iter([( + 0, + DescriptorSetLayoutBinding { + stages: ShaderStages::VERTEX, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer) + }, + )]); + let fragment_bindings = BTreeMap::::from_iter([ + ( + 0, + DescriptorSetLayoutBinding { + stages: ShaderStages::FRAGMENT, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler) + }, + ), + ( + 1, + DescriptorSetLayoutBinding { + stages: ShaderStages::FRAGMENT, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage) + }, + ), + ]); + + let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: vertex_bindings, + ..Default::default() + }; + + let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: fragment_bindings, + ..Default::default() + }; + + let create_info = PipelineDescriptorSetLayoutCreateInfo { + set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout], + flags: PipelineLayoutCreateFlags::default(), + push_constant_ranges: vec![], + } + .into_pipeline_layout_create_info(device.clone())?; + + let layout = PipelineLayout::new(device.clone(), create_info)?; + + let subpass = PipelineRenderingCreateInfo { + color_attachment_formats: vec![Some(swapchain_format)], + ..Default::default() + }; + + let pipeline = GraphicsPipeline::new( + device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + )?; + + Ok(Self { + vertex_buffer, + index_buffer, + pipeline, + }) + } + + pub fn render( + &self, + command_buffer: &mut AutoCommandBufferBuilder, + descriptor_set_allocator: &Arc, + mvp_uniform: &Subbuffer<[MVP]>, + texture: &Texture, + ) -> Result<(), Box> { + let layouts = self.pipeline.layout().set_layouts(); + + let uniform_descriptor_set = DescriptorSet::new( + descriptor_set_allocator.clone(), + layouts[0].clone(), + [WriteDescriptorSet::buffer(0, mvp_uniform.clone())], + [], + )?; + + let texture_descriptor_set = DescriptorSet::new( + descriptor_set_allocator.clone(), + layouts[1].clone(), + [ + WriteDescriptorSet::sampler(0, texture.get_sampler().clone()), + WriteDescriptorSet::image_view(1, texture.get_texture().clone()), + ], + [], + )?; + + command_buffer.bind_pipeline_graphics(self.pipeline.clone())?; + command_buffer.bind_descriptor_sets( + PipelineBindPoint::Graphics, + self.pipeline.layout().clone(), + 0, + vec![uniform_descriptor_set, texture_descriptor_set], + )?; + command_buffer.bind_vertex_buffers(0, self.vertex_buffer.clone())?; + command_buffer.bind_index_buffer(self.index_buffer.clone())?; + + unsafe { + command_buffer.draw_indexed(INDICES.len() as u32, 1, 0, 0, 0)?; + } + + Ok(()) + } +} diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 38c5bc5..c3d08ca 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,43 +1,19 @@ use crate::core::input::InputManager; -use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline; use crate::core::render::primitives::camera::Camera; -use crate::core::render::primitives::vertex::Vertex2D; use crate::core::render::render_context::RenderContext; use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::timer::Timer; use glam::{Mat4, Vec3}; -use std::sync::Arc; -use vulkano::buffer::Subbuffer; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract, }; -use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; -use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -const VERTICES: [Vertex2D; 4] = [ - Vertex2D { - position: [0.0, 0.0], - uv: [0.0, 0.0], - }, - Vertex2D { - position: [0.0, 5.0], - uv: [0.0, 0.5], - }, - Vertex2D { - position: [10.0, 0.0], - uv: [1.0, 0.0], - }, - Vertex2D { - position: [10.0, 5.0], - uv: [1.0, 0.5], - }, -]; +use super::assets::square::Square; pub struct MainSceneState { - pipeline: Arc, - vertex_buffer: Subbuffer<[Vertex2D]>, + square: Square, camera: Camera, texture: Texture, speed: f32, @@ -54,12 +30,12 @@ impl Scene for MainScene { } fn load(&mut self, render_context: &RenderContext) { - let pipeline = - create_triangle_pipeline(render_context.device(), render_context.swapchain_format()) - .unwrap(); - let vertex_buffer = - Vertex2D::create_buffer(Vec::from_iter(VERTICES), render_context.memory_allocator()) - .unwrap(); + let square = Square::new( + render_context.device(), + render_context.memory_allocator(), + render_context.swapchain_format(), + ) + .unwrap(); let camera = Camera::new( Mat4::look_at_rh( @@ -97,8 +73,7 @@ impl Scene for MainScene { .unwrap(); self.state = Some(MainSceneState { - pipeline, - vertex_buffer, + square, camera, texture, speed: 50.0, @@ -128,49 +103,20 @@ impl Scene for MainScene { builder: &mut AutoCommandBufferBuilder, ) { let state = self.state.as_ref().unwrap(); - let vertex_count = state.vertex_buffer.len() as u32; - let instance_count = vertex_count / 4; - - let layouts = state.pipeline.layout().set_layouts(); - let uniform_buffer = state + let camera_uniform = state .camera .create_buffer(render_context.memory_allocator()) .unwrap(); - let uniform_descriptor_set = DescriptorSet::new( - render_context.descriptor_set_allocator().clone(), - layouts[0].clone(), - [WriteDescriptorSet::buffer(0, uniform_buffer)], - [], - ) - .unwrap(); - let texture_descriptor_set = DescriptorSet::new( - render_context.descriptor_set_allocator().clone(), - layouts[1].clone(), - [ - WriteDescriptorSet::sampler(0, state.texture.get_sampler().clone()), - WriteDescriptorSet::image_view(1, state.texture.get_texture().clone()), - ], - [], - ) - .unwrap(); - - unsafe { - builder - .bind_pipeline_graphics(state.pipeline.clone()) - .unwrap() - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - state.pipeline.layout().clone(), - 0, - vec![uniform_descriptor_set, texture_descriptor_set], - ) - .unwrap() - .bind_vertex_buffers(0, state.vertex_buffer.clone()) - .unwrap() - .draw(vertex_count, instance_count, 0, 0) - .unwrap(); - } + state + .square + .render( + builder, + render_context.descriptor_set_allocator(), + &camera_uniform, + &state.texture, + ) + .unwrap(); } fn unload(&mut self) {} diff --git a/src/game/mod.rs b/src/game/mod.rs index 53eb070..e8924d3 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1 +1,2 @@ +pub mod assets; pub mod main_scene; From f8359414328f5032edba36efd9cfe62f70acbb63 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 00:17:21 +0200 Subject: [PATCH 79/91] app: Move render_pass into scene --- src/core/app.rs | 160 +++++++----------------- src/core/render/mod.rs | 1 - src/core/render/render_context.rs | 98 --------------- src/core/scene/context.rs | 82 ++++++++++++ src/core/{scene.rs => scene/manager.rs} | 25 +--- src/core/scene/mod.rs | 24 ++++ src/game/main_scene.rs | 127 ++++++++++++++----- 7 files changed, 250 insertions(+), 267 deletions(-) delete mode 100644 src/core/render/render_context.rs create mode 100644 src/core/scene/context.rs rename src/core/{scene.rs => scene/manager.rs} (59%) create mode 100644 src/core/scene/mod.rs diff --git a/src/core/app.rs b/src/core/app.rs index bcdb82d..5865cb1 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -1,22 +1,15 @@ use std::collections::HashMap; use std::sync::Arc; -use super::render::render_context::RenderContext; use super::render::vulkan_context::VulkanContext; +use super::scene::SceneContext; use crate::core::input::InputManager; use crate::core::scene::SceneManager; use crate::core::timer::Timer; use crate::game::main_scene::MainScene; -use egui_winit_vulkano::{Gui, GuiConfig, egui}; -use vulkano::command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, -}; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; +use egui_winit_vulkano::{Gui, GuiConfig}; use vulkano::swapchain::PresentMode; -use vulkano::sync::GpuFuture; use vulkano_util::context::VulkanoContext; -use vulkano_util::renderer::VulkanoWindowRenderer; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; @@ -27,28 +20,9 @@ pub struct App { vulkan_context: Arc, vulkano_windows: Arc, gui: HashMap, - clear_color: [f32; 3], - input_manager: InputManager, scene_manager: SceneManager, - timer: Timer, -} - -impl From<(&VulkanContext, &VulkanoWindowRenderer)> for RenderContext { - fn from((vulkan_context, renderer): (&VulkanContext, &VulkanoWindowRenderer)) -> Self { - RenderContext::new( - vulkan_context.vulkano_context().instance().clone(), - vulkan_context.vulkano_context().device().clone(), - vulkan_context.vulkano_context().graphics_queue().clone(), - vulkan_context.vulkano_context().compute_queue().clone(), - vulkan_context.vulkano_context().transfer_queue().cloned(), - vulkan_context.vulkano_context().memory_allocator().clone(), - vulkan_context.command_buffer_allocator().clone(), - vulkan_context.descriptor_set_allocator().clone(), - renderer.resolution(), - renderer.aspect_ratio(), - renderer.swapchain_format(), - ) - } + input_manager: Arc, + timer: Arc, } impl App { @@ -57,10 +31,9 @@ impl App { vulkan_context: Arc::new(VulkanContext::new(vulkano_context)), vulkano_windows: Arc::new(VulkanoWindows::default()), gui: HashMap::new(), - clear_color: [0.0, 0.0, 0.0], - input_manager, + input_manager: Arc::new(input_manager), scene_manager: SceneManager::new(), - timer: Timer::new(), + timer: Arc::new(Timer::new()), } } } @@ -109,7 +82,10 @@ impl ApplicationHandler for App { _device_id: winit::event::DeviceId, event: winit::event::DeviceEvent, ) { - self.input_manager.process_device_event(&event); + match Arc::get_mut(&mut self.input_manager) { + Some(input_manager) => input_manager.process_device_event(&event), + None => log::error!("Failed to get a mutable reference to the input manager"), + } } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { @@ -118,10 +94,12 @@ impl ApplicationHandler for App { .get_renderer_mut(id) .unwrap(); let gui = self.gui.get_mut(&id).unwrap(); - let render_context = RenderContext::from((self.vulkan_context.as_ref(), &*renderer)); if !gui.update(&event) { - self.input_manager.process_window_event(&event); + match Arc::get_mut(&mut self.input_manager) { + Some(input_manager) => input_manager.process_window_event(&event), + None => log::error!("Failed to get a mutable reference to the input manager"), + } } match event { @@ -136,86 +114,40 @@ impl ApplicationHandler for App { renderer.resize(); } WindowEvent::RedrawRequested => { - self.input_manager.update(); - self.timer.update(); - self.scene_manager.load_scene_if_not_loaded(&render_context); + match Arc::get_mut(&mut self.input_manager) { + Some(input_manager) => input_manager.update(), + None => log::error!("Failed to get a mutable reference to the input manager"), + } + match Arc::get_mut(&mut self.timer) { + Some(timer) => timer.update(), + None => log::error!("Failed to get a mutable reference to the timer"), + } + + let scene_context = SceneContext::from(( + &*renderer, + &self.vulkan_context, + &self.input_manager, + &self.timer, + )); + self.scene_manager.load_scene_if_not_loaded(&scene_context); + if let Some(scene) = self.scene_manager.current_scene_mut() { - scene.update(&render_context, &self.input_manager, &self.timer); + scene.update(&scene_context); + + let acquire_future = renderer.acquire(None, |_| {}).unwrap(); + let acquire_future = scene.render( + &renderer.swapchain_image_view(), + acquire_future, + &scene_context, + gui, + ); + match acquire_future { + Ok(future) => renderer.present(future, true), + Err(e) => { + log::error!("Error rendering scene: {}", e); + } + } } - - let acquire_future = renderer.acquire(None, |_| {}).unwrap(); - - let mut builder = AutoCommandBufferBuilder::primary( - self.vulkan_context.command_buffer_allocator().clone(), - self.vulkan_context - .vulkano_context() - .graphics_queue() - .queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); - - { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: renderer.resolution(), - depth_range: 0.0..=1.0, - }; - - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some(self.clear_color.into()), - ..RenderingAttachmentInfo::image_view( - renderer.swapchain_image_view().clone(), - ) - })], - ..Default::default() - }) - .unwrap() - .set_viewport(0, [viewport].into_iter().collect()) - .unwrap(); - } - - if let Some(scene) = self.scene_manager.current_scene() { - scene.render(&render_context, &mut builder); - } - - builder.end_rendering().unwrap(); - - let command_buffer = builder.build().unwrap(); - - let render_future = acquire_future - .then_execute( - self.vulkan_context - .vulkano_context() - .graphics_queue() - .clone(), - command_buffer, - ) - .unwrap(); - - gui.immediate_ui(|gui| { - let ctx = gui.context(); - - egui::Window::new("Informations") - .vscroll(true) - .show(&ctx, |ui| { - ui.label(format!("Resolution: {:?}", renderer.resolution())); - ui.color_edit_button_rgb(&mut self.clear_color); - - ui.label(format!("{:#?}", self.input_manager)); - - ui.label(format!("Delta time: {:?}", self.timer.delta_time())); - }); - }); - - let render_future = - gui.draw_on_image(render_future, renderer.swapchain_image_view()); - - renderer.present(render_future.boxed(), true); } _ => {} } diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index a50c5da..07e4134 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,4 +1,3 @@ pub mod primitives; -pub mod render_context; pub mod texture; pub mod vulkan_context; diff --git a/src/core/render/render_context.rs b/src/core/render/render_context.rs deleted file mode 100644 index e9e34a1..0000000 --- a/src/core/render/render_context.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::sync::Arc; - -use vulkano::{ - command_buffer::allocator::StandardCommandBufferAllocator, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{Device, Queue}, - format::Format, - instance::Instance, - memory::allocator::StandardMemoryAllocator, -}; - -pub struct RenderContext { - instance: Arc, - device: Arc, - graphics_queue: Arc, - compute_queue: Arc, - transfer_queue: Option>, - memory_allocator: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, - window_size: [f32; 2], - aspect_ratio: f32, - swapchain_format: Format, -} - -impl RenderContext { - pub fn new( - instance: Arc, - device: Arc, - graphics_queue: Arc, - compute_queue: Arc, - transfer_queue: Option>, - memory_allocator: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, - window_size: [f32; 2], - aspect_ratio: f32, - swapchain_format: Format, - ) -> Self { - Self { - instance, - device, - graphics_queue, - compute_queue, - transfer_queue, - memory_allocator, - command_buffer_allocator, - descriptor_set_allocator, - window_size, - aspect_ratio, - swapchain_format, - } - } - - pub fn instance(&self) -> &Arc { - &self.instance - } - - pub fn device(&self) -> &Arc { - &self.device - } - - pub fn graphics_queue(&self) -> &Arc { - &self.graphics_queue - } - - pub fn compute_queue(&self) -> &Arc { - &self.compute_queue - } - - pub fn transfer_queue(&self) -> Option<&Arc> { - self.transfer_queue.as_ref() - } - - pub fn memory_allocator(&self) -> &Arc { - &self.memory_allocator - } - - pub fn command_buffer_allocator(&self) -> &Arc { - &self.command_buffer_allocator - } - - pub fn descriptor_set_allocator(&self) -> &Arc { - &self.descriptor_set_allocator - } - - pub fn window_size(&self) -> &[f32; 2] { - &self.window_size - } - - pub fn aspect_ratio(&self) -> f32 { - self.aspect_ratio - } - - pub fn swapchain_format(&self) -> Format { - self.swapchain_format - } -} diff --git a/src/core/scene/context.rs b/src/core/scene/context.rs new file mode 100644 index 0000000..028d612 --- /dev/null +++ b/src/core/scene/context.rs @@ -0,0 +1,82 @@ +use std::sync::Arc; + +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + format::Format, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; +use vulkano_util::renderer::VulkanoWindowRenderer; + +use crate::core::{input::InputManager, render::vulkan_context::VulkanContext, timer::Timer}; + +pub struct SceneContext { + pub instance: Arc, + pub device: Arc, + pub graphics_queue: Arc, + pub compute_queue: Arc, + pub transfer_queue: Option>, + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, + pub window_size: [f32; 2], + pub aspect_ratio: f32, + pub swapchain_format: Format, + pub input_manager: Arc, + pub timer: Arc, +} + +impl + From<( + &VulkanoWindowRenderer, + &Arc, + &Arc, + &Arc, + )> for SceneContext +{ + fn from( + (renderer, vulkan_context, input_manager, timer): ( + &VulkanoWindowRenderer, + &Arc, + &Arc, + &Arc, + ), + ) -> Self { + let (command_buffer_allocator, descriptor_set_allocator) = { + ( + vulkan_context.command_buffer_allocator().clone(), + vulkan_context.descriptor_set_allocator().clone(), + ) + }; + + let (instance, device, graphics_queue, compute_queue, transfer_queue, memory_allocator) = { + let vulkan_context = vulkan_context.vulkano_context(); + ( + vulkan_context.instance().clone(), + vulkan_context.device().clone(), + vulkan_context.graphics_queue().clone(), + vulkan_context.compute_queue().clone(), + vulkan_context.transfer_queue().cloned(), + vulkan_context.memory_allocator().clone(), + ) + }; + + Self { + instance, + device, + graphics_queue, + compute_queue, + transfer_queue, + memory_allocator, + command_buffer_allocator, + descriptor_set_allocator, + window_size: renderer.window_size(), + aspect_ratio: renderer.aspect_ratio(), + swapchain_format: renderer.swapchain_format(), + input_manager: input_manager.clone(), + timer: timer.clone(), + } + } +} diff --git a/src/core/scene.rs b/src/core/scene/manager.rs similarity index 59% rename from src/core/scene.rs rename to src/core/scene/manager.rs index 9717117..8e25a74 100644 --- a/src/core/scene.rs +++ b/src/core/scene/manager.rs @@ -1,23 +1,4 @@ -use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; - -use super::{input::InputManager, render::render_context::RenderContext, timer::Timer}; - -pub trait Scene { - fn loaded(&self) -> bool; - fn load(&mut self, render_context: &RenderContext); - fn update( - &mut self, - render_context: &RenderContext, - input_manager: &InputManager, - timer: &Timer, - ); - fn render( - &self, - render_context: &RenderContext, - builder: &mut AutoCommandBufferBuilder, - ); - fn unload(&mut self); -} +use super::{Scene, SceneContext}; pub struct SceneManager { current_scene: Option>, @@ -30,10 +11,10 @@ impl SceneManager { } } - pub fn load_scene_if_not_loaded(&mut self, render_context: &RenderContext) { + pub fn load_scene_if_not_loaded(&mut self, scene_context: &SceneContext) { if let Some(current_scene) = self.current_scene.as_mut() { if !current_scene.loaded() { - current_scene.load(render_context); + current_scene.load(scene_context); } } } diff --git a/src/core/scene/mod.rs b/src/core/scene/mod.rs new file mode 100644 index 0000000..dc97ece --- /dev/null +++ b/src/core/scene/mod.rs @@ -0,0 +1,24 @@ +use std::{error::Error, sync::Arc}; + +use egui_winit_vulkano::Gui; +use vulkano::{image::view::ImageView, sync::GpuFuture}; + +mod context; +pub use context::SceneContext; + +mod manager; +pub use manager::SceneManager; + +pub trait Scene { + fn loaded(&self) -> bool; + fn load(&mut self, scene_context: &SceneContext); + fn update(&mut self, scene_context: &SceneContext); + fn render( + &mut self, + image_view: &Arc, + acquire_future: Box, + scene_context: &SceneContext, + gui: &mut Gui, + ) -> Result, Box>; + fn unload(&mut self); +} diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index c3d08ca..3000746 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,13 +1,20 @@ -use crate::core::input::InputManager; +use std::{error::Error, sync::Arc}; + use crate::core::render::primitives::camera::Camera; -use crate::core::render::render_context::RenderContext; use crate::core::render::texture::Texture; use crate::core::scene::Scene; -use crate::core::timer::Timer; +use crate::core::scene::SceneContext; +use egui_winit_vulkano::{Gui, egui}; use glam::{Mat4, Vec3}; -use vulkano::command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, - PrimaryCommandBufferAbstract, +use vulkano::{ + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, + RenderingAttachmentInfo, RenderingInfo, + }, + image::view::ImageView, + pipeline::graphics::viewport::Viewport, + render_pass::{AttachmentLoadOp, AttachmentStoreOp}, + sync::GpuFuture, }; use super::assets::square::Square; @@ -29,11 +36,11 @@ impl Scene for MainScene { self.state.is_some() } - fn load(&mut self, render_context: &RenderContext) { + fn load(&mut self, scene_context: &SceneContext) { let square = Square::new( - render_context.device(), - render_context.memory_allocator(), - render_context.swapchain_format(), + &scene_context.device, + &scene_context.memory_allocator, + scene_context.swapchain_format, ) .unwrap(); @@ -45,22 +52,22 @@ impl Scene for MainScene { ), Mat4::perspective_rh_gl( std::f32::consts::FRAC_PI_2, - render_context.aspect_ratio(), + scene_context.aspect_ratio, 0.01, 100.0, ), ); let mut uploads = AutoCommandBufferBuilder::primary( - render_context.command_buffer_allocator().clone(), - render_context.graphics_queue().queue_family_index(), + scene_context.command_buffer_allocator.clone(), + scene_context.graphics_queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, ) .unwrap(); let texture = Texture::from_file( - render_context.device(), - render_context.memory_allocator(), + &scene_context.device, + &scene_context.memory_allocator, &mut uploads, "res/textures/wooden-crate.jpg", ) @@ -69,7 +76,7 @@ impl Scene for MainScene { let _ = uploads .build() .unwrap() - .execute(render_context.graphics_queue().clone()) + .execute(scene_context.graphics_queue.clone()) .unwrap(); self.state = Some(MainSceneState { @@ -80,43 +87,99 @@ impl Scene for MainScene { }); } - fn update( - &mut self, - render_context: &RenderContext, - input_manager: &InputManager, - timer: &Timer, - ) { + fn update(&mut self, scene_context: &SceneContext) { let state = self.state.as_mut().unwrap(); - state.camera.update(input_manager, timer, state.speed, 10.0); + state.camera.update( + &scene_context.input_manager, + &scene_context.timer, + state.speed, + 10.0, + ); state.camera.set_projection(Mat4::perspective_rh_gl( std::f32::consts::FRAC_PI_2, - render_context.aspect_ratio(), + scene_context.aspect_ratio, 0.01, 100.0, )); } fn render( - &self, - render_context: &RenderContext, - builder: &mut AutoCommandBufferBuilder, - ) { + &mut self, + image_view: &Arc, + acquire_future: Box, + scene_context: &SceneContext, + gui: &mut Gui, + ) -> Result, Box> { let state = self.state.as_ref().unwrap(); + + let mut builder = AutoCommandBufferBuilder::primary( + scene_context.command_buffer_allocator.clone(), + scene_context.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + )?; + + { + let viewport = Viewport { + offset: [0.0, 0.0], + extent: scene_context.window_size, + depth_range: 0.0..=1.0, + }; + + builder + .begin_rendering(RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + ..RenderingAttachmentInfo::image_view(image_view.clone()) + })], + ..Default::default() + })? + .set_viewport(0, [viewport].into_iter().collect())?; + } + let camera_uniform = state .camera - .create_buffer(render_context.memory_allocator()) - .unwrap(); + .create_buffer(&scene_context.memory_allocator)?; state .square .render( - builder, - render_context.descriptor_set_allocator(), + &mut builder, + &scene_context.descriptor_set_allocator, &camera_uniform, &state.texture, ) .unwrap(); + + builder.end_rendering()?; + + let command_buffer = builder.build()?; + + let render_future = + acquire_future.then_execute(scene_context.graphics_queue.clone(), command_buffer)?; + + gui.immediate_ui(|gui| { + let ctx = gui.context(); + + egui::Window::new("Informations") + .vscroll(true) + .show(&ctx, |ui| { + ui.label(format!("Resolution: {:?}", scene_context.window_size)); + + ui.label(format!("{:#?}", scene_context.input_manager)); + + ui.label(format!( + "Delta time: {:?}", + scene_context.timer.delta_time() + )); + }); + }); + + let render_future = gui.draw_on_image(render_future, image_view.clone()); + + Ok(render_future) } fn unload(&mut self) {} From 998aa68da1321dcfb7a3018353194d3df43c95f4 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 13:54:00 +0200 Subject: [PATCH 80/91] camera: fix camera movement --- README.md | 7 ++++ flake.nix | 3 +- src/core/app.rs | 5 ++- src/core/render/primitives/camera.rs | 60 +++++++++++++++++----------- src/core/render/primitives/mvp.rs | 14 ++----- src/core/render/primitives/vertex.rs | 28 +------------ src/core/scene/manager.rs | 4 +- src/core/scene/mod.rs | 4 +- src/game/main_scene.rs | 59 ++++++++++++--------------- src/main.rs | 2 - 10 files changed, 83 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index c74b5d4..6e4bad1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ # Project +Run renderdoc on wayland: + +```console +WAYLAND_DISPLAY= QT_QPA_PLATFORM=xcb qrenderdoc +``` +> Not supported yet https://github.com/baldurk/renderdoc/issues/853 + ## Usefull links - https://vulkan-tutorial.com/fr/Introduction diff --git a/flake.nix b/flake.nix index dbd8d4c..c5bbd9a 100644 --- a/flake.nix +++ b/flake.nix @@ -39,6 +39,7 @@ libxkbcommon wayland libGL + # Xorg xorg.libX11 xorg.libXcursor @@ -64,7 +65,7 @@ ++ packages; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; - VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d"; RUST_LOG = "debug,rust_vulkan_test=trace"; }; in diff --git a/src/core/app.rs b/src/core/app.rs index 5865cb1..33a5dc1 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -65,6 +65,7 @@ impl ApplicationHandler for App { renderer.swapchain_format(), GuiConfig { is_overlay: true, + allow_srgb_render_target: true, ..Default::default() }, ) @@ -132,7 +133,9 @@ impl ApplicationHandler for App { self.scene_manager.load_scene_if_not_loaded(&scene_context); if let Some(scene) = self.scene_manager.current_scene_mut() { - scene.update(&scene_context); + if let Err(e) = scene.update(&scene_context) { + log::error!("Error updating scene: {}", e); + } let acquire_future = renderer.acquire(None, |_| {}).unwrap(); let acquire_future = scene.render( diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs index c322c7c..34950ce 100644 --- a/src/core/render/primitives/camera.rs +++ b/src/core/render/primitives/camera.rs @@ -1,6 +1,6 @@ -use std::{f32::consts::PI, sync::Arc}; +use std::{f32::consts::FRAC_PI_2, sync::Arc}; -use glam::{Mat4, Quat, Vec3}; +use glam::{Mat4, Vec3, Vec4}; use vulkano::{ Validated, buffer::{AllocateBufferError, Subbuffer}, @@ -13,7 +13,6 @@ use super::mvp::MVP; #[derive(Default)] pub struct Camera { - view: Mat4, projection: Mat4, position: Vec3, @@ -21,9 +20,8 @@ pub struct Camera { } impl Camera { - pub fn new(view: Mat4, projection: Mat4) -> Self { + pub fn new(projection: Mat4) -> Self { Self { - view, projection, position: Vec3::ZERO, rotation: Vec3::ZERO, @@ -45,43 +43,57 @@ impl Camera { 0.0, ); - if self.rotation.x > 90.0 { - self.rotation = Vec3::new(90.0, self.rotation.y, 0.0); + if self.rotation.x > FRAC_PI_2 { + self.rotation = Vec3::new(FRAC_PI_2, self.rotation.y, 0.0); } - if self.rotation.x < -90.0 { - self.rotation = Vec3::new(-90.0, self.rotation.y, 0.0); + if self.rotation.x < -FRAC_PI_2 { + self.rotation = Vec3::new(-FRAC_PI_2, self.rotation.y, 0.0); } let movement_delta = movement_speed * timer.delta_time(); + let (yaw_sin, yaw_cos) = self.rotation.y.sin_cos(); + let forward = Vec3::new(yaw_cos, 0.0, yaw_sin).normalize(); + let right = Vec3::new(-yaw_sin, 0.0, yaw_cos).normalize(); + let tx = input_manager.get_virtual_input_state("move_right") * movement_delta; + self.position += tx * right; + let tz = input_manager.get_virtual_input_state("move_forward") * movement_delta; - - self.position.x += tx * (self.rotation.y).cos(); - self.position.z += tx * (self.rotation.y).sin(); - - self.position.x += tz * (self.rotation.y + PI / 2.0).cos(); - self.position.z += tz * (self.rotation.y + PI / 2.0).sin(); + self.position += tz * forward; } pub fn set_projection(&mut self, projection: Mat4) { self.projection = projection; } + pub fn get_position(&self) -> Vec3 { + self.position + } + + pub fn get_rotation(&self) -> Vec3 { + self.rotation + } + pub fn create_buffer( &self, memory_allocator: &Arc, ) -> Result, Validated> { - let mut world_matrix = Mat4::IDENTITY; - world_matrix *= Mat4::from_quat(Quat::from_euler( - glam::EulerRot::XYX, - self.rotation.x, - self.rotation.y, - self.rotation.z, - )); - world_matrix *= Mat4::from_translation(self.position); + let (sin_pitch, cos_pitch) = self.rotation.x.sin_cos(); + let (sin_yaw, cos_yaw) = self.rotation.y.sin_cos(); - MVP::new(&world_matrix, &self.view, &self.projection).into_buffer(memory_allocator) + let view_matrix = Mat4::look_to_rh( + self.position, + Vec3::new(cos_pitch * cos_yaw, sin_pitch, cos_pitch * sin_yaw).normalize(), + Vec3::Y, + ); + + MVP { + model: Mat4::IDENTITY.to_cols_array_2d(), + view: view_matrix.to_cols_array_2d(), + projection: self.projection.to_cols_array_2d(), + } + .into_buffer(memory_allocator) } } diff --git a/src/core/render/primitives/mvp.rs b/src/core/render/primitives/mvp.rs index fb73ad3..b06b55f 100644 --- a/src/core/render/primitives/mvp.rs +++ b/src/core/render/primitives/mvp.rs @@ -10,20 +10,12 @@ use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, Standar #[derive(BufferContents, Clone, Copy)] #[repr(C)] pub struct MVP { - world: [[f32; 4]; 4], - view: [[f32; 4]; 4], - projection: [[f32; 4]; 4], + pub model: [[f32; 4]; 4], + pub view: [[f32; 4]; 4], + pub projection: [[f32; 4]; 4], } impl MVP { - pub fn new(world: &Mat4, view: &Mat4, projection: &Mat4) -> Self { - Self { - world: world.to_cols_array_2d(), - view: view.to_cols_array_2d(), - projection: projection.to_cols_array_2d(), - } - } - pub fn into_buffer( self, memory_allocator: &Arc, diff --git a/src/core/render/primitives/vertex.rs b/src/core/render/primitives/vertex.rs index b35588c..566df22 100644 --- a/src/core/render/primitives/vertex.rs +++ b/src/core/render/primitives/vertex.rs @@ -1,9 +1,4 @@ -use std::sync::Arc; -use vulkano::Validated; -use vulkano::buffer::{ - AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, -}; -use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; +use vulkano::buffer::BufferContents; use vulkano::pipeline::graphics::vertex_input::Vertex; #[derive(BufferContents, Vertex)] @@ -15,24 +10,3 @@ pub struct Vertex2D { #[format(R32G32_SFLOAT)] pub uv: [f32; 2], } - -impl Vertex2D { - pub fn create_buffer( - vertices: Vec, - memory_allocator: &Arc, - ) -> Result, Validated> { - Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - } -} diff --git a/src/core/scene/manager.rs b/src/core/scene/manager.rs index 8e25a74..e2043f2 100644 --- a/src/core/scene/manager.rs +++ b/src/core/scene/manager.rs @@ -14,7 +14,9 @@ impl SceneManager { pub fn load_scene_if_not_loaded(&mut self, scene_context: &SceneContext) { if let Some(current_scene) = self.current_scene.as_mut() { if !current_scene.loaded() { - current_scene.load(scene_context); + if let Err(e) = current_scene.load(scene_context) { + log::error!("Error loading scene: {}", e); + } } } } diff --git a/src/core/scene/mod.rs b/src/core/scene/mod.rs index dc97ece..a71bfbb 100644 --- a/src/core/scene/mod.rs +++ b/src/core/scene/mod.rs @@ -11,8 +11,8 @@ pub use manager::SceneManager; pub trait Scene { fn loaded(&self) -> bool; - fn load(&mut self, scene_context: &SceneContext); - fn update(&mut self, scene_context: &SceneContext); + fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box>; + fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box>; fn render( &mut self, image_view: &Arc, diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 3000746..0aa18ac 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -5,7 +5,7 @@ use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::scene::SceneContext; use egui_winit_vulkano::{Gui, egui}; -use glam::{Mat4, Vec3}; +use glam::Mat4; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, @@ -36,48 +36,36 @@ impl Scene for MainScene { self.state.is_some() } - fn load(&mut self, scene_context: &SceneContext) { + fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { let square = Square::new( &scene_context.device, &scene_context.memory_allocator, scene_context.swapchain_format, - ) - .unwrap(); + )?; - let camera = Camera::new( - Mat4::look_at_rh( - Vec3::new(0.3, 0.3, 1.0), - Vec3::new(0.0, 0.0, 0.0), - Vec3::new(0.0, -1.0, 0.0), - ), - Mat4::perspective_rh_gl( - std::f32::consts::FRAC_PI_2, - scene_context.aspect_ratio, - 0.01, - 100.0, - ), - ); + let camera = Camera::new(Mat4::perspective_rh_gl( + std::f32::consts::FRAC_PI_2, + scene_context.aspect_ratio, + 0.01, + 1000.0, + )); let mut uploads = AutoCommandBufferBuilder::primary( scene_context.command_buffer_allocator.clone(), scene_context.graphics_queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); + )?; let texture = Texture::from_file( &scene_context.device, &scene_context.memory_allocator, &mut uploads, "res/textures/wooden-crate.jpg", - ) - .unwrap(); + )?; let _ = uploads - .build() - .unwrap() - .execute(scene_context.graphics_queue.clone()) - .unwrap(); + .build()? + .execute(scene_context.graphics_queue.clone())?; self.state = Some(MainSceneState { square, @@ -85,10 +73,12 @@ impl Scene for MainScene { texture, speed: 50.0, }); + + Ok(()) } - fn update(&mut self, scene_context: &SceneContext) { - let state = self.state.as_mut().unwrap(); + fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box> { + let state = self.state.as_mut().ok_or("State not found")?; state.camera.update( &scene_context.input_manager, &scene_context.timer, @@ -96,12 +86,7 @@ impl Scene for MainScene { 10.0, ); - state.camera.set_projection(Mat4::perspective_rh_gl( - std::f32::consts::FRAC_PI_2, - scene_context.aspect_ratio, - 0.01, - 100.0, - )); + Ok(()) } fn render( @@ -111,7 +96,7 @@ impl Scene for MainScene { scene_context: &SceneContext, gui: &mut Gui, ) -> Result, Box> { - let state = self.state.as_ref().unwrap(); + let state = self.state.as_ref().ok_or("State not found")?; let mut builder = AutoCommandBufferBuilder::primary( scene_context.command_buffer_allocator.clone(), @@ -174,6 +159,12 @@ impl Scene for MainScene { "Delta time: {:?}", scene_context.timer.delta_time() )); + + ui.label(format!( + "Position: {:?}, Rotation: {:?}", + state.camera.get_position(), + state.camera.get_rotation() + )); }); }); diff --git a/src/main.rs b/src/main.rs index 7a4284c..ffe5f72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,6 @@ fn main() { vec![ VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Normal), VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Invert), - VirtualBinding::Axis(0, AxisDirection::Normal, 0.0), ], ), ( @@ -29,7 +28,6 @@ fn main() { vec![ VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Normal), VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Invert), - VirtualBinding::Axis(1, AxisDirection::Normal, 0.0), ], ), ( From 05532756cfad020f98c9f85b09bc4f3f1faf67b3 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 16:08:41 +0200 Subject: [PATCH 81/91] docs: Add opengl/vulkan diff --- README.md | 6 +- docs/OPENGL_VULKAN_DIFF.md | 11 + docs/images/coord_sys.png | Bin 0 -> 24096 bytes docs/images/normalized_device_coordinates.svg | 219 ++++++++++++++++++ 4 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 docs/OPENGL_VULKAN_DIFF.md create mode 100644 docs/images/coord_sys.png create mode 100644 docs/images/normalized_device_coordinates.svg diff --git a/README.md b/README.md index 6e4bad1..c271fd8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # Project -Run renderdoc on wayland: +## Notes + +1. Run renderdoc on wayland: ```console WAYLAND_DISPLAY= QT_QPA_PLATFORM=xcb qrenderdoc ``` > Not supported yet https://github.com/baldurk/renderdoc/issues/853 +2. [Difference Between OpenGL and Vulkan](./docs/OPENGL_VULKAN_DIFF.md) + ## Usefull links - https://vulkan-tutorial.com/fr/Introduction diff --git a/docs/OPENGL_VULKAN_DIFF.md b/docs/OPENGL_VULKAN_DIFF.md new file mode 100644 index 0000000..93d0855 --- /dev/null +++ b/docs/OPENGL_VULKAN_DIFF.md @@ -0,0 +1,11 @@ +# Difference between Vulkan and OpenGL + +Viewport: + +- Y axis is flipped like D3D +- Clipped Z axis is not [-1; 1] but [0; 1] + +![normalized viewport coordinates](./images/normalized_device_coordinates.svg) +![coord_sys](./images/coord_sys.png) + +See: [Vulkan Tutorial (Vertex step)](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Shader_modules) and [VK_KHR_maintenance1 (Allow negative height)](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_maintenance1.html#_description) diff --git a/docs/images/coord_sys.png b/docs/images/coord_sys.png new file mode 100644 index 0000000000000000000000000000000000000000..6d195f67ca5dbc31bf19457f0d724c1b52dd1597 GIT binary patch literal 24096 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

    XW1uvMN#KLU*8Rt+}~tt-#$T@+2O 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 + + + + + + + + + image/svg+xml + + + + + + + + Framebuffer coordinates + (0, 0) + (1920, 0) + (0, 1080) + (1920, 1080) + + (960, 540) + + Normalized device coordinates + (-1, -1) + (1, -1) + (-1, 1) + (1, 1) + + (0, 0) + + From 8b16def890ea041a0368053fc6d84bc2b26e9404 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 16:09:04 +0200 Subject: [PATCH 82/91] square: Fix indices --- src/game/assets/square.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/assets/square.rs b/src/game/assets/square.rs index 3dba163..acca0b6 100644 --- a/src/game/assets/square.rs +++ b/src/game/assets/square.rs @@ -53,7 +53,7 @@ const VERTICES: [Vertex2D; 4] = [ }, ]; -const INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3]; +const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1]; pub mod shaders { pub mod vs { From 3a562fb6ebf3ecfc0bfd9583444cac89d9ca9cfe Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 16:38:45 +0200 Subject: [PATCH 83/91] camera: Fix Y inverted --- src/core/render/primitives/camera.rs | 10 +++++++++- src/game/main_scene.rs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs index 34950ce..f1f48d7 100644 --- a/src/core/render/primitives/camera.rs +++ b/src/core/render/primitives/camera.rs @@ -11,6 +11,14 @@ use crate::core::{input::InputManager, timer::Timer}; use super::mvp::MVP; +// See docs/OPENGL_VULKAN_DIFF.md +const OPENGL_TO_VULKAN_Y_AXIS_FLIP: Mat4 = Mat4 { + x_axis: Vec4::new(1.0, 0.0, 0.0, 0.0), + y_axis: Vec4::new(0.0, -1.0, 0.0, 0.0), + z_axis: Vec4::new(0.0, 0.0, 1.0, 0.0), + w_axis: Vec4::new(0.0, 0.0, 0.0, 1.0), +}; + #[derive(Default)] pub struct Camera { projection: Mat4, @@ -90,7 +98,7 @@ impl Camera { ); MVP { - model: Mat4::IDENTITY.to_cols_array_2d(), + model: OPENGL_TO_VULKAN_Y_AXIS_FLIP.to_cols_array_2d(), view: view_matrix.to_cols_array_2d(), projection: self.projection.to_cols_array_2d(), } diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 0aa18ac..03f54ea 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -43,7 +43,7 @@ impl Scene for MainScene { scene_context.swapchain_format, )?; - let camera = Camera::new(Mat4::perspective_rh_gl( + let camera = Camera::new(Mat4::perspective_rh( std::f32::consts::FRAC_PI_2, scene_context.aspect_ratio, 0.01, From f8b81f32691a986150a3ed4bb214e06459185d1e Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 16:46:11 +0200 Subject: [PATCH 84/91] camera: Change camera to Camera3D --- src/core/render/primitives/camera.rs | 23 +++++++++++++++++++---- src/game/main_scene.rs | 11 ++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs index f1f48d7..5ea52e7 100644 --- a/src/core/render/primitives/camera.rs +++ b/src/core/render/primitives/camera.rs @@ -20,19 +20,27 @@ const OPENGL_TO_VULKAN_Y_AXIS_FLIP: Mat4 = Mat4 { }; #[derive(Default)] -pub struct Camera { +pub struct Camera3D { projection: Mat4, position: Vec3, rotation: Vec3, + aspect_ratio: f32, + fov: f32, + near: f32, + far: f32, } -impl Camera { - pub fn new(projection: Mat4) -> Self { +impl Camera3D { + pub fn new(aspect_ratio: f32, fov: f32, near: f32, far: f32) -> Self { Self { - projection, + projection: Mat4::perspective_rh(fov, aspect_ratio, near, far), position: Vec3::ZERO, rotation: Vec3::ZERO, + aspect_ratio, + fov, + near, + far, } } @@ -42,6 +50,7 @@ impl Camera { timer: &Timer, movement_speed: f32, camera_sensitivity: f32, + window_aspect_ratio: f32, ) { // Process camera rotation let camera_delta = camera_sensitivity * timer.delta_time(); @@ -70,6 +79,12 @@ impl Camera { let tz = input_manager.get_virtual_input_state("move_forward") * movement_delta; self.position += tz * forward; + + if self.aspect_ratio != window_aspect_ratio { + self.aspect_ratio = window_aspect_ratio; + self.projection = + Mat4::perspective_rh(self.fov, self.aspect_ratio, self.near, self.far); + } } pub fn set_projection(&mut self, projection: Mat4) { diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 03f54ea..7063d58 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,6 +1,6 @@ use std::{error::Error, sync::Arc}; -use crate::core::render::primitives::camera::Camera; +use crate::core::render::primitives::camera::Camera3D; use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::scene::SceneContext; @@ -21,7 +21,7 @@ use super::assets::square::Square; pub struct MainSceneState { square: Square, - camera: Camera, + camera: Camera3D, texture: Texture, speed: f32, } @@ -43,12 +43,12 @@ impl Scene for MainScene { scene_context.swapchain_format, )?; - let camera = Camera::new(Mat4::perspective_rh( - std::f32::consts::FRAC_PI_2, + let camera = Camera3D::new( scene_context.aspect_ratio, + std::f32::consts::FRAC_PI_2, 0.01, 1000.0, - )); + ); let mut uploads = AutoCommandBufferBuilder::primary( scene_context.command_buffer_allocator.clone(), @@ -84,6 +84,7 @@ impl Scene for MainScene { &scene_context.timer, state.speed, 10.0, + scene_context.aspect_ratio, ); Ok(()) From 77c717f90b38f57881d3b3b19e9904fb9165673d Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 17:13:01 +0200 Subject: [PATCH 85/91] Add instances support --- Cargo.lock | 40 ++++++++++++++--- Cargo.toml | 3 ++ res/shaders/vertex.vert | 6 ++- src/core/render/primitives/transform.rs | 57 ++++++++++++++++++++++--- src/core/render/primitives/vertex.rs | 10 +++++ src/game/assets/square.rs | 41 +++++++++++------- src/game/main_scene.rs | 32 +++++++++++++- 7 files changed, 158 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54175c9..375be30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1929,8 +1929,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1940,7 +1950,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1952,6 +1972,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rav1e" version = "0.7.1" @@ -1978,8 +2007,8 @@ dependencies = [ "once_cell", "paste", "profiling", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "simd_helpers", "system-deps", "thiserror 1.0.69", @@ -2112,6 +2141,7 @@ dependencies = [ "glam", "image", "log", + "rand 0.9.1", "thiserror 2.0.12", "vulkano", "vulkano-shaders", diff --git a/Cargo.toml b/Cargo.toml index 50be745..feb1bca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,6 @@ glam = { version = "0.30" } # Log and tracing log = "0.4" env_logger = "0.11" + +# Random +rand = "0.9" diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index 0445e54..8c1df7c 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1,7 +1,8 @@ #version 450 -layout (location = 0) in vec2 position; +layout (location = 0) in vec3 position; layout (location = 1) in vec2 uv; +layout (location = 2) in mat4 model; layout (location = 0) out vec2 fragUv; @@ -13,6 +14,7 @@ layout (set = 0, binding = 0) uniform MVP { void main() { mat4 worldview = uniforms.view * uniforms.world; - gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0); + vec4 modelPosition = model * vec4(position, 1.0); + gl_Position = uniforms.projection * worldview * modelPosition; fragUv = uv; } diff --git a/src/core/render/primitives/transform.rs b/src/core/render/primitives/transform.rs index 88e6422..131e6f1 100644 --- a/src/core/render/primitives/transform.rs +++ b/src/core/render/primitives/transform.rs @@ -1,9 +1,26 @@ +use std::sync::Arc; + use glam::{Mat4, Quat, Vec3}; +use vulkano::{ + Validated, + buffer::{ + AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, + }, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, + pipeline::graphics::vertex_input::Vertex, +}; pub struct Transform { - position: Vec3, - rotation: Quat, - scale: Vec3, + pub position: Vec3, + pub rotation: Quat, + pub scale: Vec3, +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +pub struct TransformRaw { + #[format(R32G32B32A32_SFLOAT)] + pub model: [[f32; 4]; 4], } impl Default for Transform { @@ -29,9 +46,35 @@ impl Transform { self.scale *= scale; } - pub fn get_mat4(&self) -> Mat4 { - Mat4::from_translation(self.position) - * Mat4::from_quat(self.rotation) - * Mat4::from_scale(self.scale) + pub fn into_raw(&self) -> TransformRaw { + TransformRaw { + model: (Mat4::from_translation(self.position) + * Mat4::from_quat(self.rotation) + * Mat4::from_scale(self.scale)) + .to_cols_array_2d(), + } + } + + pub fn create_buffer( + memory_allocator: &Arc, + transforms: &[Transform], + ) -> Result, Validated> { + let transform_raws: Vec = transforms.iter().map(|t| t.into_raw()).collect(); + + let buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + transform_raws, + )?; + + Ok(buffer) } } diff --git a/src/core/render/primitives/vertex.rs b/src/core/render/primitives/vertex.rs index 566df22..166ac44 100644 --- a/src/core/render/primitives/vertex.rs +++ b/src/core/render/primitives/vertex.rs @@ -10,3 +10,13 @@ pub struct Vertex2D { #[format(R32G32_SFLOAT)] pub uv: [f32; 2], } + +#[derive(BufferContents, Vertex)] +#[repr(C)] +pub struct Vertex3D { + #[format(R32G32B32_SFLOAT)] + pub position: [f32; 3], + + #[format(R32G32_SFLOAT)] + pub uv: [f32; 2], +} diff --git a/src/game/assets/square.rs b/src/game/assets/square.rs index acca0b6..ca16b93 100644 --- a/src/game/assets/square.rs +++ b/src/game/assets/square.rs @@ -30,26 +30,26 @@ use vulkano::{ }; use crate::core::render::{ - primitives::{mvp::MVP, vertex::Vertex2D}, + primitives::{mvp::MVP, transform::TransformRaw, vertex::Vertex3D}, texture::Texture, }; -const VERTICES: [Vertex2D; 4] = [ - Vertex2D { - position: [0.0, 0.0], +const VERTICES: [Vertex3D; 4] = [ + Vertex3D { + position: [-0.5, -0.5, 0.0], uv: [0.0, 0.0], }, - Vertex2D { - position: [0.0, 5.0], - uv: [0.0, 0.5], + Vertex3D { + position: [-0.5, 0.5, 0.0], + uv: [0.0, 1.0], }, - Vertex2D { - position: [10.0, 0.0], + Vertex3D { + position: [0.5, -0.5, 0.0], uv: [1.0, 0.0], }, - Vertex2D { - position: [10.0, 5.0], - uv: [1.0, 0.5], + Vertex3D { + position: [0.5, 0.5, 0.0], + uv: [1.0, 1.0], }, ]; @@ -74,7 +74,7 @@ pub mod shaders { } pub struct Square { - vertex_buffer: Subbuffer<[Vertex2D]>, + vertex_buffer: Subbuffer<[Vertex3D]>, index_buffer: Subbuffer<[u32]>, pipeline: Arc, } @@ -120,7 +120,8 @@ impl Square { .entry_point("main") .ok_or("Failed find main entry point of fragment shader".to_string())?; - let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; + let vertex_input_state = + [Vertex3D::per_vertex(), TransformRaw::per_instance()].definition(&vs)?; let stages = [ PipelineShaderStageCreateInfo::new(vs), @@ -207,6 +208,7 @@ impl Square { command_buffer: &mut AutoCommandBufferBuilder, descriptor_set_allocator: &Arc, mvp_uniform: &Subbuffer<[MVP]>, + transform_uniform: &Subbuffer<[TransformRaw]>, texture: &Texture, ) -> Result<(), Box> { let layouts = self.pipeline.layout().set_layouts(); @@ -235,11 +237,18 @@ impl Square { 0, vec![uniform_descriptor_set, texture_descriptor_set], )?; - command_buffer.bind_vertex_buffers(0, self.vertex_buffer.clone())?; + command_buffer + .bind_vertex_buffers(0, (self.vertex_buffer.clone(), transform_uniform.clone()))?; command_buffer.bind_index_buffer(self.index_buffer.clone())?; unsafe { - command_buffer.draw_indexed(INDICES.len() as u32, 1, 0, 0, 0)?; + command_buffer.draw_indexed( + INDICES.len() as u32, + transform_uniform.len() as u32, + 0, + 0, + 0, + )?; } Ok(()) diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 7063d58..5fd757a 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,11 +1,14 @@ use std::{error::Error, sync::Arc}; use crate::core::render::primitives::camera::Camera3D; +use crate::core::render::primitives::transform::Transform; use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::scene::SceneContext; use egui_winit_vulkano::{Gui, egui}; -use glam::Mat4; +use glam::EulerRot; +use glam::Quat; +use glam::Vec3; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, @@ -21,6 +24,7 @@ use super::assets::square::Square; pub struct MainSceneState { square: Square, + instances: Vec, camera: Camera3D, texture: Texture, speed: f32, @@ -43,6 +47,27 @@ impl Scene for MainScene { scene_context.swapchain_format, )?; + let num_instances = 100; + let instance_size = 10.0; + let instance_spacing = 10.0; + let num_instances_per_row = (num_instances as f32 / instance_spacing as f32).ceil() as u32; + let instances: Vec = (0..num_instances) + .map(|i| Transform { + position: Vec3::new( + (i % num_instances_per_row) as f32 * (instance_spacing + instance_size), + 0.0, + (i / num_instances_per_row) as f32 * (instance_spacing + instance_size), + ), + rotation: Quat::from_euler( + EulerRot::XYZ, + 0.0, + rand::random_range(0.0..=360.0), + 0.0, + ), + scale: Vec3::new(instance_size, instance_size, instance_size), + }) + .collect(); + let camera = Camera3D::new( scene_context.aspect_ratio, std::f32::consts::FRAC_PI_2, @@ -69,6 +94,7 @@ impl Scene for MainScene { self.state = Some(MainSceneState { square, + instances, camera, texture, speed: 50.0, @@ -129,12 +155,16 @@ impl Scene for MainScene { .camera .create_buffer(&scene_context.memory_allocator)?; + let transform_uniform = + Transform::create_buffer(&scene_context.memory_allocator, &state.instances)?; + state .square .render( &mut builder, &scene_context.descriptor_set_allocator, &camera_uniform, + &transform_uniform, &state.texture, ) .unwrap(); From 650b61e3ae48437b37063f3cb9f091f087e50c29 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 17:44:00 +0200 Subject: [PATCH 86/91] render: add depth buffer --- src/core/app.rs | 30 ++++++++++++++---------------- src/core/scene/manager.rs | 12 ++++++++---- src/game/assets/square.rs | 9 ++++++++- src/game/main_scene.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/core/app.rs b/src/core/app.rs index 33a5dc1..d80d5c6 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -130,26 +130,24 @@ impl ApplicationHandler for App { &self.input_manager, &self.timer, )); - self.scene_manager.load_scene_if_not_loaded(&scene_context); + + self.scene_manager + .load_scene_if_not_loaded(&scene_context) + .unwrap(); if let Some(scene) = self.scene_manager.current_scene_mut() { - if let Err(e) = scene.update(&scene_context) { - log::error!("Error updating scene: {}", e); - } + scene.update(&scene_context).unwrap(); let acquire_future = renderer.acquire(None, |_| {}).unwrap(); - let acquire_future = scene.render( - &renderer.swapchain_image_view(), - acquire_future, - &scene_context, - gui, - ); - match acquire_future { - Ok(future) => renderer.present(future, true), - Err(e) => { - log::error!("Error rendering scene: {}", e); - } - } + let acquire_future = scene + .render( + &renderer.swapchain_image_view(), + acquire_future, + &scene_context, + gui, + ) + .unwrap(); + renderer.present(acquire_future, true); } } _ => {} diff --git a/src/core/scene/manager.rs b/src/core/scene/manager.rs index e2043f2..e44765d 100644 --- a/src/core/scene/manager.rs +++ b/src/core/scene/manager.rs @@ -1,3 +1,5 @@ +use std::error::Error; + use super::{Scene, SceneContext}; pub struct SceneManager { @@ -11,14 +13,16 @@ impl SceneManager { } } - pub fn load_scene_if_not_loaded(&mut self, scene_context: &SceneContext) { + pub fn load_scene_if_not_loaded( + &mut self, + scene_context: &SceneContext, + ) -> Result<(), Box> { if let Some(current_scene) = self.current_scene.as_mut() { if !current_scene.loaded() { - if let Err(e) = current_scene.load(scene_context) { - log::error!("Error loading scene: {}", e); - } + current_scene.load(scene_context)?; } } + Ok(()) } pub fn load_scene(&mut self, scene: Box) { diff --git a/src/game/assets/square.rs b/src/game/assets/square.rs index ca16b93..80a24e2 100644 --- a/src/game/assets/square.rs +++ b/src/game/assets/square.rs @@ -17,7 +17,8 @@ use vulkano::{ graphics::{ GraphicsPipelineCreateInfo, color_blend::{ColorBlendAttachmentState, ColorBlendState}, - input_assembly::{InputAssemblyState, PrimitiveTopology}, + depth_stencil::{DepthState, DepthStencilState}, + input_assembly::InputAssemblyState, multisample::MultisampleState, rasterization::RasterizationState, subpass::PipelineRenderingCreateInfo, @@ -84,6 +85,7 @@ impl Square { device: &Arc, memory_allocator: &Arc, swapchain_format: Format, + depth_format: Format, ) -> Result> { let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), @@ -173,6 +175,7 @@ impl Square { let subpass = PipelineRenderingCreateInfo { color_attachment_formats: vec![Some(swapchain_format)], + depth_attachment_format: Some(depth_format), ..Default::default() }; @@ -190,6 +193,10 @@ impl Square { subpass.color_attachment_formats.len() as u32, ColorBlendAttachmentState::default(), )), + depth_stencil_state: Some(DepthStencilState { + depth: Some(DepthState::simple()), + ..Default::default() + }), dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), ..GraphicsPipelineCreateInfo::layout(layout) diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 5fd757a..b1638c6 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -9,6 +9,12 @@ use egui_winit_vulkano::{Gui, egui}; use glam::EulerRot; use glam::Quat; use glam::Vec3; +use vulkano::format::Format; +use vulkano::image::Image; +use vulkano::image::ImageCreateInfo; +use vulkano::image::ImageLayout; +use vulkano::image::ImageUsage; +use vulkano::memory::allocator::AllocationCreateInfo; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, @@ -28,6 +34,7 @@ pub struct MainSceneState { camera: Camera3D, texture: Texture, speed: f32, + depth_image_view: Arc, } #[derive(Default)] @@ -41,10 +48,27 @@ impl Scene for MainScene { } fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { + let depth_image = Image::new( + scene_context.memory_allocator.clone(), + ImageCreateInfo { + format: Format::D16_UNORM, + extent: [ + scene_context.window_size[0] as u32, + scene_context.window_size[1] as u32, + 1, + ], + usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT, + ..Default::default() + }, + AllocationCreateInfo::default(), + )?; + let depth_image_view = ImageView::new_default(depth_image)?; + let square = Square::new( &scene_context.device, &scene_context.memory_allocator, scene_context.swapchain_format, + Format::D16_UNORM, )?; let num_instances = 100; @@ -98,6 +122,7 @@ impl Scene for MainScene { camera, texture, speed: 50.0, + depth_image_view, }); Ok(()) @@ -146,6 +171,12 @@ impl Scene for MainScene { clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), ..RenderingAttachmentInfo::image_view(image_view.clone()) })], + depth_attachment: Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::DontCare, + clear_value: Some([1.0].into()), + ..RenderingAttachmentInfo::image_view(state.depth_image_view.clone()) + }), ..Default::default() })? .set_viewport(0, [viewport].into_iter().collect())?; From 6a6b1821a4ee69ee72f0980c60f58c2b8c01615a Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 18:16:26 +0200 Subject: [PATCH 87/91] depth: Fix not resized --- src/core/app.rs | 28 +++++++++++++--------- src/core/render/primitives/camera.rs | 2 +- src/core/scene/context.rs | 16 ++++++++----- src/core/scene/mod.rs | 1 - src/game/main_scene.rs | 35 ++++++++-------------------- 5 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/core/app.rs b/src/core/app.rs index d80d5c6..93ef9bc 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -8,6 +8,8 @@ use crate::core::scene::SceneManager; use crate::core::timer::Timer; use crate::game::main_scene::MainScene; use egui_winit_vulkano::{Gui, GuiConfig}; +use vulkano::format::Format; +use vulkano::image::ImageUsage; use vulkano::swapchain::PresentMode; use vulkano_util::context::VulkanoContext; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; @@ -16,6 +18,8 @@ use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; +pub const DEPTH_IMAGE_ID: usize = 0; + pub struct App { vulkan_context: Arc, vulkano_windows: Arc, @@ -56,8 +60,14 @@ impl ApplicationHandler for App { |_| {}, ); + let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); + renderer.add_additional_image_view( + DEPTH_IMAGE_ID, + Format::D16_UNORM, + ImageUsage::DEPTH_STENCIL_ATTACHMENT, + ); + let gui = { - let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); Gui::new( event_loop, renderer.surface(), @@ -124,8 +134,8 @@ impl ApplicationHandler for App { None => log::error!("Failed to get a mutable reference to the timer"), } - let scene_context = SceneContext::from(( - &*renderer, + let mut scene_context = SceneContext::from(( + &mut *renderer, &self.vulkan_context, &self.input_manager, &self.timer, @@ -139,14 +149,10 @@ impl ApplicationHandler for App { scene.update(&scene_context).unwrap(); let acquire_future = renderer.acquire(None, |_| {}).unwrap(); - let acquire_future = scene - .render( - &renderer.swapchain_image_view(), - acquire_future, - &scene_context, - gui, - ) - .unwrap(); + // Update the swapchain image view to the current one after acquiring the next image + scene_context.swapchain_image_view = renderer.swapchain_image_view(); + + let acquire_future = scene.render(acquire_future, &scene_context, gui).unwrap(); renderer.present(acquire_future, true); } } diff --git a/src/core/render/primitives/camera.rs b/src/core/render/primitives/camera.rs index 5ea52e7..e8cdf1e 100644 --- a/src/core/render/primitives/camera.rs +++ b/src/core/render/primitives/camera.rs @@ -55,7 +55,7 @@ impl Camera3D { // Process camera rotation let camera_delta = camera_sensitivity * timer.delta_time(); self.rotation += Vec3::new( - -(input_manager.get_virtual_input_state("mouse_y") * camera_delta).to_radians(), + (input_manager.get_virtual_input_state("mouse_y") * camera_delta).to_radians(), (input_manager.get_virtual_input_state("mouse_x") * camera_delta).to_radians(), 0.0, ); diff --git a/src/core/scene/context.rs b/src/core/scene/context.rs index 028d612..545f39c 100644 --- a/src/core/scene/context.rs +++ b/src/core/scene/context.rs @@ -4,13 +4,15 @@ use vulkano::{ command_buffer::allocator::StandardCommandBufferAllocator, descriptor_set::allocator::StandardDescriptorSetAllocator, device::{Device, Queue}, - format::Format, + image::view::ImageView, instance::Instance, memory::allocator::StandardMemoryAllocator, }; use vulkano_util::renderer::VulkanoWindowRenderer; -use crate::core::{input::InputManager, render::vulkan_context::VulkanContext, timer::Timer}; +use crate::core::{ + app::DEPTH_IMAGE_ID, input::InputManager, render::vulkan_context::VulkanContext, timer::Timer, +}; pub struct SceneContext { pub instance: Arc, @@ -23,14 +25,15 @@ pub struct SceneContext { pub descriptor_set_allocator: Arc, pub window_size: [f32; 2], pub aspect_ratio: f32, - pub swapchain_format: Format, + pub swapchain_image_view: Arc, + pub depth_stencil_image_view: Arc, pub input_manager: Arc, pub timer: Arc, } impl From<( - &VulkanoWindowRenderer, + &mut VulkanoWindowRenderer, &Arc, &Arc, &Arc, @@ -38,7 +41,7 @@ impl { fn from( (renderer, vulkan_context, input_manager, timer): ( - &VulkanoWindowRenderer, + &mut VulkanoWindowRenderer, &Arc, &Arc, &Arc, @@ -74,7 +77,8 @@ impl descriptor_set_allocator, window_size: renderer.window_size(), aspect_ratio: renderer.aspect_ratio(), - swapchain_format: renderer.swapchain_format(), + swapchain_image_view: renderer.swapchain_image_view(), + depth_stencil_image_view: renderer.get_additional_image_view(DEPTH_IMAGE_ID), input_manager: input_manager.clone(), timer: timer.clone(), } diff --git a/src/core/scene/mod.rs b/src/core/scene/mod.rs index a71bfbb..8642224 100644 --- a/src/core/scene/mod.rs +++ b/src/core/scene/mod.rs @@ -15,7 +15,6 @@ pub trait Scene { fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box>; fn render( &mut self, - image_view: &Arc, acquire_future: Box, scene_context: &SceneContext, gui: &mut Gui, diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index b1638c6..22c851b 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -12,7 +12,6 @@ use glam::Vec3; use vulkano::format::Format; use vulkano::image::Image; use vulkano::image::ImageCreateInfo; -use vulkano::image::ImageLayout; use vulkano::image::ImageUsage; use vulkano::memory::allocator::AllocationCreateInfo; use vulkano::{ @@ -34,7 +33,6 @@ pub struct MainSceneState { camera: Camera3D, texture: Texture, speed: f32, - depth_image_view: Arc, } #[derive(Default)] @@ -48,27 +46,11 @@ impl Scene for MainScene { } fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { - let depth_image = Image::new( - scene_context.memory_allocator.clone(), - ImageCreateInfo { - format: Format::D16_UNORM, - extent: [ - scene_context.window_size[0] as u32, - scene_context.window_size[1] as u32, - 1, - ], - usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - )?; - let depth_image_view = ImageView::new_default(depth_image)?; - let square = Square::new( &scene_context.device, &scene_context.memory_allocator, - scene_context.swapchain_format, - Format::D16_UNORM, + scene_context.swapchain_image_view.format(), + scene_context.depth_stencil_image_view.format(), )?; let num_instances = 100; @@ -122,7 +104,6 @@ impl Scene for MainScene { camera, texture, speed: 50.0, - depth_image_view, }); Ok(()) @@ -143,7 +124,6 @@ impl Scene for MainScene { fn render( &mut self, - image_view: &Arc, acquire_future: Box, scene_context: &SceneContext, gui: &mut Gui, @@ -169,13 +149,17 @@ impl Scene for MainScene { load_op: AttachmentLoadOp::Clear, store_op: AttachmentStoreOp::Store, clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view(image_view.clone()) + ..RenderingAttachmentInfo::image_view( + scene_context.swapchain_image_view.clone(), + ) })], depth_attachment: Some(RenderingAttachmentInfo { load_op: AttachmentLoadOp::Clear, store_op: AttachmentStoreOp::DontCare, clear_value: Some([1.0].into()), - ..RenderingAttachmentInfo::image_view(state.depth_image_view.clone()) + ..RenderingAttachmentInfo::image_view( + scene_context.depth_stencil_image_view.clone(), + ) }), ..Default::default() })? @@ -230,7 +214,8 @@ impl Scene for MainScene { }); }); - let render_future = gui.draw_on_image(render_future, image_view.clone()); + let render_future = + gui.draw_on_image(render_future, scene_context.swapchain_image_view.clone()); Ok(render_future) } From f1ae54bc7383d5499174567a2fc17f80271deb6e Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 29 May 2025 21:38:07 +0200 Subject: [PATCH 88/91] winit: Use user event --- src/core/{app.rs => app/mod.rs} | 55 +++++++++++--- src/core/app/user_event.rs | 9 +++ src/core/scene/context.rs | 29 ++++++-- src/game/mod.rs | 2 +- src/game/{ => scenes}/main_scene.rs | 43 +++++++++-- src/game/scenes/mod.rs | 2 + src/game/scenes/test_scene.rs | 111 ++++++++++++++++++++++++++++ src/main.rs | 5 +- 8 files changed, 227 insertions(+), 29 deletions(-) rename src/core/{app.rs => app/mod.rs} (75%) create mode 100644 src/core/app/user_event.rs rename src/game/{ => scenes}/main_scene.rs (85%) create mode 100644 src/game/scenes/mod.rs create mode 100644 src/game/scenes/test_scene.rs diff --git a/src/core/app.rs b/src/core/app/mod.rs similarity index 75% rename from src/core/app.rs rename to src/core/app/mod.rs index 93ef9bc..ab548f8 100644 --- a/src/core/app.rs +++ b/src/core/app/mod.rs @@ -6,8 +6,9 @@ use super::scene::SceneContext; use crate::core::input::InputManager; use crate::core::scene::SceneManager; use crate::core::timer::Timer; -use crate::game::main_scene::MainScene; +use crate::game::scenes::test_scene::TestScene; use egui_winit_vulkano::{Gui, GuiConfig}; +use user_event::UserEvent; use vulkano::format::Format; use vulkano::image::ImageUsage; use vulkano::swapchain::PresentMode; @@ -15,34 +16,42 @@ use vulkano_util::context::VulkanoContext; use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; -use winit::event_loop::ActiveEventLoop; +use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; use winit::window::WindowId; +pub mod user_event; + pub const DEPTH_IMAGE_ID: usize = 0; pub struct App { vulkan_context: Arc, vulkano_windows: Arc, gui: HashMap, - scene_manager: SceneManager, + scene_manager: HashMap, input_manager: Arc, timer: Arc, + event_loop_proxy: EventLoopProxy, } impl App { - pub fn new(vulkano_context: VulkanoContext, input_manager: InputManager) -> Self { + pub fn new( + vulkano_context: VulkanoContext, + input_manager: InputManager, + event_loop_proxy: EventLoopProxy, + ) -> Self { Self { vulkan_context: Arc::new(VulkanContext::new(vulkano_context)), vulkano_windows: Arc::new(VulkanoWindows::default()), gui: HashMap::new(), input_manager: Arc::new(input_manager), - scene_manager: SceneManager::new(), + scene_manager: HashMap::new(), timer: Arc::new(Timer::new()), + event_loop_proxy, } } } -impl ApplicationHandler for App { +impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if let Some(vulkano_windows) = Arc::get_mut(&mut self.vulkano_windows) { let window_id = vulkano_windows.create_window( @@ -81,10 +90,12 @@ impl ApplicationHandler for App { ) }; self.gui.insert(window_id, gui); - } - self.scene_manager - .load_scene(Box::new(MainScene::default())); + let mut scene_manager = SceneManager::new(); + scene_manager.load_scene(Box::new(TestScene::default())); + + self.scene_manager.insert(window_id, scene_manager); + } } fn device_event( @@ -139,13 +150,16 @@ impl ApplicationHandler for App { &self.vulkan_context, &self.input_manager, &self.timer, + id, + &self.event_loop_proxy, )); - self.scene_manager + let scene_manager = self.scene_manager.get_mut(&id).unwrap(); + scene_manager .load_scene_if_not_loaded(&scene_context) .unwrap(); - if let Some(scene) = self.scene_manager.current_scene_mut() { + if let Some(scene) = scene_manager.current_scene_mut() { scene.update(&scene_context).unwrap(); let acquire_future = renderer.acquire(None, |_| {}).unwrap(); @@ -164,4 +178,23 @@ impl ApplicationHandler for App { let window = self.vulkano_windows.get_primary_window().unwrap(); window.request_redraw(); } + + fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { + match event { + UserEvent::CursorGrabMode(window_id, grab) => { + let window = self.vulkano_windows.get_window(window_id).unwrap(); + if let Err(e) = window.set_cursor_grab(grab) { + log::error!("Failed to set cursor grab: {}", e); + } + } + UserEvent::CursorVisible(window_id, visible) => { + let window = self.vulkano_windows.get_window(window_id).unwrap(); + window.set_cursor_visible(visible); + } + UserEvent::ChangeScene(window_id, scene) => { + let scene_manager = self.scene_manager.get_mut(&window_id).unwrap(); + scene_manager.load_scene(scene); + } + } + } } diff --git a/src/core/app/user_event.rs b/src/core/app/user_event.rs new file mode 100644 index 0000000..316ac4c --- /dev/null +++ b/src/core/app/user_event.rs @@ -0,0 +1,9 @@ +use winit::window::{CursorGrabMode, WindowId}; + +use crate::core::scene::Scene; + +pub enum UserEvent { + CursorGrabMode(WindowId, CursorGrabMode), + CursorVisible(WindowId, bool), + ChangeScene(WindowId, Box), +} diff --git a/src/core/scene/context.rs b/src/core/scene/context.rs index 545f39c..4e0dab3 100644 --- a/src/core/scene/context.rs +++ b/src/core/scene/context.rs @@ -9,12 +9,19 @@ use vulkano::{ memory::allocator::StandardMemoryAllocator, }; use vulkano_util::renderer::VulkanoWindowRenderer; - -use crate::core::{ - app::DEPTH_IMAGE_ID, input::InputManager, render::vulkan_context::VulkanContext, timer::Timer, +use winit::{ + event_loop::EventLoopProxy, + window::{Window, WindowId}, }; -pub struct SceneContext { +use crate::core::{ + app::{DEPTH_IMAGE_ID, user_event::UserEvent}, + input::InputManager, + render::vulkan_context::VulkanContext, + timer::Timer, +}; + +pub struct SceneContext<'a> { pub instance: Arc, pub device: Arc, pub graphics_queue: Arc, @@ -29,22 +36,28 @@ pub struct SceneContext { pub depth_stencil_image_view: Arc, pub input_manager: Arc, pub timer: Arc, + pub event_loop_proxy: &'a EventLoopProxy, + pub window_id: WindowId, } -impl +impl<'a> From<( &mut VulkanoWindowRenderer, &Arc, &Arc, &Arc, - )> for SceneContext + WindowId, + &'a EventLoopProxy, + )> for SceneContext<'a> { fn from( - (renderer, vulkan_context, input_manager, timer): ( + (renderer, vulkan_context, input_manager, timer, window_id, event_loop_proxy): ( &mut VulkanoWindowRenderer, &Arc, &Arc, &Arc, + WindowId, + &'a EventLoopProxy, ), ) -> Self { let (command_buffer_allocator, descriptor_set_allocator) = { @@ -81,6 +94,8 @@ impl depth_stencil_image_view: renderer.get_additional_image_view(DEPTH_IMAGE_ID), input_manager: input_manager.clone(), timer: timer.clone(), + event_loop_proxy, + window_id, } } } diff --git a/src/game/mod.rs b/src/game/mod.rs index e8924d3..065e8e4 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,2 +1,2 @@ pub mod assets; -pub mod main_scene; +pub mod scenes; diff --git a/src/game/main_scene.rs b/src/game/scenes/main_scene.rs similarity index 85% rename from src/game/main_scene.rs rename to src/game/scenes/main_scene.rs index 22c851b..bad12d0 100644 --- a/src/game/main_scene.rs +++ b/src/game/scenes/main_scene.rs @@ -1,31 +1,26 @@ use std::{error::Error, sync::Arc}; +use crate::core::app::user_event::UserEvent; use crate::core::render::primitives::camera::Camera3D; use crate::core::render::primitives::transform::Transform; use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::scene::SceneContext; +use crate::game::assets::square::Square; use egui_winit_vulkano::{Gui, egui}; use glam::EulerRot; use glam::Quat; use glam::Vec3; -use vulkano::format::Format; -use vulkano::image::Image; -use vulkano::image::ImageCreateInfo; -use vulkano::image::ImageUsage; -use vulkano::memory::allocator::AllocationCreateInfo; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, }, - image::view::ImageView, pipeline::graphics::viewport::Viewport, render_pass::{AttachmentLoadOp, AttachmentStoreOp}, sync::GpuFuture, }; - -use super::assets::square::Square; +use winit::window::CursorGrabMode; pub struct MainSceneState { square: Square, @@ -119,6 +114,38 @@ impl Scene for MainScene { scene_context.aspect_ratio, ); + if scene_context + .input_manager + .get_virtual_input_state("mouse_left") + > 0.0 + { + let _ = scene_context + .event_loop_proxy + .send_event(UserEvent::CursorVisible(scene_context.window_id, false)); + let _ = scene_context + .event_loop_proxy + .send_event(UserEvent::CursorGrabMode( + scene_context.window_id, + CursorGrabMode::Locked, + )); + } + + if scene_context + .input_manager + .get_virtual_input_state("mouse_right") + > 0.0 + { + let _ = scene_context + .event_loop_proxy + .send_event(UserEvent::CursorVisible(scene_context.window_id, true)); + let _ = scene_context + .event_loop_proxy + .send_event(UserEvent::CursorGrabMode( + scene_context.window_id, + CursorGrabMode::None, + )); + } + Ok(()) } diff --git a/src/game/scenes/mod.rs b/src/game/scenes/mod.rs new file mode 100644 index 0000000..e5c4945 --- /dev/null +++ b/src/game/scenes/mod.rs @@ -0,0 +1,2 @@ +pub mod main_scene; +pub mod test_scene; diff --git a/src/game/scenes/test_scene.rs b/src/game/scenes/test_scene.rs new file mode 100644 index 0000000..9ed8707 --- /dev/null +++ b/src/game/scenes/test_scene.rs @@ -0,0 +1,111 @@ +use std::error::Error; + +use crate::core::app::user_event::UserEvent; +use crate::core::scene::Scene; +use crate::core::scene::SceneContext; +use egui_winit_vulkano::{Gui, egui}; +use vulkano::command_buffer::AutoCommandBufferBuilder; +use vulkano::command_buffer::CommandBufferUsage; +use vulkano::command_buffer::RenderingAttachmentInfo; +use vulkano::command_buffer::RenderingInfo; +use vulkano::pipeline::graphics::viewport::Viewport; +use vulkano::render_pass::AttachmentLoadOp; +use vulkano::render_pass::AttachmentStoreOp; +use vulkano::sync::GpuFuture; + +use super::main_scene::MainScene; + +pub struct MainSceneState {} + +#[derive(Default)] +pub struct TestScene { + state: Option, +} + +impl Scene for TestScene { + fn loaded(&self) -> bool { + self.state.is_some() + } + + fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { + self.state = Some(MainSceneState {}); + + Ok(()) + } + + fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box> { + Ok(()) + } + + fn render( + &mut self, + acquire_future: Box, + scene_context: &SceneContext, + gui: &mut Gui, + ) -> Result, Box> { + let mut builder = AutoCommandBufferBuilder::primary( + scene_context.command_buffer_allocator.clone(), + scene_context.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + )?; + + { + let viewport = Viewport { + offset: [0.0, 0.0], + extent: scene_context.window_size, + depth_range: 0.0..=1.0, + }; + + builder + .begin_rendering(RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + ..RenderingAttachmentInfo::image_view( + scene_context.swapchain_image_view.clone(), + ) + })], + depth_attachment: Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::DontCare, + clear_value: Some([1.0].into()), + ..RenderingAttachmentInfo::image_view( + scene_context.depth_stencil_image_view.clone(), + ) + }), + ..Default::default() + })? + .set_viewport(0, [viewport].into_iter().collect())?; + } + + builder.end_rendering()?; + + let command_buffer = builder.build()?; + + let render_future = + acquire_future.then_execute(scene_context.graphics_queue.clone(), command_buffer)?; + + gui.immediate_ui(|gui| { + let ctx = gui.context(); + + egui::CentralPanel::default().show(&ctx, |ui| { + if ui.button("Start Game").clicked() { + let _ = scene_context + .event_loop_proxy + .send_event(UserEvent::ChangeScene( + scene_context.window_id, + Box::new(MainScene::default()), + )); + } + }); + }); + + let render_future = + gui.draw_on_image(render_future, scene_context.swapchain_image_view.clone()); + + Ok(render_future) + } + + fn unload(&mut self) {} +} diff --git a/src/main.rs b/src/main.rs index ffe5f72..1ea49c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,10 +77,11 @@ fn main() { let vulkano_context = VulkanoContext::new(vulkano_config); - let event_loop = EventLoop::new().unwrap(); + let event_loop = EventLoop::with_user_event().build().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); + let proxy = event_loop.create_proxy(); - let mut app = core::app::App::new(vulkano_context, input_manager); + let mut app = core::app::App::new(vulkano_context, input_manager, proxy); match event_loop.run_app(&mut app) { Ok(_) => {} From 2c169548b99a307fa082b0b0a8467cba5274fb39 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 30 May 2025 19:04:46 +0200 Subject: [PATCH 89/91] Refactor app context and render pass manager --- src/core/app/context.rs | 230 +++++++++++++++++++++ src/core/app/mod.rs | 232 ++++++++++++--------- src/core/app/user_event.rs | 2 + src/core/render/mod.rs | 1 + src/core/render/render_pass_manager.rs | 113 +++++++++++ src/core/scene/context.rs | 101 --------- src/core/scene/manager.rs | 96 ++++++--- src/core/scene/mod.rs | 18 +- src/game/scenes/main_scene.rs | 271 ++++++++++++++----------- src/game/scenes/mod.rs | 2 +- src/game/scenes/settings_scene.rs | 135 ++++++++++++ src/game/scenes/test_scene.rs | 111 ---------- 12 files changed, 846 insertions(+), 466 deletions(-) create mode 100644 src/core/app/context.rs create mode 100644 src/core/render/render_pass_manager.rs delete mode 100644 src/core/scene/context.rs create mode 100644 src/game/scenes/settings_scene.rs delete mode 100644 src/game/scenes/test_scene.rs diff --git a/src/core/app/context.rs b/src/core/app/context.rs new file mode 100644 index 0000000..1c0ccb6 --- /dev/null +++ b/src/core/app/context.rs @@ -0,0 +1,230 @@ +use std::sync::{Arc, Mutex}; + +use egui_winit_vulkano::Gui; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; +use vulkano_util::{renderer::VulkanoWindowRenderer, window::VulkanoWindows}; +use winit::{ + event_loop::EventLoopProxy, + window::{Window, WindowId}, +}; + +use crate::core::{input::InputManager, render::vulkan_context::VulkanContext, timer::Timer}; + +use super::user_event::UserEvent; + +/// Contexte d'application unifié avec Arc> pour la mutabilité partagée +#[derive(Clone)] +pub struct ApplicationContext { + // Données Vulkan (immutables) + pub vulkan_context: Arc, + pub device: Arc, + pub instance: Arc, + pub graphics_queue: Arc, + pub compute_queue: Arc, + pub transfer_queue: Option>, + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, + pub event_loop_proxy: EventLoopProxy, + pub window_id: WindowId, + + // Données mutables partagées avec Arc> + pub vulkano_windows: Arc>, + pub input_manager: Arc>, + pub timer: Arc>, + pub gui: Arc>, +} + +impl ApplicationContext { + pub fn new( + vulkan_context: Arc, + vulkano_windows: Arc>, + input_manager: Arc>, + timer: Arc>, + gui: Arc>, + event_loop_proxy: EventLoopProxy, + window_id: WindowId, + ) -> Self { + let vulkano_context_inner = vulkan_context.vulkano_context(); + + Self { + // Données Vulkan + vulkan_context: vulkan_context.clone(), + device: vulkano_context_inner.device().clone(), + instance: vulkano_context_inner.instance().clone(), + graphics_queue: vulkano_context_inner.graphics_queue().clone(), + compute_queue: vulkano_context_inner.compute_queue().clone(), + transfer_queue: vulkano_context_inner.transfer_queue().cloned(), + memory_allocator: vulkano_context_inner.memory_allocator().clone(), + command_buffer_allocator: vulkan_context.command_buffer_allocator().clone(), + descriptor_set_allocator: vulkan_context.descriptor_set_allocator().clone(), + event_loop_proxy, + window_id, + + // Données mutables partagées + vulkano_windows, + input_manager, + timer, + gui, + } + } + + /// Récupère les résolutions disponibles du moniteur (méthode utilitaire statique) + fn get_monitor_resolutions(window: &Window) -> Vec<(u32, u32)> { + // Première tentative : moniteur actuel + if let Some(monitor) = window.current_monitor() { + let resolutions = Self::extract_resolutions_from_monitor(&monitor); + if !resolutions.is_empty() { + log::debug!( + "Résolutions trouvées via moniteur actuel: {:?}", + resolutions + ); + return resolutions; + } + } + + // Deuxième tentative : tous les moniteurs disponibles + log::debug!("Tentative de récupération via tous les moniteurs disponibles..."); + let mut all_resolutions = Vec::new(); + + for monitor in window.available_monitors() { + let resolutions = Self::extract_resolutions_from_monitor(&monitor); + all_resolutions.extend(resolutions); + } + + if !all_resolutions.is_empty() { + // Supprime les doublons et trie + all_resolutions.sort_unstable(); + all_resolutions.dedup(); + all_resolutions.sort_by(|a, b| (b.0 * b.1).cmp(&(a.0 * a.1))); + + log::debug!( + "Résolutions trouvées via tous les moniteurs: {:?}", + all_resolutions + ); + return all_resolutions; + } + + // Aucune résolution détectée - retourne un vecteur vide + log::warn!("Aucune résolution détectée pour cette fenêtre"); + Vec::new() + } + + /// Extrait les résolutions d'un moniteur donné + fn extract_resolutions_from_monitor( + monitor: &winit::monitor::MonitorHandle, + ) -> Vec<(u32, u32)> { + let video_modes: Vec<_> = monitor.video_modes().collect(); + + if video_modes.is_empty() { + log::debug!( + "Aucun mode vidéo trouvé pour le moniteur {:?}", + monitor.name() + ); + return Vec::new(); + } + + let resolutions: Vec<(u32, u32)> = video_modes + .into_iter() + .map(|mode| { + let size = mode.size(); + (size.width, size.height) + }) + .collect(); + + log::debug!( + "Modes vidéo trouvés pour {:?}: {:?}", + monitor.name(), + resolutions + ); + resolutions + } + + /// Récupère les résolutions disponibles + pub fn get_available_resolutions(&self) -> Vec<(u32, u32)> { + self.with_renderer(|renderer| Self::get_monitor_resolutions(&renderer.window())) + } + + /// Récupère le delta time actuel depuis le timer + pub fn get_delta_time(&self) -> f32 { + self.with_timer(|timer| timer.delta_time()) + } + + /// Récupère la taille de la fenêtre depuis le renderer + pub fn get_window_size(&self) -> [f32; 2] { + self.with_renderer(|renderer| renderer.window_size()) + } + + /// Récupère l'aspect ratio depuis le renderer + pub fn get_aspect_ratio(&self) -> f32 { + self.with_renderer(|renderer| renderer.aspect_ratio()) + } + + pub fn with_renderer(&self, f: F) -> T + where + F: FnOnce(&VulkanoWindowRenderer) -> T, + { + let vulkano_windows = self + .vulkano_windows + .lock() + .expect("Failed to lock vulkano_windows"); + let renderer = vulkano_windows + .get_renderer(self.window_id) + .expect("Failed to get renderer"); + f(&renderer) + } + + pub fn with_renderer_mut(&mut self, f: F) -> T + where + F: FnOnce(&mut VulkanoWindowRenderer) -> T, + { + let mut vulkano_windows = self + .vulkano_windows + .lock() + .expect("Failed to lock vulkano_windows"); + let renderer = vulkano_windows + .get_renderer_mut(self.window_id) + .expect("Failed to get renderer"); + f(renderer) + } + + pub fn with_gui(&self, f: F) -> T + where + F: FnOnce(&Gui) -> T, + { + let gui = self.gui.lock().unwrap(); + f(&gui) + } + + pub fn with_gui_mut(&mut self, f: F) -> T + where + F: FnOnce(&mut Gui) -> T, + { + let mut gui = self.gui.lock().unwrap(); + f(&mut gui) + } + + /// Méthode utilitaire pour accéder à l'input manager de manière thread-safe + pub fn with_input_manager(&self, f: F) -> T + where + F: FnOnce(&InputManager) -> T, + { + let input_manager = self.input_manager.lock().unwrap(); + f(&input_manager) + } + + /// Méthode utilitaire pour accéder au timer de manière thread-safe + pub fn with_timer(&self, f: F) -> T + where + F: FnOnce(&Timer) -> T, + { + let timer = self.timer.lock().unwrap(); + f(&timer) + } +} diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs index ab548f8..243420b 100644 --- a/src/core/app/mod.rs +++ b/src/core/app/mod.rs @@ -1,12 +1,11 @@ use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use super::render::vulkan_context::VulkanContext; -use super::scene::SceneContext; use crate::core::input::InputManager; -use crate::core::scene::SceneManager; +use crate::core::scene::manager::SceneManager; use crate::core::timer::Timer; -use crate::game::scenes::test_scene::TestScene; +use crate::game::scenes::main_scene::MainScene; use egui_winit_vulkano::{Gui, GuiConfig}; use user_event::UserEvent; use vulkano::format::Format; @@ -19,18 +18,23 @@ use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; use winit::window::WindowId; +use self::context::ApplicationContext; + +pub mod context; pub mod user_event; pub const DEPTH_IMAGE_ID: usize = 0; pub struct App { vulkan_context: Arc, - vulkano_windows: Arc, - gui: HashMap, + vulkano_windows: Arc>, + gui: HashMap>>, scene_manager: HashMap, - input_manager: Arc, - timer: Arc, + input_manager: Arc>, + timer: Arc>, event_loop_proxy: EventLoopProxy, + // Context d'application partagé par fenêtre - architecture unifiée + app_contexts: HashMap>>, } impl App { @@ -41,61 +45,61 @@ impl App { ) -> Self { Self { vulkan_context: Arc::new(VulkanContext::new(vulkano_context)), - vulkano_windows: Arc::new(VulkanoWindows::default()), + vulkano_windows: Arc::new(Mutex::new(VulkanoWindows::default())), gui: HashMap::new(), - input_manager: Arc::new(input_manager), + input_manager: Arc::new(Mutex::new(input_manager)), scene_manager: HashMap::new(), - timer: Arc::new(Timer::new()), + timer: Arc::new(Mutex::new(Timer::new())), event_loop_proxy, + app_contexts: HashMap::new(), } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if let Some(vulkano_windows) = Arc::get_mut(&mut self.vulkano_windows) { - let window_id = vulkano_windows.create_window( + let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window_id = vulkano_windows.create_window( + event_loop, + &self.vulkan_context.vulkano_context(), + &WindowDescriptor { + title: "Rust ASH Test".to_string(), + width: 800.0, + height: 600.0, + present_mode: PresentMode::Fifo, + cursor_visible: false, + cursor_locked: true, + ..Default::default() + }, + |_| {}, + ); + + let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); + renderer.add_additional_image_view( + DEPTH_IMAGE_ID, + Format::D16_UNORM, + ImageUsage::DEPTH_STENCIL_ATTACHMENT, + ); + + let gui = { + Gui::new( event_loop, - self.vulkan_context.vulkano_context(), - &WindowDescriptor { - title: "Rust ASH Test".to_string(), - width: 800.0, - height: 600.0, - present_mode: PresentMode::Fifo, - cursor_visible: false, - cursor_locked: true, + renderer.surface(), + renderer.graphics_queue(), + renderer.swapchain_format(), + GuiConfig { + is_overlay: true, + allow_srgb_render_target: true, ..Default::default() }, - |_| {}, - ); + ) + }; + self.gui.insert(window_id, Arc::new(Mutex::new(gui))); - let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); - renderer.add_additional_image_view( - DEPTH_IMAGE_ID, - Format::D16_UNORM, - ImageUsage::DEPTH_STENCIL_ATTACHMENT, - ); + let mut scene_manager = SceneManager::new(); + scene_manager.load_scene(Box::new(MainScene::default())); - let gui = { - Gui::new( - event_loop, - renderer.surface(), - renderer.graphics_queue(), - renderer.swapchain_format(), - GuiConfig { - is_overlay: true, - allow_srgb_render_target: true, - ..Default::default() - }, - ) - }; - self.gui.insert(window_id, gui); - - let mut scene_manager = SceneManager::new(); - scene_manager.load_scene(Box::new(TestScene::default())); - - self.scene_manager.insert(window_id, scene_manager); - } + self.scene_manager.insert(window_id, scene_manager); } fn device_event( @@ -104,23 +108,17 @@ impl ApplicationHandler for App { _device_id: winit::event::DeviceId, event: winit::event::DeviceEvent, ) { - match Arc::get_mut(&mut self.input_manager) { - Some(input_manager) => input_manager.process_device_event(&event), - None => log::error!("Failed to get a mutable reference to the input manager"), - } + let mut input_manager = self.input_manager.lock().unwrap(); + input_manager.process_device_event(&event); } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { - let renderer = Arc::get_mut(&mut self.vulkano_windows) - .unwrap() - .get_renderer_mut(id) - .unwrap(); - let gui = self.gui.get_mut(&id).unwrap(); - - if !gui.update(&event) { - match Arc::get_mut(&mut self.input_manager) { - Some(input_manager) => input_manager.process_window_event(&event), - None => log::error!("Failed to get a mutable reference to the input manager"), + { + let gui = self.gui.get_mut(&id).unwrap(); + let mut gui = gui.lock().unwrap(); + if !gui.update(&event) { + let mut input_manager = self.input_manager.lock().unwrap(); + input_manager.process_window_event(&event); } } @@ -129,45 +127,64 @@ impl ApplicationHandler for App { log::debug!("The close button was pressed; stopping"); event_loop.exit(); } - WindowEvent::Resized(_) => { - renderer.resize(); - } - WindowEvent::ScaleFactorChanged { .. } => { - renderer.resize(); + WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. } => { + let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + vulkano_windows.get_renderer_mut(id).unwrap().resize(); } WindowEvent::RedrawRequested => { - match Arc::get_mut(&mut self.input_manager) { - Some(input_manager) => input_manager.update(), - None => log::error!("Failed to get a mutable reference to the input manager"), + { + let mut input_manager = self + .input_manager + .lock() + .expect("Failed to lock input manager"); + input_manager.update(); } - match Arc::get_mut(&mut self.timer) { - Some(timer) => timer.update(), - None => log::error!("Failed to get a mutable reference to the timer"), + { + let mut timer = self.timer.lock().expect("Failed to lock timer"); + timer.update(); } - let mut scene_context = SceneContext::from(( - &mut *renderer, - &self.vulkan_context, - &self.input_manager, - &self.timer, - id, - &self.event_loop_proxy, - )); + // Créer ou mettre à jour le contexte d'application + let app_context = self + .app_contexts + .entry(id) + .or_insert_with(|| { + Arc::new(Mutex::new(ApplicationContext::new( + self.vulkan_context.clone(), + self.vulkano_windows.clone(), + self.input_manager.clone(), + self.timer.clone(), + self.gui.get(&id).unwrap().clone(), + self.event_loop_proxy.clone(), + id, + ))) + }) + .clone(); let scene_manager = self.scene_manager.get_mut(&id).unwrap(); - scene_manager - .load_scene_if_not_loaded(&scene_context) - .unwrap(); - if let Some(scene) = scene_manager.current_scene_mut() { - scene.update(&scene_context).unwrap(); + // Utiliser le contexte partagé pour les scènes + { + let mut context = app_context.lock().unwrap(); - let acquire_future = renderer.acquire(None, |_| {}).unwrap(); - // Update the swapchain image view to the current one after acquiring the next image - scene_context.swapchain_image_view = renderer.swapchain_image_view(); + scene_manager + .load_scene_if_not_loaded(&mut context) + .unwrap(); - let acquire_future = scene.render(acquire_future, &scene_context, gui).unwrap(); - renderer.present(acquire_future, true); + if let Some(scene) = scene_manager.current_scene_mut() { + scene.update(&mut context).unwrap(); + + let acquire_future = context + .with_renderer_mut(|renderer| renderer.acquire(None, |_| {}).unwrap()); + + let acquire_future = scene.render(acquire_future, &mut context).unwrap(); + + context.with_renderer_mut(|renderer| { + renderer.present(acquire_future, true); + }); + } else { + log::warn!("No current scene found for update!"); + } } } _ => {} @@ -175,25 +192,46 @@ impl ApplicationHandler for App { } fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let window = self.vulkano_windows.get_primary_window().unwrap(); + let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_primary_window().unwrap(); window.request_redraw(); } fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { match event { UserEvent::CursorGrabMode(window_id, grab) => { - let window = self.vulkano_windows.get_window(window_id).unwrap(); + let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_window(window_id).unwrap(); if let Err(e) = window.set_cursor_grab(grab) { log::error!("Failed to set cursor grab: {}", e); } } UserEvent::CursorVisible(window_id, visible) => { - let window = self.vulkano_windows.get_window(window_id).unwrap(); + let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_window(window_id).unwrap(); window.set_cursor_visible(visible); } UserEvent::ChangeScene(window_id, scene) => { - let scene_manager = self.scene_manager.get_mut(&window_id).unwrap(); - scene_manager.load_scene(scene); + if let Some(scene_manager) = self.scene_manager.get_mut(&window_id) { + scene_manager.load_scene(scene); + } + } + UserEvent::ChangeResolution(window_id, width, height) => { + let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + let window = vulkano_windows.get_window(window_id).unwrap(); + let _ = window.request_inner_size(winit::dpi::LogicalSize::new(width, height)); + let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap(); + renderer.resize(); + log::info!( + "Resolution changed to {}x{} for window {:?}", + width, + height, + window_id + ); + } + UserEvent::Exit(window_id) => { + log::info!("Exit requested for window {:?}", window_id); + event_loop.exit(); } } } diff --git a/src/core/app/user_event.rs b/src/core/app/user_event.rs index 316ac4c..21b70f1 100644 --- a/src/core/app/user_event.rs +++ b/src/core/app/user_event.rs @@ -6,4 +6,6 @@ pub enum UserEvent { CursorGrabMode(WindowId, CursorGrabMode), CursorVisible(WindowId, bool), ChangeScene(WindowId, Box), + ChangeResolution(WindowId, f32, f32), + Exit(WindowId), } diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index 07e4134..f56fdf4 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,3 +1,4 @@ pub mod primitives; +pub mod render_pass_manager; pub mod texture; pub mod vulkan_context; diff --git a/src/core/render/render_pass_manager.rs b/src/core/render/render_pass_manager.rs new file mode 100644 index 0000000..02c3d7a --- /dev/null +++ b/src/core/render/render_pass_manager.rs @@ -0,0 +1,113 @@ +use std::sync::Arc; +use vulkano::{ + command_buffer::{AutoCommandBufferBuilder, RenderingAttachmentInfo, RenderingInfo}, + image::view::ImageView, + pipeline::graphics::viewport::Viewport, + render_pass::{AttachmentLoadOp, AttachmentStoreOp}, +}; + +/// Types de render passes disponibles +#[derive(Debug, Clone)] +pub enum RenderPassType { + Standard, + ShadowMap, + PostProcess, +} + +/// Configuration pour un render pass +#[derive(Debug, Clone)] +pub struct RenderPassConfig { + pub pass_type: RenderPassType, + pub clear_color: Option<[f32; 4]>, + pub clear_depth: Option, + pub load_op: AttachmentLoadOp, + pub store_op: AttachmentStoreOp, +} + +impl Default for RenderPassConfig { + fn default() -> Self { + Self { + pass_type: RenderPassType::Standard, + clear_color: Some([0.0, 0.0, 0.0, 1.0]), + clear_depth: Some(1.0), + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + } + } +} + +/// Gestionnaire de render passes réutilisable +pub struct RenderPassManager; + +impl RenderPassManager { + /// Commence un render pass standard avec les paramètres donnés + pub fn begin_standard_rendering( + builder: &mut AutoCommandBufferBuilder, + config: &RenderPassConfig, + color_attachment: Arc, + depth_attachment: Option>, + window_size: [f32; 2], + ) -> Result<(), Box> { + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size, + depth_range: 0.0..=1.0, + }; + + let mut rendering_info = RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: config.load_op, + store_op: config.store_op, + clear_value: config.clear_color.map(|c| c.into()), + ..RenderingAttachmentInfo::image_view(color_attachment) + })], + depth_attachment: None, + ..Default::default() + }; + + if let Some(depth_view) = depth_attachment { + rendering_info.depth_attachment = Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::DontCare, + clear_value: config.clear_depth.map(|d| [d].into()), + ..RenderingAttachmentInfo::image_view(depth_view) + }); + } + + builder + .begin_rendering(rendering_info)? + .set_viewport(0, [viewport].into_iter().collect())?; + + Ok(()) + } + + /// Termine le render pass actuel + pub fn end_rendering( + builder: &mut AutoCommandBufferBuilder, + ) -> Result<(), Box> { + builder.end_rendering()?; + Ok(()) + } + + /// Crée une configuration pour un render pass shadow map + pub fn shadow_map_config() -> RenderPassConfig { + RenderPassConfig { + pass_type: RenderPassType::ShadowMap, + clear_color: None, + clear_depth: Some(1.0), + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + } + } + + /// Crée une configuration pour un render pass de post-processing + pub fn post_process_config() -> RenderPassConfig { + RenderPassConfig { + pass_type: RenderPassType::PostProcess, + clear_color: Some([0.0, 0.0, 0.0, 1.0]), + clear_depth: None, + load_op: AttachmentLoadOp::Load, + store_op: AttachmentStoreOp::Store, + } + } +} diff --git a/src/core/scene/context.rs b/src/core/scene/context.rs deleted file mode 100644 index 4e0dab3..0000000 --- a/src/core/scene/context.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::sync::Arc; - -use vulkano::{ - command_buffer::allocator::StandardCommandBufferAllocator, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{Device, Queue}, - image::view::ImageView, - instance::Instance, - memory::allocator::StandardMemoryAllocator, -}; -use vulkano_util::renderer::VulkanoWindowRenderer; -use winit::{ - event_loop::EventLoopProxy, - window::{Window, WindowId}, -}; - -use crate::core::{ - app::{DEPTH_IMAGE_ID, user_event::UserEvent}, - input::InputManager, - render::vulkan_context::VulkanContext, - timer::Timer, -}; - -pub struct SceneContext<'a> { - pub instance: Arc, - pub device: Arc, - pub graphics_queue: Arc, - pub compute_queue: Arc, - pub transfer_queue: Option>, - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, - pub window_size: [f32; 2], - pub aspect_ratio: f32, - pub swapchain_image_view: Arc, - pub depth_stencil_image_view: Arc, - pub input_manager: Arc, - pub timer: Arc, - pub event_loop_proxy: &'a EventLoopProxy, - pub window_id: WindowId, -} - -impl<'a> - From<( - &mut VulkanoWindowRenderer, - &Arc, - &Arc, - &Arc, - WindowId, - &'a EventLoopProxy, - )> for SceneContext<'a> -{ - fn from( - (renderer, vulkan_context, input_manager, timer, window_id, event_loop_proxy): ( - &mut VulkanoWindowRenderer, - &Arc, - &Arc, - &Arc, - WindowId, - &'a EventLoopProxy, - ), - ) -> Self { - let (command_buffer_allocator, descriptor_set_allocator) = { - ( - vulkan_context.command_buffer_allocator().clone(), - vulkan_context.descriptor_set_allocator().clone(), - ) - }; - - let (instance, device, graphics_queue, compute_queue, transfer_queue, memory_allocator) = { - let vulkan_context = vulkan_context.vulkano_context(); - ( - vulkan_context.instance().clone(), - vulkan_context.device().clone(), - vulkan_context.graphics_queue().clone(), - vulkan_context.compute_queue().clone(), - vulkan_context.transfer_queue().cloned(), - vulkan_context.memory_allocator().clone(), - ) - }; - - Self { - instance, - device, - graphics_queue, - compute_queue, - transfer_queue, - memory_allocator, - command_buffer_allocator, - descriptor_set_allocator, - window_size: renderer.window_size(), - aspect_ratio: renderer.aspect_ratio(), - swapchain_image_view: renderer.swapchain_image_view(), - depth_stencil_image_view: renderer.get_additional_image_view(DEPTH_IMAGE_ID), - input_manager: input_manager.clone(), - timer: timer.clone(), - event_loop_proxy, - window_id, - } - } -} diff --git a/src/core/scene/manager.rs b/src/core/scene/manager.rs index e44765d..9ff65ac 100644 --- a/src/core/scene/manager.rs +++ b/src/core/scene/manager.rs @@ -1,52 +1,84 @@ use std::error::Error; -use super::{Scene, SceneContext}; +use crate::core::app::context::ApplicationContext; + +use super::Scene; pub struct SceneManager { - current_scene: Option>, + scenes: Vec>, + current_scene_index: Option, } impl SceneManager { pub fn new() -> Self { Self { - current_scene: None, + scenes: Vec::new(), + current_scene_index: None, + } + } + + pub fn load_scene(&mut self, scene: Box) { + self.scenes.push(scene); + self.current_scene_index = Some(self.scenes.len() - 1); + } + + pub fn replace_current_scene(&mut self, scene: Box) { + if let Some(index) = self.current_scene_index { + if index < self.scenes.len() { + self.scenes[index].unload(); + self.scenes[index] = scene; + } + } else { + self.load_scene(scene); + } + } + + pub fn current_scene(&self) -> Option<&Box> { + if let Some(index) = self.current_scene_index { + self.scenes.get(index) + } else { + None + } + } + + pub fn current_scene_mut(&mut self) -> Option<&mut Box> { + log::debug!( + "current_scene_mut called - index: {:?}, scenes len: {}", + self.current_scene_index, + self.scenes.len() + ); + if let Some(index) = self.current_scene_index { + log::debug!("Getting scene at index {}", index); + self.scenes.get_mut(index) + } else { + log::debug!("No current scene index set"); + None } } pub fn load_scene_if_not_loaded( &mut self, - scene_context: &SceneContext, + app_context: &mut ApplicationContext, ) -> Result<(), Box> { - if let Some(current_scene) = self.current_scene.as_mut() { - if !current_scene.loaded() { - current_scene.load(scene_context)?; + log::debug!("SceneManager::load_scene_if_not_loaded called"); + log::debug!( + "Current scene index: {:?}, scenes count: {}", + self.current_scene_index, + self.scenes.len() + ); + + if let Some(scene) = self.current_scene_mut() { + log::debug!("Scene found, checking if loaded: {}", scene.loaded()); + if !scene.loaded() { + log::debug!("Scene not loaded, loading..."); + scene.load(app_context)?; + log::debug!("Scene loaded successfully"); + } else { + log::debug!("Scene already loaded"); } + } else { + log::warn!("No scene found in SceneManager!"); } Ok(()) } - - pub fn load_scene(&mut self, scene: Box) { - if let Some(current_scene) = self.current_scene.as_mut() { - current_scene.unload(); - } - self.current_scene = Some(scene); - } - - pub fn current_scene(&self) -> Option<&Box> { - if let Some(current_scene) = self.current_scene.as_ref() { - if current_scene.loaded() { - return Some(current_scene); - } - } - None - } - - pub fn current_scene_mut(&mut self) -> Option<&mut Box> { - if let Some(current_scene) = self.current_scene.as_mut() { - if current_scene.loaded() { - return Some(current_scene); - } - } - None - } } diff --git a/src/core/scene/mod.rs b/src/core/scene/mod.rs index 8642224..58a75df 100644 --- a/src/core/scene/mod.rs +++ b/src/core/scene/mod.rs @@ -1,23 +1,19 @@ -use std::{error::Error, sync::Arc}; +use std::error::Error; -use egui_winit_vulkano::Gui; -use vulkano::{image::view::ImageView, sync::GpuFuture}; +use vulkano::sync::GpuFuture; -mod context; -pub use context::SceneContext; +use crate::core::app::context::ApplicationContext; -mod manager; -pub use manager::SceneManager; +pub mod manager; pub trait Scene { fn loaded(&self) -> bool; - fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box>; - fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box>; + fn load(&mut self, app_context: &mut ApplicationContext) -> Result<(), Box>; + fn update(&mut self, app_context: &mut ApplicationContext) -> Result<(), Box>; fn render( &mut self, acquire_future: Box, - scene_context: &SceneContext, - gui: &mut Gui, + app_context: &mut ApplicationContext, ) -> Result, Box>; fn unload(&mut self); } diff --git a/src/game/scenes/main_scene.rs b/src/game/scenes/main_scene.rs index bad12d0..6461289 100644 --- a/src/game/scenes/main_scene.rs +++ b/src/game/scenes/main_scene.rs @@ -1,23 +1,21 @@ -use std::{error::Error, sync::Arc}; +use std::error::Error; +use super::settings_scene::SettingsScene; +use crate::core::app::DEPTH_IMAGE_ID; +use crate::core::app::context::ApplicationContext; use crate::core::app::user_event::UserEvent; use crate::core::render::primitives::camera::Camera3D; use crate::core::render::primitives::transform::Transform; +use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::render::texture::Texture; use crate::core::scene::Scene; -use crate::core::scene::SceneContext; use crate::game::assets::square::Square; -use egui_winit_vulkano::{Gui, egui}; +use egui_winit_vulkano::egui; use glam::EulerRot; use glam::Quat; use glam::Vec3; use vulkano::{ - command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, - RenderingAttachmentInfo, RenderingInfo, - }, - pipeline::graphics::viewport::Viewport, - render_pass::{AttachmentLoadOp, AttachmentStoreOp}, + command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract}, sync::GpuFuture, }; use winit::window::CursorGrabMode; @@ -40,12 +38,22 @@ impl Scene for MainScene { self.state.is_some() } - fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { + fn load( + &mut self, + app_context: &mut ApplicationContext, + ) -> Result<(), Box> { + let depth_image_view = app_context.with_renderer_mut(|renderer| { + renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() + }); + + let swapchain_image_view = + app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + let square = Square::new( - &scene_context.device, - &scene_context.memory_allocator, - scene_context.swapchain_image_view.format(), - scene_context.depth_stencil_image_view.format(), + &app_context.device, + &app_context.memory_allocator, + swapchain_image_view.format(), + depth_image_view.format(), )?; let num_instances = 100; @@ -69,29 +77,35 @@ impl Scene for MainScene { }) .collect(); - let camera = Camera3D::new( - scene_context.aspect_ratio, - std::f32::consts::FRAC_PI_2, - 0.01, - 1000.0, - ); + let texture = { + let mut uploads = AutoCommandBufferBuilder::primary( + app_context.command_buffer_allocator.clone(), + app_context.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + )?; - let mut uploads = AutoCommandBufferBuilder::primary( - scene_context.command_buffer_allocator.clone(), - scene_context.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - )?; + let texture = Texture::from_file( + &app_context.device, + &app_context.memory_allocator, + &mut uploads, + "res/textures/wooden-crate.jpg", + )?; - let texture = Texture::from_file( - &scene_context.device, - &scene_context.memory_allocator, - &mut uploads, - "res/textures/wooden-crate.jpg", - )?; + let _ = uploads + .build()? + .execute(app_context.graphics_queue.clone())?; - let _ = uploads - .build()? - .execute(scene_context.graphics_queue.clone())?; + texture + }; + + let camera = app_context.with_renderer(|renderer| { + Camera3D::new( + renderer.aspect_ratio(), + std::f32::consts::FRAC_PI_2, + 0.01, + 1000.0, + ) + }); self.state = Some(MainSceneState { square, @@ -104,44 +118,46 @@ impl Scene for MainScene { Ok(()) } - fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box> { - let state = self.state.as_mut().ok_or("State not found")?; - state.camera.update( - &scene_context.input_manager, - &scene_context.timer, - state.speed, - 10.0, - scene_context.aspect_ratio, - ); + fn update(&mut self, app_context: &mut ApplicationContext) -> Result<(), Box> { + let state = self.state.as_mut().unwrap(); + app_context.with_input_manager(|input_manager| { + app_context.with_timer(|timer| { + state.camera.update( + input_manager, + timer, + state.speed, + 10.0, + app_context.get_aspect_ratio(), + ); + }); + }); - if scene_context - .input_manager - .get_virtual_input_state("mouse_left") + if app_context + .with_input_manager(|input_manager| input_manager.get_virtual_input_state("mouse_left")) > 0.0 { - let _ = scene_context + let _ = app_context .event_loop_proxy - .send_event(UserEvent::CursorVisible(scene_context.window_id, false)); - let _ = scene_context + .send_event(UserEvent::CursorVisible(app_context.window_id, false)); + let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( - scene_context.window_id, + app_context.window_id, CursorGrabMode::Locked, )); } - if scene_context - .input_manager - .get_virtual_input_state("mouse_right") - > 0.0 + if app_context.with_input_manager(|input_manager| { + input_manager.get_virtual_input_state("mouse_right") + }) > 0.0 { - let _ = scene_context + let _ = app_context .event_loop_proxy - .send_event(UserEvent::CursorVisible(scene_context.window_id, true)); - let _ = scene_context + .send_event(UserEvent::CursorVisible(app_context.window_id, true)); + let _ = app_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( - scene_context.window_id, + app_context.window_id, CursorGrabMode::None, )); } @@ -151,87 +167,112 @@ impl Scene for MainScene { fn render( &mut self, - acquire_future: Box, - scene_context: &SceneContext, - gui: &mut Gui, + before_future: Box, + app_context: &mut ApplicationContext, ) -> Result, Box> { - let state = self.state.as_ref().ok_or("State not found")?; + let state = self.state.as_ref().ok_or("State not loaded")?; let mut builder = AutoCommandBufferBuilder::primary( - scene_context.command_buffer_allocator.clone(), - scene_context.graphics_queue.queue_family_index(), + app_context.command_buffer_allocator.clone(), + app_context.graphics_queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, )?; { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: scene_context.window_size, - depth_range: 0.0..=1.0, - }; - - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.swapchain_image_view.clone(), - ) - })], - depth_attachment: Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::DontCare, - clear_value: Some([1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.depth_stencil_image_view.clone(), - ) - }), - ..Default::default() - })? - .set_viewport(0, [viewport].into_iter().collect())?; + let swapchain_image_view = + app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + let depth_image_view = app_context.with_renderer_mut(|renderer| { + renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() + }); + let config = RenderPassConfig::default(); + RenderPassManager::begin_standard_rendering( + &mut builder, + &config, + swapchain_image_view, + Some(depth_image_view), + app_context.get_window_size(), + )?; } - let camera_uniform = state - .camera - .create_buffer(&scene_context.memory_allocator)?; + // Create camera uniform using the actual camera + let camera_uniform = state.camera.create_buffer(&app_context.memory_allocator)?; let transform_uniform = - Transform::create_buffer(&scene_context.memory_allocator, &state.instances)?; + Transform::create_buffer(&app_context.memory_allocator, &state.instances)?; state .square .render( &mut builder, - &scene_context.descriptor_set_allocator, + &app_context.descriptor_set_allocator, &camera_uniform, &transform_uniform, &state.texture, ) .unwrap(); - builder.end_rendering()?; + RenderPassManager::end_rendering(&mut builder)?; let command_buffer = builder.build()?; let render_future = - acquire_future.then_execute(scene_context.graphics_queue.clone(), command_buffer)?; + before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; - gui.immediate_ui(|gui| { - let ctx = gui.context(); + let swapchain_image_view = + app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + let input_manager_status = + app_context.with_input_manager(|input_manager| format!("{:#?}", input_manager)); + let event_loop_proxy = app_context.event_loop_proxy.clone(); + let delta_time = app_context.get_delta_time(); + let window_id = app_context.window_id; + let window_size = app_context.get_window_size(); + let render_future = app_context.with_gui_mut(|gui| { + gui.immediate_ui(|gui| { + let ctx = gui.context(); + egui::TopBottomPanel::top("top_panel").show(&ctx, |ui| { + ui.horizontal(|ui| { + ui.heading("Vulkan Test - Moteur 3D"); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + if ui.button("Paramètres").clicked() { + let _ = event_loop_proxy.send_event(UserEvent::ChangeScene( + window_id, + Box::new(SettingsScene::default()), + )); + } + if ui.button("Quitter").clicked() { + let _ = event_loop_proxy.send_event(UserEvent::Exit(window_id)); + } + }); + }); + }); - egui::Window::new("Informations") - .vscroll(true) - .show(&ctx, |ui| { - ui.label(format!("Resolution: {:?}", scene_context.window_size)); + egui::SidePanel::left("side_panel").show(&ctx, |ui| { + ui.heading("Informations"); - ui.label(format!("{:#?}", scene_context.input_manager)); + ui.separator(); - ui.label(format!( - "Delta time: {:?}", - scene_context.timer.delta_time() - )); + ui.label(format!("Résolution: {:?}", window_size)); + ui.label(format!("Delta Time: {:.2}ms", delta_time * 1000.0)); + + ui.separator(); + + ui.label("Position caméra:"); + let position = state.camera.get_position(); + ui.label(format!(" X: {:.2}", position[0])); + ui.label(format!(" Y: {:.2}", position[1])); + ui.label(format!(" Z: {:.2}", position[2])); + + ui.separator(); + + ui.label("Rotation caméra:"); + let rotation = state.camera.get_rotation(); + ui.label(format!(" Yaw: {:.2}°", rotation.y.to_degrees())); + ui.label(format!(" Pitch: {:.2}°", rotation.x.to_degrees())); + + ui.separator(); + ui.label(input_manager_status); + + ui.label(format!("Delta time: {:?}", delta_time)); ui.label(format!( "Position: {:?}, Rotation: {:?}", @@ -239,13 +280,17 @@ impl Scene for MainScene { state.camera.get_rotation() )); }); - }); + }); - let render_future = - gui.draw_on_image(render_future, scene_context.swapchain_image_view.clone()); + let render_future = gui.draw_on_image(render_future, swapchain_image_view.clone()); + + render_future + }); Ok(render_future) } - fn unload(&mut self) {} + fn unload(&mut self) { + self.state = None; + } } diff --git a/src/game/scenes/mod.rs b/src/game/scenes/mod.rs index e5c4945..516fa6f 100644 --- a/src/game/scenes/mod.rs +++ b/src/game/scenes/mod.rs @@ -1,2 +1,2 @@ pub mod main_scene; -pub mod test_scene; +pub mod settings_scene; diff --git a/src/game/scenes/settings_scene.rs b/src/game/scenes/settings_scene.rs new file mode 100644 index 0000000..68e842b --- /dev/null +++ b/src/game/scenes/settings_scene.rs @@ -0,0 +1,135 @@ +use std::error::Error; + +use crate::core::app::DEPTH_IMAGE_ID; +use crate::core::app::context::ApplicationContext; +use crate::core::app::user_event::UserEvent; +use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; +use crate::core::scene::Scene; +use egui_winit_vulkano::egui; +use vulkano::{ + command_buffer::AutoCommandBufferBuilder, command_buffer::CommandBufferUsage, sync::GpuFuture, +}; + +use super::main_scene::MainScene; + +pub struct SettingsSceneState { + current_resolution: [f32; 2], + available_resolutions: Vec<(u32, u32)>, +} + +#[derive(Default)] +pub struct SettingsScene { + state: Option, +} + +impl Scene for SettingsScene { + fn loaded(&self) -> bool { + self.state.is_some() + } + + fn load(&mut self, app_context: &mut ApplicationContext) -> Result<(), Box> { + let current_resolution = app_context.get_window_size(); + let available_resolutions = app_context.get_available_resolutions(); + + self.state = Some(SettingsSceneState { + current_resolution, + available_resolutions, + }); + + Ok(()) + } + + fn update(&mut self, _app_context: &mut ApplicationContext) -> Result<(), Box> { + Ok(()) + } + + fn render( + &mut self, + before_future: Box, + app_context: &mut ApplicationContext, + ) -> Result, Box> { + let state = self.state.as_ref().ok_or("State not found")?; + + let mut builder = AutoCommandBufferBuilder::primary( + app_context.command_buffer_allocator.clone(), + app_context.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + )?; + + // Utiliser le RenderPassManager + { + let swapchain_image_view = + app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + let depth_stencil_image_view = app_context.with_renderer_mut(|renderer| { + renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() + }); + let config = RenderPassConfig::default(); + RenderPassManager::begin_standard_rendering( + &mut builder, + &config, + swapchain_image_view, + Some(depth_stencil_image_view), + app_context.get_window_size(), + )?; + } + + // Pas de géométrie dans cette scène - juste un écran de paramètres + RenderPassManager::end_rendering(&mut builder)?; + + let command_buffer = builder.build()?; + + let render_future = + before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; + + let swapchain_image_view = + app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + + let event_loop_proxy = app_context.event_loop_proxy.clone(); + let window_id = app_context.window_id; + + let render_future = app_context.with_gui_mut(|gui| { + gui.immediate_ui(|gui| { + let ctx = gui.context(); + + egui::CentralPanel::default().show(&ctx, |ui| { + ui.heading("Paramètres"); + + ui.separator(); + + ui.label(format!( + "Résolution actuelle: {:?}", + state.current_resolution + )); + + ui.separator(); + ui.label("Changer la résolution:"); + + for &(width, height) in &state.available_resolutions { + if ui.button(format!("{}x{}", width, height)).clicked() { + let _ = event_loop_proxy.send_event(UserEvent::ChangeResolution( + window_id, + width as f32, + height as f32, + )); + } + } + + ui.separator(); + + if ui.button("Retour au jeu").clicked() { + let _ = event_loop_proxy.send_event(UserEvent::ChangeScene( + window_id, + Box::new(MainScene::default()), + )); + } + }); + }); + + gui.draw_on_image(render_future, swapchain_image_view.clone()) + }); + + Ok(render_future) + } + + fn unload(&mut self) {} +} diff --git a/src/game/scenes/test_scene.rs b/src/game/scenes/test_scene.rs deleted file mode 100644 index 9ed8707..0000000 --- a/src/game/scenes/test_scene.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::error::Error; - -use crate::core::app::user_event::UserEvent; -use crate::core::scene::Scene; -use crate::core::scene::SceneContext; -use egui_winit_vulkano::{Gui, egui}; -use vulkano::command_buffer::AutoCommandBufferBuilder; -use vulkano::command_buffer::CommandBufferUsage; -use vulkano::command_buffer::RenderingAttachmentInfo; -use vulkano::command_buffer::RenderingInfo; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::render_pass::AttachmentLoadOp; -use vulkano::render_pass::AttachmentStoreOp; -use vulkano::sync::GpuFuture; - -use super::main_scene::MainScene; - -pub struct MainSceneState {} - -#[derive(Default)] -pub struct TestScene { - state: Option, -} - -impl Scene for TestScene { - fn loaded(&self) -> bool { - self.state.is_some() - } - - fn load(&mut self, scene_context: &SceneContext) -> Result<(), Box> { - self.state = Some(MainSceneState {}); - - Ok(()) - } - - fn update(&mut self, scene_context: &SceneContext) -> Result<(), Box> { - Ok(()) - } - - fn render( - &mut self, - acquire_future: Box, - scene_context: &SceneContext, - gui: &mut Gui, - ) -> Result, Box> { - let mut builder = AutoCommandBufferBuilder::primary( - scene_context.command_buffer_allocator.clone(), - scene_context.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - )?; - - { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: scene_context.window_size, - depth_range: 0.0..=1.0, - }; - - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.swapchain_image_view.clone(), - ) - })], - depth_attachment: Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::DontCare, - clear_value: Some([1.0].into()), - ..RenderingAttachmentInfo::image_view( - scene_context.depth_stencil_image_view.clone(), - ) - }), - ..Default::default() - })? - .set_viewport(0, [viewport].into_iter().collect())?; - } - - builder.end_rendering()?; - - let command_buffer = builder.build()?; - - let render_future = - acquire_future.then_execute(scene_context.graphics_queue.clone(), command_buffer)?; - - gui.immediate_ui(|gui| { - let ctx = gui.context(); - - egui::CentralPanel::default().show(&ctx, |ui| { - if ui.button("Start Game").clicked() { - let _ = scene_context - .event_loop_proxy - .send_event(UserEvent::ChangeScene( - scene_context.window_id, - Box::new(MainScene::default()), - )); - } - }); - }); - - let render_future = - gui.draw_on_image(render_future, scene_context.swapchain_image_view.clone()); - - Ok(render_future) - } - - fn unload(&mut self) {} -} From bc42892d39f0422da9ac7f63816711c209bfa904 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 30 May 2025 21:54:58 +0200 Subject: [PATCH 90/91] cleanup --- src/core/app/context.rs | 3 +++ src/core/app/mod.rs | 1 + src/core/scene/manager.rs | 19 ------------------- src/game/scenes/main_scene.rs | 16 +++++----------- src/game/scenes/settings_scene.rs | 5 +++-- 5 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/core/app/context.rs b/src/core/app/context.rs index 1c0ccb6..ce01c7c 100644 --- a/src/core/app/context.rs +++ b/src/core/app/context.rs @@ -180,6 +180,7 @@ impl ApplicationContext { f(&renderer) } + /// Méthode utilitaire pour accéder au renderer de manière thread-safe pub fn with_renderer_mut(&mut self, f: F) -> T where F: FnOnce(&mut VulkanoWindowRenderer) -> T, @@ -194,6 +195,7 @@ impl ApplicationContext { f(renderer) } + /// Méthode utilitaire pour accéder au gui de manière thread-safe pub fn with_gui(&self, f: F) -> T where F: FnOnce(&Gui) -> T, @@ -202,6 +204,7 @@ impl ApplicationContext { f(&gui) } + /// Méthode utilitaire pour accéder au gui de manière thread-safe pub fn with_gui_mut(&mut self, f: F) -> T where F: FnOnce(&mut Gui) -> T, diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs index 243420b..140dee1 100644 --- a/src/core/app/mod.rs +++ b/src/core/app/mod.rs @@ -33,6 +33,7 @@ pub struct App { input_manager: Arc>, timer: Arc>, event_loop_proxy: EventLoopProxy, + // Context d'application partagé par fenêtre - architecture unifiée app_contexts: HashMap>>, } diff --git a/src/core/scene/manager.rs b/src/core/scene/manager.rs index 9ff65ac..a12892e 100644 --- a/src/core/scene/manager.rs +++ b/src/core/scene/manager.rs @@ -42,16 +42,9 @@ impl SceneManager { } pub fn current_scene_mut(&mut self) -> Option<&mut Box> { - log::debug!( - "current_scene_mut called - index: {:?}, scenes len: {}", - self.current_scene_index, - self.scenes.len() - ); if let Some(index) = self.current_scene_index { - log::debug!("Getting scene at index {}", index); self.scenes.get_mut(index) } else { - log::debug!("No current scene index set"); None } } @@ -60,21 +53,9 @@ impl SceneManager { &mut self, app_context: &mut ApplicationContext, ) -> Result<(), Box> { - log::debug!("SceneManager::load_scene_if_not_loaded called"); - log::debug!( - "Current scene index: {:?}, scenes count: {}", - self.current_scene_index, - self.scenes.len() - ); - if let Some(scene) = self.current_scene_mut() { - log::debug!("Scene found, checking if loaded: {}", scene.loaded()); if !scene.loaded() { - log::debug!("Scene not loaded, loading..."); scene.load(app_context)?; - log::debug!("Scene loaded successfully"); - } else { - log::debug!("Scene already loaded"); } } else { log::warn!("No scene found in SceneManager!"); diff --git a/src/game/scenes/main_scene.rs b/src/game/scenes/main_scene.rs index 6461289..26e8631 100644 --- a/src/game/scenes/main_scene.rs +++ b/src/game/scenes/main_scene.rs @@ -47,7 +47,7 @@ impl Scene for MainScene { }); let swapchain_image_view = - app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let square = Square::new( &app_context.device, @@ -180,7 +180,7 @@ impl Scene for MainScene { { let swapchain_image_view = - app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let depth_image_view = app_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); @@ -219,13 +219,14 @@ impl Scene for MainScene { before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; let swapchain_image_view = - app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let input_manager_status = app_context.with_input_manager(|input_manager| format!("{:#?}", input_manager)); let event_loop_proxy = app_context.event_loop_proxy.clone(); let delta_time = app_context.get_delta_time(); let window_id = app_context.window_id; let window_size = app_context.get_window_size(); + let render_future = app_context.with_gui_mut(|gui| { gui.immediate_ui(|gui| { let ctx = gui.context(); @@ -270,15 +271,8 @@ impl Scene for MainScene { ui.label(format!(" Pitch: {:.2}°", rotation.x.to_degrees())); ui.separator(); + ui.label(input_manager_status); - - ui.label(format!("Delta time: {:?}", delta_time)); - - ui.label(format!( - "Position: {:?}, Rotation: {:?}", - state.camera.get_position(), - state.camera.get_rotation() - )); }); }); diff --git a/src/game/scenes/settings_scene.rs b/src/game/scenes/settings_scene.rs index 68e842b..4d6fc53 100644 --- a/src/game/scenes/settings_scene.rs +++ b/src/game/scenes/settings_scene.rs @@ -59,10 +59,11 @@ impl Scene for SettingsScene { // Utiliser le RenderPassManager { let swapchain_image_view = - app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let depth_stencil_image_view = app_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); + let config = RenderPassConfig::default(); RenderPassManager::begin_standard_rendering( &mut builder, @@ -82,7 +83,7 @@ impl Scene for SettingsScene { before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; let swapchain_image_view = - app_context.with_renderer_mut(|renderer| renderer.swapchain_image_view().clone()); + app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let event_loop_proxy = app_context.event_loop_proxy.clone(); let window_id = app_context.window_id; From b1458785e51efe651b283eaa23f454ca21e23543 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 30 May 2025 22:04:54 +0200 Subject: [PATCH 91/91] Change Mutex by RwLock --- src/core/app/context.rs | 33 ++++++++++++++------------- src/core/app/mod.rs | 49 ++++++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/core/app/context.rs b/src/core/app/context.rs index ce01c7c..f796563 100644 --- a/src/core/app/context.rs +++ b/src/core/app/context.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; use egui_winit_vulkano::Gui; use vulkano::{ @@ -35,19 +35,19 @@ pub struct ApplicationContext { pub window_id: WindowId, // Données mutables partagées avec Arc> - pub vulkano_windows: Arc>, - pub input_manager: Arc>, - pub timer: Arc>, - pub gui: Arc>, + pub vulkano_windows: Arc>, + pub input_manager: Arc>, + pub timer: Arc>, + pub gui: Arc>, } impl ApplicationContext { pub fn new( vulkan_context: Arc, - vulkano_windows: Arc>, - input_manager: Arc>, - timer: Arc>, - gui: Arc>, + vulkano_windows: Arc>, + input_manager: Arc>, + timer: Arc>, + gui: Arc>, event_loop_proxy: EventLoopProxy, window_id: WindowId, ) -> Self { @@ -172,7 +172,7 @@ impl ApplicationContext { { let vulkano_windows = self .vulkano_windows - .lock() + .read() .expect("Failed to lock vulkano_windows"); let renderer = vulkano_windows .get_renderer(self.window_id) @@ -187,7 +187,7 @@ impl ApplicationContext { { let mut vulkano_windows = self .vulkano_windows - .lock() + .write() .expect("Failed to lock vulkano_windows"); let renderer = vulkano_windows .get_renderer_mut(self.window_id) @@ -200,7 +200,7 @@ impl ApplicationContext { where F: FnOnce(&Gui) -> T, { - let gui = self.gui.lock().unwrap(); + let gui = self.gui.read().expect("Failed to lock gui"); f(&gui) } @@ -209,7 +209,7 @@ impl ApplicationContext { where F: FnOnce(&mut Gui) -> T, { - let mut gui = self.gui.lock().unwrap(); + let mut gui = self.gui.write().expect("Failed to lock gui"); f(&mut gui) } @@ -218,7 +218,10 @@ impl ApplicationContext { where F: FnOnce(&InputManager) -> T, { - let input_manager = self.input_manager.lock().unwrap(); + let input_manager = self + .input_manager + .read() + .expect("Failed to lock input_manager"); f(&input_manager) } @@ -227,7 +230,7 @@ impl ApplicationContext { where F: FnOnce(&Timer) -> T, { - let timer = self.timer.lock().unwrap(); + let timer = self.timer.read().expect("Failed to lock timer"); f(&timer) } } diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs index 140dee1..d17e84d 100644 --- a/src/core/app/mod.rs +++ b/src/core/app/mod.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; use super::render::vulkan_context::VulkanContext; use crate::core::input::InputManager; @@ -27,15 +27,15 @@ pub const DEPTH_IMAGE_ID: usize = 0; pub struct App { vulkan_context: Arc, - vulkano_windows: Arc>, - gui: HashMap>>, + vulkano_windows: Arc>, + gui: HashMap>>, scene_manager: HashMap, - input_manager: Arc>, - timer: Arc>, + input_manager: Arc>, + timer: Arc>, event_loop_proxy: EventLoopProxy, // Context d'application partagé par fenêtre - architecture unifiée - app_contexts: HashMap>>, + app_contexts: HashMap>>, } impl App { @@ -46,11 +46,11 @@ impl App { ) -> Self { Self { vulkan_context: Arc::new(VulkanContext::new(vulkano_context)), - vulkano_windows: Arc::new(Mutex::new(VulkanoWindows::default())), + vulkano_windows: Arc::new(RwLock::new(VulkanoWindows::default())), gui: HashMap::new(), - input_manager: Arc::new(Mutex::new(input_manager)), + input_manager: Arc::new(RwLock::new(input_manager)), scene_manager: HashMap::new(), - timer: Arc::new(Mutex::new(Timer::new())), + timer: Arc::new(RwLock::new(Timer::new())), event_loop_proxy, app_contexts: HashMap::new(), } @@ -59,7 +59,10 @@ impl App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + let mut vulkano_windows = self + .vulkano_windows + .write() + .expect("Failed to lock vulkano_windows"); let window_id = vulkano_windows.create_window( event_loop, &self.vulkan_context.vulkano_context(), @@ -95,7 +98,7 @@ impl ApplicationHandler for App { }, ) }; - self.gui.insert(window_id, Arc::new(Mutex::new(gui))); + self.gui.insert(window_id, Arc::new(RwLock::new(gui))); let mut scene_manager = SceneManager::new(); scene_manager.load_scene(Box::new(MainScene::default())); @@ -109,16 +112,16 @@ impl ApplicationHandler for App { _device_id: winit::event::DeviceId, event: winit::event::DeviceEvent, ) { - let mut input_manager = self.input_manager.lock().unwrap(); + let mut input_manager = self.input_manager.write().unwrap(); input_manager.process_device_event(&event); } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { { let gui = self.gui.get_mut(&id).unwrap(); - let mut gui = gui.lock().unwrap(); + let mut gui = gui.write().expect("Failed to lock gui"); if !gui.update(&event) { - let mut input_manager = self.input_manager.lock().unwrap(); + let mut input_manager = self.input_manager.write().unwrap(); input_manager.process_window_event(&event); } } @@ -129,19 +132,19 @@ impl ApplicationHandler for App { event_loop.exit(); } WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. } => { - let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + let mut vulkano_windows = self.vulkano_windows.write().unwrap(); vulkano_windows.get_renderer_mut(id).unwrap().resize(); } WindowEvent::RedrawRequested => { { let mut input_manager = self .input_manager - .lock() + .write() .expect("Failed to lock input manager"); input_manager.update(); } { - let mut timer = self.timer.lock().expect("Failed to lock timer"); + let mut timer = self.timer.write().expect("Failed to lock timer"); timer.update(); } @@ -150,7 +153,7 @@ impl ApplicationHandler for App { .app_contexts .entry(id) .or_insert_with(|| { - Arc::new(Mutex::new(ApplicationContext::new( + Arc::new(RwLock::new(ApplicationContext::new( self.vulkan_context.clone(), self.vulkano_windows.clone(), self.input_manager.clone(), @@ -166,7 +169,7 @@ impl ApplicationHandler for App { // Utiliser le contexte partagé pour les scènes { - let mut context = app_context.lock().unwrap(); + let mut context = app_context.write().unwrap(); scene_manager .load_scene_if_not_loaded(&mut context) @@ -193,7 +196,7 @@ impl ApplicationHandler for App { } fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let vulkano_windows = self.vulkano_windows.read().unwrap(); let window = vulkano_windows.get_primary_window().unwrap(); window.request_redraw(); } @@ -201,14 +204,14 @@ impl ApplicationHandler for App { fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { match event { UserEvent::CursorGrabMode(window_id, grab) => { - let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let vulkano_windows = self.vulkano_windows.read().unwrap(); let window = vulkano_windows.get_window(window_id).unwrap(); if let Err(e) = window.set_cursor_grab(grab) { log::error!("Failed to set cursor grab: {}", e); } } UserEvent::CursorVisible(window_id, visible) => { - let vulkano_windows = self.vulkano_windows.lock().unwrap(); + let vulkano_windows = self.vulkano_windows.read().unwrap(); let window = vulkano_windows.get_window(window_id).unwrap(); window.set_cursor_visible(visible); } @@ -218,7 +221,7 @@ impl ApplicationHandler for App { } } UserEvent::ChangeResolution(window_id, width, height) => { - let mut vulkano_windows = self.vulkano_windows.lock().unwrap(); + let mut vulkano_windows = self.vulkano_windows.write().unwrap(); let window = vulkano_windows.get_window(window_id).unwrap(); let _ = window.request_inner_size(winit::dpi::LogicalSize::new(width, height)); let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap();