From 8ce620a74b77eae10abe20453623f2ecb453a8b4 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 11 Jun 2025 16:05:35 +0200 Subject: [PATCH] Integration of ECS pattern: Iteration 1 --- Cargo.lock | 396 +++++++++++++++++- Cargo.toml | 3 + src/core/app/context.rs | 34 +- src/core/app/mod.rs | 29 +- src/core/render/primitives/mod.rs | 1 + src/core/render/primitives/mvp.rs | 2 +- src/core/render/primitives/vulkan_resource.rs | 79 ++++ src/core/render/resources/texture/loader.rs | 30 +- src/core/scene/manager.rs | 101 ++++- src/core/scene/mod.rs | 16 +- src/game/assets/pipelines/simple.rs | 5 +- src/game/scenes/main_scene.rs | 119 ++++-- src/game/scenes/settings_scene.rs | 47 ++- 13 files changed, 727 insertions(+), 135 deletions(-) create mode 100644 src/core/render/primitives/vulkan_resource.rs diff --git a/Cargo.lock b/Cargo.lock index c35674a..befb3ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,11 +149,48 @@ 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" @@ -184,6 +221,147 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "bevy_ecs" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2bf6521aae57a0ec3487c4bfb59e36c4a378e834b626a4bea6a885af2fdfe7" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38748d6f3339175c582d751f410fb60a93baf2286c3deb7efebb0878dce7f413" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_macro_utils" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052eeebcb8e7e072beea5031b227d9a290f8a7fbbb947573ab6ec81df0fb94be" +dependencies = [ + "parking_lot", + "proc-macro2", + "quote", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_platform" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7573dc824a1b08b4c93fdbe421c53e1e8188e9ca1dd74a414455fe571facb47" +dependencies = [ + "cfg-if", + "critical-section", + "foldhash", + "hashbrown", + "portable-atomic", + "portable-atomic-util", + "serde", + "spin", +] + +[[package]] +name = "bevy_ptr" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7370d0e46b60e071917711d0860721f5347bc958bf325975ae6913a5dfcf01" + +[[package]] +name = "bevy_reflect" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeb91a63a1a4df00aa58da8cc4ddbd4b9f16ab8bb647c5553eb156ce36fa8c2" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ddadc55fe16b45faaa54ab2f9cb00548013c74812e8b018aa172387103cce6" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_tasks" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b674242641cab680688fc3b850243b351c1af49d4f3417a576debd6cca8dcf5" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f7a8905a125d2017e8561beefb7f2f5e67e93ff6324f072ad87c5fd6ec3b99" +dependencies = [ + "bevy_platform", + "thread_local", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -201,6 +379,9 @@ name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] [[package]] name = "bitstream-io" @@ -249,6 +430,12 @@ dependencies = [ "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "byteorder-lite" version = "0.1.0" @@ -367,6 +554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", + "portable-atomic", ] [[package]] @@ -428,6 +616,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -474,6 +668,27 @@ 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" @@ -501,6 +716,12 @@ dependencies = [ "syn", ] +[[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" @@ -516,6 +737,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" @@ -626,6 +853,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.12" @@ -657,6 +894,12 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fdeflate" version = "0.3.7" @@ -666,6 +909,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.1" @@ -718,6 +967,31 @@ dependencies = [ "percent-encoding", ] +[[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 = "generator" version = "0.8.5" @@ -775,6 +1049,15 @@ dependencies = [ "weezl", ] +[[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" @@ -792,11 +1075,35 @@ 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" @@ -1256,6 +1563,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 = "noop_proc_macro" version = "0.3.0" @@ -1667,6 +1980,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" @@ -1762,6 +2081,21 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[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 = "potential_utf" version = "0.1.2" @@ -2085,8 +2419,9 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", + "bevy_ecs", "egui_winit_vulkano", - "glam", + "glam 0.30.3", "image", "rand 0.9.1", "thiserror 2.0.12", @@ -2318,6 +2653,15 @@ 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" @@ -2609,6 +2953,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" @@ -2621,6 +2971,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 = "url" version = "2.5.4" @@ -2638,6 +2994,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "v_frame" version = "0.3.8" @@ -2655,6 +3023,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[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-compare" version = "0.2.0" @@ -2849,7 +3228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", - "downcast-rs", + "downcast-rs 1.2.1", "rustix", "scoped-tls", "smallvec", @@ -2994,6 +3373,19 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" +[[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" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index c33debe..6883189 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,6 @@ rand = "0.9" # OBJ loader tobj = "4.0" + +# ECS +bevy_ecs = "0.16.1" # Latest version diff --git a/src/core/app/context.rs b/src/core/app/context.rs index 80b602e..a12bb3c 100644 --- a/src/core/app/context.rs +++ b/src/core/app/context.rs @@ -5,13 +5,6 @@ use std::{ }; 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, monitor::MonitorHandle, window::WindowId}; @@ -19,19 +12,10 @@ use crate::core::{input::InputManager, render::vulkan_context::VulkanContext, ti use super::user_event::UserEvent; -/// Contexte d'application unifié avec Arc> pour la mutabilité partagée -#[derive(Clone)] +/// Contexte d'application unifié pour les fonctions liées à la fenêtre pub struct WindowContext { // 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, @@ -52,19 +36,9 @@ impl WindowContext { 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(), + vulkan_context, event_loop_proxy, window_id, @@ -76,6 +50,10 @@ impl WindowContext { } } + pub fn vulkan_context(&self) -> &VulkanContext { + &self.vulkan_context + } + /// Extrait les résolutions d'un moniteur donné fn extract_resolutions_from_monitor(monitor: MonitorHandle) -> Vec<(u32, u32)> { let video_modes: Vec<_> = monitor.video_modes().collect(); diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs index 81fb0a3..8f0ace8 100644 --- a/src/core/app/mod.rs +++ b/src/core/app/mod.rs @@ -99,11 +99,7 @@ impl ApplicationHandler for App { }; self.gui.insert(window_id, Rc::new(RefCell::new(gui))); - let mut scene_manager = SceneManager::new(); - scene_manager.load_scene(Box::new(MainScene::default())); - - self.scene_manager.insert(window_id, scene_manager); - + // Create the WindowContext with simplified arguments let app_context = Rc::new(RefCell::new(WindowContext::new( self.vulkan_context.clone(), self.vulkano_windows.clone(), @@ -113,7 +109,16 @@ impl ApplicationHandler for App { self.event_loop_proxy.clone(), window_id, ))); - self.app_contexts.insert(window_id, app_context); + self.app_contexts.insert(window_id, app_context.clone()); + + // Now use the created context to load the scene + let mut scene_manager = SceneManager::new(); + { + let context = app_context.borrow(); + scene_manager.load_scene(Box::new(MainScene::default()), &context); + } + + self.scene_manager.insert(window_id, scene_manager); } fn device_event( @@ -181,7 +186,7 @@ impl ApplicationHandler for App { if let Some(scene) = scene_manager.current_scene_mut() { { let _update_span = tracing::debug_span!("scene_update").entered(); - scene.update(&mut context).unwrap(); + scene.0.update(&mut scene.1, &context).unwrap(); } let acquire_future = { @@ -193,7 +198,10 @@ impl ApplicationHandler for App { let acquire_future = { let _render_span = tracing::debug_span!("scene_render").entered(); - scene.render(acquire_future, &mut context).unwrap() + scene + .0 + .render(acquire_future, &mut scene.1, &mut context) + .unwrap() }; { @@ -235,7 +243,10 @@ impl ApplicationHandler for App { } UserEvent::ChangeScene(window_id, scene) => { if let Some(scene_manager) = self.scene_manager.get_mut(&window_id) { - scene_manager.load_scene(scene); + if let Some(app_context) = self.app_contexts.get(&window_id) { + let context = app_context.borrow(); + scene_manager.load_scene(scene, &context); + } } } UserEvent::ChangeResolution(window_id, width, height) => { diff --git a/src/core/render/primitives/mod.rs b/src/core/render/primitives/mod.rs index 6f795e4..c7211a6 100644 --- a/src/core/render/primitives/mod.rs +++ b/src/core/render/primitives/mod.rs @@ -1,6 +1,7 @@ mod buffer; mod command; mod descriptor_set; +pub mod vulkan_resource; pub mod camera; pub mod mvp; diff --git a/src/core/render/primitives/mvp.rs b/src/core/render/primitives/mvp.rs index 6cf1362..814a6e6 100644 --- a/src/core/render/primitives/mvp.rs +++ b/src/core/render/primitives/mvp.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use vulkano::buffer::{ AllocateBufferError, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, }; -use vulkano::descriptor_set::allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator}; +use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; use vulkano::descriptor_set::layout::{ DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType, }; diff --git a/src/core/render/primitives/vulkan_resource.rs b/src/core/render/primitives/vulkan_resource.rs new file mode 100644 index 0000000..db59468 --- /dev/null +++ b/src/core/render/primitives/vulkan_resource.rs @@ -0,0 +1,79 @@ +use bevy_ecs::prelude::Resource; +use bevy_ecs::world::World; +use std::sync::Arc; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; + +/// Vulkan Instance resource +#[derive(Resource)] +pub struct VulkanInstance(pub Arc); + +/// Vulkan Device resource +#[derive(Resource)] +pub struct VulkanDevice(pub Arc); + +/// Vulkan Graphics Queue resource +#[derive(Resource)] +pub struct VulkanGraphicsQueue(pub Arc); + +/// Vulkan Compute Queue resource +#[derive(Resource)] +pub struct VulkanComputeQueue(pub Arc); + +/// Vulkan Transfer Queue resource +#[derive(Resource)] +pub struct VulkanTransferQueue(pub Option>); + +/// Vulkan Memory Allocator resource +#[derive(Resource)] +pub struct VulkanMemoryAllocator(pub Arc); + +/// Vulkan Command Buffer Allocator resource +#[derive(Resource)] +pub struct VulkanCommandBufferAllocator(pub Arc); + +/// Vulkan Descriptor Set Allocator resource +#[derive(Resource)] +pub struct VulkanDescriptorSetAllocator(pub Arc); + +/// Helper functions to access vulkan resources from the ECS world +impl VulkanDevice { + pub fn get_from_world(world: &World) -> &Arc { + &world.resource::().0 + } +} + +impl VulkanMemoryAllocator { + pub fn get_from_world(world: &World) -> &Arc { + &world.resource::().0 + } +} + +impl VulkanCommandBufferAllocator { + pub fn get_from_world(world: &World) -> &Arc { + &world.resource::().0 + } +} + +impl VulkanDescriptorSetAllocator { + pub fn get_from_world(world: &World) -> &Arc { + &world.resource::().0 + } +} + +impl VulkanGraphicsQueue { + pub fn get_from_world(world: &World) -> &Arc { + &world.resource::().0 + } +} + +impl VulkanTransferQueue { + pub fn get_from_world(world: &World) -> Option<&Arc> { + world.resource::().0.as_ref() + } +} diff --git a/src/core/render/resources/texture/loader.rs b/src/core/render/resources/texture/loader.rs index d440291..38a365c 100644 --- a/src/core/render/resources/texture/loader.rs +++ b/src/core/render/resources/texture/loader.rs @@ -11,7 +11,11 @@ use vulkano::{ memory::allocator::StandardMemoryAllocator, }; -use crate::core::app::context::WindowContext; +use crate::core::render::primitives::vulkan_resource::{ + VulkanCommandBufferAllocator, VulkanDevice, VulkanGraphicsQueue, VulkanMemoryAllocator, + VulkanTransferQueue, +}; +use bevy_ecs::world::World; use super::Texture; @@ -36,21 +40,19 @@ pub struct TextureLoader { } impl TextureLoader { - pub fn new(app_context: &WindowContext) -> Self { + pub fn new(world: &World) -> Self { Self { loaded_textures: HashMap::new(), pending_textures: HashMap::new(), - device: app_context.device.clone(), - command_buffer_allocator: app_context.command_buffer_allocator.clone(), - memory_allocator: app_context.memory_allocator.clone(), - queue: Self::select_best_suitable_queue(app_context), + device: VulkanDevice::get_from_world(world).clone(), + command_buffer_allocator: VulkanCommandBufferAllocator::get_from_world(world).clone(), + memory_allocator: VulkanMemoryAllocator::get_from_world(world).clone(), + queue: Self::select_best_suitable_queue(world), } } - fn select_best_suitable_queue(app_context: &WindowContext) -> Arc { - app_context - .transfer_queue - .as_ref() + fn select_best_suitable_queue(world: &World) -> Arc { + VulkanTransferQueue::get_from_world(world) .map(|queue| { tracing::trace!( "Select transfer queue for texture loading with family index: {:?}", @@ -58,14 +60,14 @@ impl TextureLoader { ); queue.clone() }) - .or_else(|| { + .unwrap_or_else(|| { + let graphics_queue = VulkanGraphicsQueue::get_from_world(world); tracing::trace!( "Select graphics queue for texture loading with family index: {:?}", - app_context.graphics_queue.queue_family_index() + graphics_queue.queue_family_index() ); - Some(app_context.graphics_queue.clone()) + graphics_queue.clone() }) - .unwrap() } pub fn add_texture(&mut self, name: String, load_info: TextureLoadInfo) { diff --git a/src/core/scene/manager.rs b/src/core/scene/manager.rs index cf07ebc..e8660e8 100644 --- a/src/core/scene/manager.rs +++ b/src/core/scene/manager.rs @@ -1,12 +1,20 @@ +use std::cell::RefCell; use std::error::Error; +use std::rc::Rc; use crate::core::app::context::WindowContext; +use crate::core::render::primitives::vulkan_resource::{ + VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanDescriptorSetAllocator, VulkanDevice, + VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator, VulkanTransferQueue, +}; +use bevy_ecs::world::World; use super::Scene; pub struct SceneManager { - scenes: Vec>, + scenes: Vec<(Box, World)>, current_scene_index: Option, + window_context: Option>>, } impl SceneManager { @@ -14,26 +22,95 @@ impl SceneManager { Self { scenes: Vec::new(), current_scene_index: None, + window_context: None, } } - pub fn load_scene(&mut self, scene: Box) { - self.scenes.push(scene); + pub fn set_window_context(&mut self, window_context: Rc>) { + self.window_context = Some(window_context); + } + + fn create_world_with_resources(window_context: &WindowContext) -> World { + let mut world = World::new(); + + // Add Vulkan resources + world.insert_resource(VulkanInstance( + window_context + .vulkan_context() + .vulkano_context() + .instance() + .clone(), + )); + world.insert_resource(VulkanDevice( + window_context + .vulkan_context() + .vulkano_context() + .device() + .clone(), + )); + world.insert_resource(VulkanGraphicsQueue( + window_context + .vulkan_context() + .vulkano_context() + .graphics_queue() + .clone(), + )); + world.insert_resource(VulkanComputeQueue( + window_context + .vulkan_context() + .vulkano_context() + .compute_queue() + .clone(), + )); + world.insert_resource(VulkanTransferQueue( + window_context + .vulkan_context() + .vulkano_context() + .transfer_queue() + .cloned(), + )); + world.insert_resource(VulkanMemoryAllocator( + window_context + .vulkan_context() + .vulkano_context() + .memory_allocator() + .clone(), + )); + world.insert_resource(VulkanCommandBufferAllocator( + window_context + .vulkan_context() + .command_buffer_allocator() + .clone(), + )); + world.insert_resource(VulkanDescriptorSetAllocator( + window_context + .vulkan_context() + .descriptor_set_allocator() + .clone(), + )); + + world + } + + pub fn load_scene(&mut self, scene: Box, window_context: &WindowContext) { + let world = Self::create_world_with_resources(window_context); + self.scenes.push((scene, world)); self.current_scene_index = Some(self.scenes.len() - 1); } - pub fn replace_current_scene(&mut self, scene: Box) { + pub fn replace_current_scene(&mut self, scene: Box, window_context: &WindowContext) { if let Some(index) = self.current_scene_index { if index < self.scenes.len() { - self.scenes[index].unload(); - self.scenes[index] = scene; + self.scenes[index].0.unload(); + let world = Self::create_world_with_resources(window_context); + self.scenes[index] = (scene, world); } } else { - self.load_scene(scene); + self.load_scene(scene, window_context); } } - pub fn current_scene(&self) -> Option<&Box> { + pub fn current_scene(&self) -> Option<&(Box, World)> { if let Some(index) = self.current_scene_index { self.scenes.get(index) } else { @@ -41,7 +118,7 @@ impl SceneManager { } } - pub fn current_scene_mut(&mut self) -> Option<&mut Box> { + pub fn current_scene_mut(&mut self) -> Option<&mut (Box, World)> { if let Some(index) = self.current_scene_index { self.scenes.get_mut(index) } else { @@ -51,11 +128,11 @@ impl SceneManager { pub fn load_scene_if_not_loaded( &mut self, - app_context: &mut WindowContext, + window_context: &mut WindowContext, ) -> Result<(), Box> { - if let Some(scene) = self.current_scene_mut() { + if let Some((scene, world)) = self.current_scene_mut() { if !scene.loaded() { - scene.load(app_context)?; + scene.load(world, window_context)?; } } else { tracing::warn!("No scene found in SceneManager!"); diff --git a/src/core/scene/mod.rs b/src/core/scene/mod.rs index 5dd8a57..c12f00e 100644 --- a/src/core/scene/mod.rs +++ b/src/core/scene/mod.rs @@ -1,5 +1,6 @@ use std::error::Error; +use bevy_ecs::world::World; use vulkano::sync::GpuFuture; use crate::core::app::context::WindowContext; @@ -8,12 +9,21 @@ pub mod manager; pub trait Scene { fn loaded(&self) -> bool; - fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box>; - fn update(&mut self, app_context: &mut WindowContext) -> Result<(), Box>; + fn load( + &mut self, + world: &mut World, + window_context: &mut WindowContext, + ) -> Result<(), Box>; + fn update( + &mut self, + world: &mut World, + window_context: &WindowContext, + ) -> Result<(), Box>; fn render( &mut self, acquire_future: Box, - app_context: &mut WindowContext, + world: &mut World, + window_context: &mut WindowContext, ) -> Result, Box>; fn unload(&mut self); } diff --git a/src/game/assets/pipelines/simple.rs b/src/game/assets/pipelines/simple.rs index 3f3a4db..6a114c8 100644 --- a/src/game/assets/pipelines/simple.rs +++ b/src/game/assets/pipelines/simple.rs @@ -1,7 +1,4 @@ -use std::{ - error::Error, - sync::{Arc, RwLock}, -}; +use std::{error::Error, sync::Arc}; use vulkano::{ command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}, diff --git a/src/game/scenes/main_scene.rs b/src/game/scenes/main_scene.rs index bb29c35..33532f8 100644 --- a/src/game/scenes/main_scene.rs +++ b/src/game/scenes/main_scene.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::error::Error; use std::sync::Arc; @@ -8,6 +7,10 @@ use crate::core::app::context::WindowContext; 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::primitives::vulkan_resource::{ + VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, + VulkanMemoryAllocator, +}; use crate::core::render::primitives::{AsDescriptorSet, AsRecordable}; use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::render::resources::meshes::{ObjMesh, SquareMesh}; @@ -15,6 +18,7 @@ use crate::core::render::resources::pipeline::PipelineLoader; use crate::core::render::resources::texture::{TextureLoadInfo, TextureLoader, TextureSourceKind}; use crate::core::scene::Scene; use crate::game::assets::pipelines::simple::SimplePipeline; +use bevy_ecs::world::World; use egui_winit_vulkano::egui; use glam::EulerRot; use glam::Quat; @@ -48,23 +52,27 @@ impl Scene for MainScene { self.state.is_some() } - fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box> { - let depth_image_view = app_context.with_renderer_mut(|renderer| { + fn load( + &mut self, + world: &mut World, + window_context: &mut WindowContext, + ) -> Result<(), Box> { + let depth_image_view = window_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); let swapchain_image_view = - app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); + window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); let mut pipeline_loader = PipelineLoader::new( - app_context.device.clone(), + VulkanDevice::get_from_world(world).clone(), swapchain_image_view.format(), depth_image_view.format(), ); pipeline_loader.register::()?; pipeline_loader.load_pending_pipelines()?; - let mut texture_loader = TextureLoader::new(app_context); + let mut texture_loader = TextureLoader::new(world); texture_loader.add_texture( "wooden-crate".to_string(), TextureLoadInfo { @@ -93,10 +101,13 @@ impl Scene for MainScene { ); texture_loader.load_pending_textures()?; - let square = SquareMesh::new(&app_context.memory_allocator)?; + let square = SquareMesh::new(VulkanMemoryAllocator::get_from_world(world))?; let obj = { - let obj = ObjMesh::new(&app_context.memory_allocator, "res/objects/cube.obj")?; + let obj = ObjMesh::new( + VulkanMemoryAllocator::get_from_world(world), + "res/objects/cube.obj", + )?; obj.into_iter().next().unwrap() }; @@ -139,7 +150,7 @@ impl Scene for MainScene { }) .collect(); - let camera = app_context.with_renderer(|renderer| { + let camera = window_context.with_renderer(|renderer| { Camera3D::new( renderer.aspect_ratio(), std::f32::consts::FRAC_PI_2, @@ -162,21 +173,26 @@ impl Scene for MainScene { Ok(()) } - fn update(&mut self, app_context: &mut WindowContext) -> Result<(), Box> { + fn update( + &mut self, + _world: &mut World, + window_context: &WindowContext, + ) -> Result<(), Box> { let state = self.state.as_mut().unwrap(); - app_context.with_input_manager(|input_manager| { - app_context.with_timer(|timer| { + + window_context.with_input_manager(|input_manager| { + window_context.with_timer(|timer| { state.camera.update( input_manager, timer, state.speed, 10.0, - app_context.get_aspect_ratio(), + window_context.get_aspect_ratio(), ); }); }); - let delta_time = app_context.get_delta_time(); + let delta_time = window_context.get_delta_time(); for (i, instance) in state.square_instances.iter_mut().enumerate() { let rotation_speed = (i % 10) as f32; let rotation_delta = Quat::from_rotation_y(rotation_speed * delta_time); @@ -191,32 +207,32 @@ impl Scene for MainScene { instance.rotate(rotation_delta); } - if app_context + if window_context .with_input_manager(|input_manager| input_manager.get_virtual_input_state("mouse_left")) > 0.0 { - let _ = app_context + let _ = window_context .event_loop_proxy - .send_event(UserEvent::CursorVisible(app_context.window_id, false)); - let _ = app_context + .send_event(UserEvent::CursorVisible(window_context.window_id, false)); + let _ = window_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( - app_context.window_id, + window_context.window_id, CursorGrabMode::Locked, )); } - if app_context.with_input_manager(|input_manager| { + if window_context.with_input_manager(|input_manager| { input_manager.get_virtual_input_state("mouse_right") }) > 0.0 { - let _ = app_context + let _ = window_context .event_loop_proxy - .send_event(UserEvent::CursorVisible(app_context.window_id, true)); - let _ = app_context + .send_event(UserEvent::CursorVisible(window_context.window_id, true)); + let _ = window_context .event_loop_proxy .send_event(UserEvent::CursorGrabMode( - app_context.window_id, + window_context.window_id, CursorGrabMode::None, )); } @@ -227,20 +243,21 @@ impl Scene for MainScene { fn render( &mut self, before_future: Box, - app_context: &mut WindowContext, + world: &mut World, + window_context: &mut WindowContext, ) -> Result, Box> { let state = self.state.as_mut().ok_or("State not loaded")?; let mut builder = AutoCommandBufferBuilder::primary( - app_context.command_buffer_allocator.clone(), - app_context.graphics_queue.queue_family_index(), + VulkanCommandBufferAllocator::get_from_world(world).clone(), + VulkanGraphicsQueue::get_from_world(world).queue_family_index(), CommandBufferUsage::OneTimeSubmit, )?; { let swapchain_image_view = - app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); - let depth_image_view = app_context.with_renderer_mut(|renderer| { + window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); + let depth_image_view = window_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); let config = RenderPassConfig::default(); @@ -249,23 +266,31 @@ impl Scene for MainScene { &config, swapchain_image_view, Some(depth_image_view), - app_context.get_window_size(), + window_context.get_window_size(), )?; } // Create camera uniform using the actual camera - let camera_uniform = Arc::new(state.camera.create_buffer(&app_context.memory_allocator)?); - let square_transform_uniform = - Transform::create_buffer(&app_context.memory_allocator, &state.square_instances)?; - let obj_transform_uniform = - Transform::create_buffer(&app_context.memory_allocator, &state.obj_instances)?; + let camera_uniform = Arc::new( + state + .camera + .create_buffer(VulkanMemoryAllocator::get_from_world(world))?, + ); + let square_transform_uniform = Transform::create_buffer( + VulkanMemoryAllocator::get_from_world(world), + &state.square_instances, + )?; + let obj_transform_uniform = Transform::create_buffer( + VulkanMemoryAllocator::get_from_world(world), + &state.obj_instances, + )?; state .pipeline_loader .with_pipeline::(|pipeline| { SimplePipeline::record_commands( &mut builder, - &app_context.descriptor_set_allocator, + &VulkanDescriptorSetAllocator::get_from_world(world), pipeline, &state.square, &square_transform_uniform, @@ -281,7 +306,7 @@ impl Scene for MainScene { SimplePipeline::record_commands( &mut builder, - &app_context.descriptor_set_allocator, + &VulkanDescriptorSetAllocator::get_from_world(world), pipeline, &state.obj, &obj_transform_uniform, @@ -302,19 +327,21 @@ impl Scene for MainScene { let command_buffer = builder.build()?; - let render_future = - before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; + let render_future = before_future.then_execute( + VulkanGraphicsQueue::get_from_world(world).clone(), + command_buffer, + )?; let swapchain_image_view = - app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); + window_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(); + window_context.with_input_manager(|input_manager| format!("{:#?}", input_manager)); + let event_loop_proxy = window_context.event_loop_proxy.clone(); + let delta_time = window_context.get_delta_time(); + let window_id = window_context.window_id; + let window_size = window_context.get_window_size(); - let render_future = app_context.with_gui_mut(|gui| { + let render_future = window_context.with_gui_mut(|gui| { gui.immediate_ui(|gui| { let ctx = gui.context(); egui::TopBottomPanel::top("top_panel").show(&ctx, |ui| { diff --git a/src/game/scenes/settings_scene.rs b/src/game/scenes/settings_scene.rs index 466652a..f2e1eff 100644 --- a/src/game/scenes/settings_scene.rs +++ b/src/game/scenes/settings_scene.rs @@ -3,8 +3,12 @@ use std::error::Error; use crate::core::app::DEPTH_IMAGE_ID; use crate::core::app::context::WindowContext; use crate::core::app::user_event::UserEvent; +use crate::core::render::primitives::vulkan_resource::{ + VulkanCommandBufferAllocator, VulkanGraphicsQueue, +}; use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager}; use crate::core::scene::Scene; +use bevy_ecs::world::World; use egui_winit_vulkano::egui; use vulkano::{ command_buffer::AutoCommandBufferBuilder, command_buffer::CommandBufferUsage, sync::GpuFuture, @@ -27,9 +31,13 @@ impl Scene for SettingsScene { self.state.is_some() } - fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box> { - let current_resolution = app_context.get_window_size(); - let available_resolutions = app_context.get_available_resolutions(); + fn load( + &mut self, + _world: &mut World, + window_context: &mut WindowContext, + ) -> Result<(), Box> { + let current_resolution = window_context.get_window_size(); + let available_resolutions = window_context.get_available_resolutions(); self.state = Some(SettingsSceneState { current_resolution, @@ -39,28 +47,33 @@ impl Scene for SettingsScene { Ok(()) } - fn update(&mut self, _app_context: &mut WindowContext) -> Result<(), Box> { + fn update( + &mut self, + _world: &mut World, + _window_context: &WindowContext, + ) -> Result<(), Box> { Ok(()) } fn render( &mut self, before_future: Box, - app_context: &mut WindowContext, + world: &mut World, + window_context: &mut WindowContext, ) -> 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(), + VulkanCommandBufferAllocator::get_from_world(world).clone(), + VulkanGraphicsQueue::get_from_world(world).queue_family_index(), CommandBufferUsage::OneTimeSubmit, )?; // Utiliser le RenderPassManager { let swapchain_image_view = - app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); - let depth_stencil_image_view = app_context.with_renderer_mut(|renderer| { + window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); + let depth_stencil_image_view = window_context.with_renderer_mut(|renderer| { renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone() }); @@ -70,7 +83,7 @@ impl Scene for SettingsScene { &config, swapchain_image_view, Some(depth_stencil_image_view), - app_context.get_window_size(), + window_context.get_window_size(), )?; } @@ -79,16 +92,18 @@ impl Scene for SettingsScene { let command_buffer = builder.build()?; - let render_future = - before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?; + let render_future = before_future.then_execute( + VulkanGraphicsQueue::get_from_world(world).clone(), + command_buffer, + )?; let swapchain_image_view = - app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone()); + window_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; + let event_loop_proxy = window_context.event_loop_proxy.clone(); + let window_id = window_context.window_id; - let render_future = app_context.with_gui_mut(|gui| { + let render_future = window_context.with_gui_mut(|gui| { gui.immediate_ui(|gui| { let ctx = gui.context();