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