First scene refactor working
Some checks failed
Build legacy Nix package on Ubuntu / build (push) Failing after 8m13s

This commit is contained in:
Florian RICHER 2025-05-26 19:55:34 +02:00
parent 5b74eef561
commit 7401a9b5f3
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
6 changed files with 251 additions and 110 deletions

View file

@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use crate::core::input::InputState; use crate::core::input::InputState;
use crate::core::scene::SceneManager; use crate::core::scene::SceneManager;
@ -13,17 +14,19 @@ use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp};
use vulkano::swapchain::PresentMode; use vulkano::swapchain::PresentMode;
use vulkano::sync::GpuFuture; use vulkano::sync::GpuFuture;
use vulkano_util::context::VulkanoContext; use vulkano_util::context::VulkanoContext;
use vulkano_util::renderer::VulkanoWindowRenderer;
use vulkano_util::window::{VulkanoWindows, WindowDescriptor}; use vulkano_util::window::{VulkanoWindows, WindowDescriptor};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::{ElementState, WindowEvent}; use winit::event::{ElementState, WindowEvent};
use winit::event_loop::ActiveEventLoop; use winit::event_loop::ActiveEventLoop;
use winit::window::WindowId; use winit::window::WindowId;
use super::render_context::RenderContext;
use super::vulkan_context::VulkanContext; use super::vulkan_context::VulkanContext;
pub struct App { pub struct App {
vulkan_context: VulkanContext, vulkan_context: Arc<VulkanContext>,
vulkano_windows: VulkanoWindows, vulkano_windows: Arc<VulkanoWindows>,
gui: HashMap<WindowId, Gui>, gui: HashMap<WindowId, Gui>,
clear_color: [f32; 3], clear_color: [f32; 3],
input_state: InputState, input_state: InputState,
@ -31,11 +34,29 @@ pub struct App {
timer: Timer, 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<VulkanoContext> for App { impl From<VulkanoContext> for App {
fn from(vulkano_context: VulkanoContext) -> Self { fn from(vulkano_context: VulkanoContext) -> Self {
Self { Self {
vulkan_context: VulkanContext::new(vulkano_context), vulkan_context: Arc::new(VulkanContext::new(vulkano_context)),
vulkano_windows: VulkanoWindows::default(), vulkano_windows: Arc::new(VulkanoWindows::default()),
gui: HashMap::new(), gui: HashMap::new(),
clear_color: [0.0, 0.0, 0.0], clear_color: [0.0, 0.0, 0.0],
input_state: InputState::default(), input_state: InputState::default(),
@ -47,41 +68,47 @@ impl From<VulkanoContext> for App {
impl ApplicationHandler for App { impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_id = self.vulkano_windows.create_window( if let Some(vulkano_windows) = Arc::get_mut(&mut self.vulkano_windows) {
event_loop, let window_id = vulkano_windows.create_window(
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(
event_loop, event_loop,
renderer.surface(), self.vulkan_context.vulkano_context(),
renderer.graphics_queue(), &WindowDescriptor {
renderer.swapchain_format(), title: "Rust ASH Test".to_string(),
GuiConfig { width: 800.0,
is_overlay: true, height: 600.0,
present_mode: PresentMode::Fifo,
..Default::default() ..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 self.scene_manager
.load_scene(Box::new(MainScene::default())); .load_scene(Box::new(MainScene::default()));
} }
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 renderer = Arc::get_mut(&mut self.vulkano_windows)
.unwrap()
.get_renderer_mut(id)
.unwrap();
let gui = self.gui.get_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) { if !gui.update(&event) {
self.input_state.process_event(&event); self.input_state.process_event(&event);
@ -100,6 +127,11 @@ impl ApplicationHandler for App {
} }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
self.input_state.update(); 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(); let acquire_future = renderer.acquire(None, |_| {}).unwrap();
@ -137,10 +169,8 @@ impl ApplicationHandler for App {
.unwrap(); .unwrap();
} }
if let Some(scene) = self.scene.as_ref() { if let Some(scene) = self.scene_manager.current_scene() {
scene scene.render(&render_context, &mut builder);
.render(&self.vulkan_context, renderer, &mut builder)
.unwrap();
} }
builder.end_rendering().unwrap(); builder.end_rendering().unwrap();
@ -174,6 +204,8 @@ impl ApplicationHandler for App {
self.input_state.get_mouse_state().delta self.input_state.get_mouse_state().delta
)); ));
ui.label(format!("Delta time: {:?}", self.timer.delta_time()));
for (key, state) in self for (key, state) in self
.input_state .input_state
.key_states .key_states

View file

@ -1,4 +1,5 @@
pub mod app; pub mod app;
pub mod pipelines; pub mod pipelines;
pub mod render_context;
pub mod vertex; pub mod vertex;
pub mod vulkan_context; pub mod vulkan_context;

View file

@ -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<Instance>,
pub(super) device: Arc<Device>,
pub(super) graphics_queue: Arc<Queue>,
pub(super) compute_queue: Arc<Queue>,
pub(super) transfer_queue: Option<Arc<Queue>>,
pub(super) memory_allocator: Arc<StandardMemoryAllocator>,
pub(super) command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
pub(super) descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
pub(super) window_size: [f32; 2],
pub(super) aspect_ratio: f32,
pub(super) swapchain_format: Format,
}
impl RenderContext {
pub fn instance(&self) -> &Arc<Instance> {
&self.instance
}
pub fn device(&self) -> &Arc<Device> {
&self.device
}
pub fn graphics_queue(&self) -> &Arc<Queue> {
&self.graphics_queue
}
pub fn compute_queue(&self) -> &Arc<Queue> {
&self.compute_queue
}
pub fn transfer_queue(&self) -> Option<&Arc<Queue>> {
self.transfer_queue.as_ref()
}
pub fn memory_allocator(&self) -> &Arc<StandardMemoryAllocator> {
&self.memory_allocator
}
pub fn command_buffer_allocator(&self) -> &Arc<StandardCommandBufferAllocator> {
&self.command_buffer_allocator
}
pub fn descriptor_set_allocator(&self) -> &Arc<StandardDescriptorSetAllocator> {
&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
}
}

View file

@ -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 { pub trait Scene {
fn load(&mut self, app: &mut App); fn loaded(&self) -> bool;
fn update(&mut self, app: &mut App); fn load(&mut self, render_context: &RenderContext);
fn render(&self); fn update(&mut self, render_context: &RenderContext, input_state: &InputState, timer: &Timer);
fn render(
&self,
render_context: &RenderContext,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
);
fn unload(&mut self); 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<dyn Scene>) { pub fn load_scene(&mut self, scene: Box<dyn Scene>) {
if let Some(current_scene) = self.current_scene.as_mut() { if let Some(current_scene) = self.current_scene.as_mut() {
current_scene.unload(); current_scene.unload();
} }
self.current_scene = Some(scene); self.current_scene = Some(scene);
} }
pub fn current_scene(&self) -> Option<&Box<dyn Scene>> {
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<dyn Scene>> {
if let Some(current_scene) = self.current_scene.as_mut() {
if current_scene.loaded() {
return Some(current_scene);
}
}
None
}
} }

View file

@ -24,7 +24,11 @@ impl Timer {
self.last_time = self.current_time; self.last_time = self.current_time;
} }
pub fn get_delta_time(&self) -> f32 { pub fn delta_time(&self) -> f32 {
self.delta_time self.delta_time
} }
pub fn start_time(&self) -> std::time::Instant {
self.start_time
}
} }

View file

@ -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::core::scene::Scene;
use crate::render::pipelines::triangle_pipeline::shaders::vs; use crate::core::timer::Timer;
use glam::{Mat3, Mat4, Vec3}; use glam::{Mat3, Mat4, Vec3};
use std::error::Error; use std::error::Error;
use std::sync::Arc; use std::sync::Arc;
@ -10,11 +13,13 @@ use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
use vulkano_util::renderer::VulkanoWindowRenderer; 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::core::render::pipelines::triangle_pipeline::create_triangle_pipeline;
use crate::render::vertex::Vertex2D; use crate::core::render::vertex::Vertex2D;
use super::vulkan_context::VulkanContext; use crate::core::render::vulkan_context::VulkanContext;
const VERTICES: [Vertex2D; 12] = [ const VERTICES: [Vertex2D; 12] = [
// Triangle en haut à gauche // Triangle en haut à gauche
@ -74,8 +79,8 @@ const VERTICES: [Vertex2D; 12] = [
pub struct MainSceneState { pub struct MainSceneState {
pipeline: Arc<GraphicsPipeline>, pipeline: Arc<GraphicsPipeline>,
vertex_buffer: Subbuffer<[Vertex2D]>, vertex_buffer: Subbuffer<[Vertex2D]>,
uniform_buffer: Subbuffer<vs::MVPData>,
rotation_start: Instant, rotation: f32,
} }
#[derive(Default)] #[derive(Default)]
@ -84,103 +89,101 @@ pub struct MainScene {
} }
impl Scene for MainScene { impl Scene for MainScene {
fn load(&mut self, app: &mut App) { fn loaded(&self) -> bool {
let pipeline = create_triangle_pipeline( self.state.is_some()
app.vulkan_context.vulkano_context().device(), }
app.vulkano_windows.swapchain_format(),
)?; fn load(&mut self, render_context: &RenderContext) {
let vertex_buffer = Vertex2D::create_buffer( let pipeline =
Vec::from_iter(VERTICES), create_triangle_pipeline(render_context.device(), render_context.swapchain_format())
vulkano_context.vulkano_context().memory_allocator(), .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 { self.state = Some(MainSceneState {
pipeline, pipeline,
vertex_buffer, vertex_buffer,
rotation_start: Instant::now(), uniform_buffer,
rotation: 0.0,
}) })
} }
fn update(&mut self, app: &mut App) { fn update(&mut self, render_context: &RenderContext, input_state: &InputState, timer: &Timer) {
todo!() 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) { fn render(
todo!()
}
fn unload(&mut self) {
todo!()
}
}
impl MainScene {
pub fn load(
vulkano_context: &VulkanContext,
vulkano_window_renderer: &VulkanoWindowRenderer,
) -> Result<Self, Box<dyn Error>> {
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(
&self, &self,
vulkan_context: &VulkanContext, render_context: &RenderContext,
vulkano_window_renderer: &VulkanoWindowRenderer,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>, builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
) -> Result<(), Box<dyn Error>> { ) {
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 instance_count = vertex_count / 3;
let uniform_buffer = self.get_uniform_buffer( let layout = &self.state.as_ref().unwrap().pipeline.layout().set_layouts()[0];
vulkan_context.vulkano_context().memory_allocator(),
vulkano_window_renderer.aspect_ratio(),
);
let layout = &self.pipeline.layout().set_layouts()[0];
let descriptor_set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
vulkan_context.descriptor_set_allocator().clone(), render_context.descriptor_set_allocator().clone(),
layout.clone(), layout.clone(),
[WriteDescriptorSet::buffer(0, uniform_buffer)], [WriteDescriptorSet::buffer(
0,
self.state.as_ref().unwrap().uniform_buffer.clone(),
)],
[], [],
) )
.unwrap(); .unwrap();
unsafe { unsafe {
builder builder
.bind_pipeline_graphics(self.pipeline.clone())? .bind_pipeline_graphics(self.state.as_ref().unwrap().pipeline.clone())
.unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
self.pipeline.layout().clone(), self.state.as_ref().unwrap().pipeline.layout().clone(),
0, 0,
descriptor_set, descriptor_set,
)? )
.bind_vertex_buffers(0, self.vertex_buffer.clone())? .unwrap()
.draw(vertex_count, instance_count, 0, 0)?; .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( fn get_uniform_buffer(
&self, rotation: f32,
memory_allocator: &Arc<StandardMemoryAllocator>, memory_allocator: &Arc<StandardMemoryAllocator>,
aspect_ratio: f32, aspect_ratio: f32,
) -> Subbuffer<vs::MVPData> { ) -> Subbuffer<vs::MVPData> {
let elapsed = self.rotation_start.elapsed(); let rotation = Mat3::from_rotation_y(rotation);
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 // 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. // instead the origin is at the upper left in Vulkan, so we reverse the Y axis.