diff options
-rw-r--r-- | examples/render-quad/src/main.rs | 12 | ||||
-rw-r--r-- | stockton-render/src/draw/camera.rs | 70 | ||||
-rw-r--r-- | stockton-render/src/draw/context.rs | 249 |
3 files changed, 221 insertions, 110 deletions
diff --git a/examples/render-quad/src/main.rs b/examples/render-quad/src/main.rs index 130bc5e..f04fca7 100644 --- a/examples/render-quad/src/main.rs +++ b/examples/render-quad/src/main.rs @@ -107,19 +107,23 @@ fn main() { *flow = ControlFlow::Poll; match event { - // TODO: Handle resize Event::WindowEvent { - event: WindowEvent::CloseRequested, + event: WindowEvent::CloseRequested, .. } => { *flow = ControlFlow::Exit - }, + } Event::MainEventsCleared => { window.request_redraw() }, Event::RedrawRequested(_) => { - ctx.draw_vertices().unwrap(); + if let Err(err) = ctx.draw_vertices() { + unsafe {ctx.handle_surface_change().unwrap()}; + + // If it fails twice, then panic + ctx.draw_vertices().unwrap(); + } } _ => () } diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs index 2cba57e..6733cd7 100644 --- a/stockton-render/src/draw/camera.rs +++ b/stockton-render/src/draw/camera.rs @@ -27,15 +27,15 @@ use crate::types::*; use super::buffer::{StagedBuffer, ModifiableBuffer}; use stockton_types::{Vector3, Matrix4}; -/// Holds settings related to the projection of world space to screen space -/// Also holds maths for generating important matrices -pub struct Camera<'a> { +pub struct CameraSettings { + /// Position of the camera position: Vector3, + + /// A point the camera is looking directly at looking_at: Vector3, - up: Vector3, - /// Aspect ratio as a fraction - aspect_ratio: f32, + /// The up direction + up: Vector3, /// FOV in radians fov: f32, @@ -45,6 +45,16 @@ pub struct Camera<'a> { /// Far clipping plane (world units) far: f32, +} + +/// Holds settings related to the projection of world space to screen space +/// Also holds maths for generating important matrices +pub struct WorkingCamera<'a> { + /// Settings for the camera + settings: CameraSettings, + + /// Aspect ratio as a fraction + aspect_ratio: f32, /// Layout of the descriptor set to pass to the shader pub descriptor_set_layout: ManuallyDrop<DescriptorSetLayout>, @@ -61,12 +71,25 @@ pub struct Camera<'a> { is_dirty: bool } -impl<'a> Camera<'a> { +impl<'a> WorkingCamera<'a> { + pub fn defaults(aspect_ratio: f32, device: &mut Device, adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool) -> Result<WorkingCamera<'a>, error::CreationError> { + WorkingCamera::with_settings(CameraSettings { + position: Vector3::new(-0.5, 1.5, -1.0), + looking_at: Vector3::new(0.5, 0.5, 0.5), + up: Vector3::new(0.0, 1.0, 0.0), + fov: f32::to_radians(90.0), + near: 0.1, + far: 100.0, + }, aspect_ratio, device, adapter, command_queue, command_pool) + } + /// Return a camera with default settings // TODO - pub fn defaults(aspect_ratio: f32, device: &mut Device, adapter: &Adapter, + pub fn with_settings(settings: CameraSettings, aspect_ratio: f32, device: &mut Device, adapter: &Adapter, command_queue: &mut CommandQueue, - command_pool: &mut CommandPool) -> Result<Camera<'a>, error::CreationError> { + command_pool: &mut CommandPool) -> Result<WorkingCamera<'a>, error::CreationError> { let descriptor_type = { use hal::pso::{DescriptorType, BufferDescriptorType, BufferDescriptorFormat}; @@ -137,15 +160,9 @@ impl<'a> Camera<'a> { )); } - Ok(Camera { - position: Vector3::new(-0.5, 1.5, -1.0), - looking_at: Vector3::new(0.5, 0.5, 0.5), - up: Vector3::new(0.0, 1.0, 0.0), - + Ok(WorkingCamera { aspect_ratio, - fov: f32::to_radians(90.0), - near: 0.1, - far: 100.0, + settings, descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout), buffer: ManuallyDrop::new(buffer), @@ -161,21 +178,21 @@ impl<'a> Camera<'a> { pub fn vp_matrix(&self) -> Matrix4 { // Converts world space to camera space let view_matrix = look_at_lh( - &self.position, - &self.looking_at, - &self.up + &self.settings.position, + &self.settings.looking_at, + &self.settings.up ); // Converts camera space to screen space let projection_matrix = { let mut temp = perspective_lh_zo( self.aspect_ratio, - self.fov, - self.near, - self.far + self.settings.fov, + self.settings.near, + self.settings.far ); - // Vulkan's co-ord system is different from openGLs + // Vulkan's co-ord system is different from OpenGLs temp[(1, 1)] *= -1.0; temp @@ -185,6 +202,11 @@ impl<'a> Camera<'a> { projection_matrix * view_matrix } + pub fn update_aspect_ratio(&mut self, new: f32) { + self.aspect_ratio = new; + self.is_dirty = true; + } + pub fn commit<'b>(&'b mut self, device: &Device, command_queue: &mut CommandQueue, command_pool: &mut CommandPool) -> &'b DescriptorSet { diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index 061d22c..d9d426d 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -35,7 +35,7 @@ use stockton_types::{Vector2, Vector3}; use crate::types::*; use crate::error; -use super::camera::Camera; +use super::camera::WorkingCamera; use super::texture::TextureStore; use super::buffer::{StagedBuffer, ModifiableBuffer}; @@ -111,7 +111,7 @@ pub struct RenderingContext<'a> { pub vert_buffer: ManuallyDrop<StagedBuffer<'a, UVPoint>>, pub index_buffer: ManuallyDrop<StagedBuffer<'a, (u16, u16, u16)>>, - camera: ManuallyDrop<Camera<'a>> + camera: ManuallyDrop<WorkingCamera<'a>> } impl<'a> RenderingContext<'a> { @@ -152,84 +152,7 @@ impl<'a> RenderingContext<'a> { }; // Swapchain - let (format, viewport, extent, swapchain, backbuffer) = { - use hal::{ - window::{PresentMode, CompositeAlphaMode}, - format::{Format, ChannelType}, - image::Usage, - pso::Viewport - }; - - // Figure out what the surface supports - let caps = surface.capabilities(&adapter.physical_device); - let formats = surface.supported_formats(&adapter.physical_device); - - // Find which settings we'll actually use based on preset preferences - let format = formats.map_or(Format::Rgba8Srgb, |formats| { - formats.iter() - .find(|format| format.base_format().1 == ChannelType::Srgb) - .map(|format| *format) - .unwrap_or(formats[0]) - }); - - let present_mode = { - [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE] - .iter() - .cloned() - .find(|pm| caps.present_modes.contains(*pm)) - .ok_or(error::CreationError::BadSurface)? - }; - let composite_alpha = { - [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] - .iter() - .cloned() - .find(|ca| caps.composite_alpha_modes.contains(*ca)) - .ok_or(error::CreationError::BadSurface)? - }; - - // Figure out properties for our swapchain - let extent = caps.extents.end(); // Size - - // Number of frames to pre-render - let image_count = if present_mode == PresentMode::MAILBOX { - ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3)) - } else { - ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2)) - }; - - let image_layers = 1; // Don't support 3D - let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { - Usage::COLOR_ATTACHMENT - } else { - Err(error::CreationError::BadSurface)? - }; - - // Swap config - let swap_config = SwapchainConfig { - present_mode, - composite_alpha_mode: composite_alpha, - format, - extent: *extent, - image_count, - image_layers, - image_usage, - }; - - // Viewport - let extent = extent.to_extent(); - let viewport = Viewport { - rect: extent.rect(), - depth: 0.0..1.0 - }; - - // Swapchain - let (swapchain, backbuffer) = unsafe { - device.create_swapchain(&mut surface, swap_config, None) - .map_err(|e| error::CreationError::SwapchainError (e))? - }; - - (format, viewport, extent, swapchain, backbuffer) - }; + let (format, viewport, extent, swapchain, backbuffer) = RenderingContext::create_swapchain(&mut surface, &mut device, &adapter, None)?; // Renderpass let renderpass = { @@ -338,7 +261,7 @@ impl<'a> RenderingContext<'a> { // Camera // TODO: Settings let ratio = extent.width as f32 / extent.height as f32; - let camera = Camera::defaults(ratio, &mut device, &adapter, &mut queue_group.queues[0], &mut cmd_pool)?; + let camera = WorkingCamera::defaults(ratio, &mut device, &adapter, &mut queue_group.queues[0], &mut cmd_pool)?; let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); descriptor_set_layouts.push(camera.descriptor_set_layout.deref()); @@ -382,6 +305,168 @@ impl<'a> RenderingContext<'a> { }) } + /// If this function fails the whole context is probably dead + /// The context must not be used while this is being called + pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> { + use core::ptr::read; + + self.device.wait_idle().unwrap(); + + // Swapchain itself + let old_swapchain = unsafe { + ManuallyDrop::into_inner(read(&self.swapchain)) + }; + + let (format, viewport, extent, swapchain, backbuffer) = RenderingContext::create_swapchain(&mut self.surface, &mut self.device, &self.adapter, Some(old_swapchain))?; + + self.swapchain = ManuallyDrop::new(swapchain); + self.viewport = viewport; + + // Camera settings (aspect ratio) + self.camera.update_aspect_ratio(extent.width as f32 / extent.height as f32); + + // Graphics pipeline + unsafe { + self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); + + self.device + .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); + } + + let (pipeline_layout, pipeline) = { + let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); + descriptor_set_layouts.push(self.camera.descriptor_set_layout.deref()); + descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref()); + + let subpass = hal::pass::Subpass { + index: 0, + main_pass: &(*self.renderpass) + }; + + Self::create_pipeline(&mut self.device, extent, &subpass, descriptor_set_layouts)? + }; + + self.pipeline_layout = ManuallyDrop::new(pipeline_layout); + self.pipeline = ManuallyDrop::new(pipeline); + + // Imageviews, framebuffers + // Destroy old objects + for framebuffer in self.framebuffers.drain(..) { + self.device.destroy_framebuffer(framebuffer); + } + for image_view in self.imageviews.drain(..) { + self.device.destroy_image_view(image_view); + } + // Make new ones + for i in 0..backbuffer.len() { + unsafe { + use hal::image::ViewKind; + use hal::format::Swizzle; + + self.imageviews.push(self.device.create_image_view( + &backbuffer[i], + ViewKind::D2, + format, + Swizzle::NO, + COLOR_RANGE.clone(), + ).map_err(|e| error::CreationError::ImageViewError (e))?); + + self.framebuffers.push(self.device.create_framebuffer( + &self.renderpass, + Some(&self.imageviews[i]), + extent + ).map_err(|_| error::CreationError::OutOfMemoryError)?); + } + } + + Ok(()) + } + + fn create_swapchain(surface: &mut Surface, device: &mut Device, adapter: &Adapter, old_swapchain: Option<Swapchain>) -> Result<( + hal::format::Format, + hal::pso::Viewport, + hal::image::Extent, + Swapchain, + Vec<Image> + ), error::CreationError> { + use hal::{ + window::{PresentMode, CompositeAlphaMode}, + format::{Format, ChannelType}, + image::Usage, + pso::Viewport + }; + + // Figure out what the surface supports + let caps = surface.capabilities(&adapter.physical_device); + let formats = surface.supported_formats(&adapter.physical_device); + + // Find which settings we'll actually use based on preset preferences + let format = formats.map_or(Format::Rgba8Srgb, |formats| { + formats.iter() + .find(|format| format.base_format().1 == ChannelType::Srgb) + .map(|format| *format) + .unwrap_or(formats[0]) + }); + + let present_mode = { + [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE] + .iter() + .cloned() + .find(|pm| caps.present_modes.contains(*pm)) + .ok_or(error::CreationError::BadSurface)? + }; + let composite_alpha = { + [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] + .iter() + .cloned() + .find(|ca| caps.composite_alpha_modes.contains(*ca)) + .ok_or(error::CreationError::BadSurface)? + }; + + // Figure out properties for our swapchain + let extent = caps.extents.end(); // Size + + // Number of frames to pre-render + let image_count = if present_mode == PresentMode::MAILBOX { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3)) + } else { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2)) + }; + + let image_layers = 1; // Don't support 3D + let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { + Usage::COLOR_ATTACHMENT + } else { + Err(error::CreationError::BadSurface)? + }; + + // Swap config + let swap_config = SwapchainConfig { + present_mode, + composite_alpha_mode: composite_alpha, + format, + extent: *extent, + image_count, + image_layers, + image_usage, + }; + + // Viewport + let extent = extent.to_extent(); + let viewport = Viewport { + rect: extent.rect(), + depth: 0.0..1.0 + }; + + // Swapchain + let (swapchain, backbuffer) = unsafe { + device.create_swapchain(surface, swap_config, old_swapchain) + .map_err(|e| error::CreationError::SwapchainError (e))? + }; + + Ok((format, viewport, extent, swapchain, backbuffer)) + } + /// Load the given image into the texturestore, returning the index or an error. pub fn add_texture(&mut self, image: RgbaImage) -> Result<usize, &'static str> { self.texture_store.add_texture(image, @@ -392,7 +477,7 @@ impl<'a> RenderingContext<'a> { } #[allow(clippy::type_complexity)] - pub fn create_pipeline<T>(device: &mut Device, extent: hal::image::Extent, subpass: &hal::pass::Subpass<back::Backend>, set_layouts: T) -> Result< + fn create_pipeline<T>(device: &mut Device, extent: hal::image::Extent, subpass: &hal::pass::Subpass<back::Backend>, set_layouts: T) -> Result< ( PipelineLayout, GraphicsPipeline, |