From 7401a9b5f3ae0375f53a45b89a663cb66222af25 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 26 May 2025 19:55:34 +0200 Subject: [PATCH] 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.