#[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use std::{ mem::ManuallyDrop, ptr::read, collections::VecDeque, }; use gfx_hal::{ window::{Extent2D, AcquireError, PresentationSurface, SurfaceCapabilities, SwapchainConfig}, pso::Rect as GfxRect, format::Format, }; mod frame; use frame::Frame; use super::gpu::Gpu; //--SwapSystem implementation----------------------------------------------------------------------- #[derive(Debug)] pub struct SwapSystem { surface: ManuallyDrop, format: Format, extent: Extent2D, pub render_area: GfxRect, //TODO may not be needed (duplicate of extent) pub render_pass: ManuallyDrop, //TODO move to Pipeline ? frames: VecDeque>, frame_nb: usize, } impl SwapSystem where B: gfx_hal::Backend, { pub fn drop(&mut self, gpu: &mut Gpu) -> B::Surface { use gfx_hal::device::Device; debug!("Dropping SwapSystem..."); for mut frame in self.frames.drain(..) { frame.drop(gpu); } unsafe { gpu.device().destroy_render_pass( ManuallyDrop::into_inner(read(&mut self.render_pass))); //render_area extent and format can be dropped automatically self.surface.unconfigure_swapchain(gpu.device()); trace!("SwapSystem dropped !"); ManuallyDrop::take(&mut self.surface) } } pub fn new(gpu: &mut Gpu, mut surface: B::Surface) -> Result, &'static str> { debug!("Creating SwapSystem..."); trace!("Obtaining surface capabilities..."); let capabilities = { use gfx_hal::window::Surface; surface.capabilities(&gpu.adapter().physical_device) }; debug!("Surface capabitlities : {:#?}", capabilities); trace!("Selecting image format..."); let format = { use gfx_hal::{ format::ChannelType, window::Surface, }; match surface.supported_formats(&gpu.adapter().physical_device) { None => Format::Rgba8Srgb, Some(formats) => formats .iter() .find(|f| f.base_format().1 == ChannelType::Srgb) .cloned() .ok_or("Could no find suitable format")? }}; trace!("Getting surface's render area size..."); let extent = capabilities.current_extent .unwrap_or(*capabilities.extents.end()) .clone(); let render_area = GfxRect{x: 0, y: 0, w: extent.width as i16, h: extent.height as i16}; trace!("Generating Swapchain configuration..."); let (swapchain_config, frame_nb) = generate_swapchain_config(capabilities, &format, &extent)?; debug!("Swapchain configuration : {:#?}", swapchain_config); trace!("Configuring Swapchain..."); let _ = unsafe { surface .configure_swapchain(gpu.device(), swapchain_config) .map_err(|_| "Failed to create swapchain and backbuffer")? }; trace!("Creating RenderPass..."); let render_pass = { use gfx_hal::{ pass::{Attachment, AttachmentOps, AttachmentLoadOp, AttachmentStoreOp, SubpassDesc}, image::Layout, device::Device, }; let color_attachment = Attachment { format : Some(format), samples : 1, ops : AttachmentOps { load : AttachmentLoadOp::Clear, store : AttachmentStoreOp::Store, }, stencil_ops : AttachmentOps::DONT_CARE, layouts : (Layout::Undefined..Layout::Present), }; let subpass = SubpassDesc { colors : &[(0, Layout::ColorAttachmentOptimal)], depth_stencil : None, inputs : &[], resolves : &[], preserves : &[], }; unsafe { gpu.device().create_render_pass(&[color_attachment], &[subpass], &[]) .map_err(|_| "Could not create render pass")? } }; warn!("Generating hard-coded number of frames !"); let mut frames = VecDeque::with_capacity(frame_nb); for _ in 0..frame_nb { let frame = Frame::new(gpu) .map_err(|_| "Could not create frame")?; frames.push_back(frame); }; let frame_nb = frames.len(); trace!("Created SwapSystem !"); Ok( SwapSystem { surface: ManuallyDrop::new(surface), format, extent, render_area, render_pass: ManuallyDrop::new(render_pass), frames, frame_nb, }) } pub fn recreate(&mut self, gpu: &mut Gpu) -> Result<(), &'static str> { trace!("Obtaining surface capabilities..."); let capabilities = { use gfx_hal::window::Surface; self.surface.capabilities(&gpu.adapter().physical_device) }; debug!("Surface capabitlities : {:#?}", capabilities); trace!("Getting surface's render area size..."); self.extent = capabilities.current_extent .unwrap_or(*capabilities.extents.end()) .clone(); self.render_area = GfxRect{x: 0, y: 0, w: self.extent.width as i16, h: self.extent.height as i16}; trace!("Generating Swapchain configuration..."); let (swapchain_config, _frame_nb) = //TODO check frame nb change generate_swapchain_config(capabilities, &self.format, &self.extent)?; debug!("Swapchain configuration : {:#?}", swapchain_config); trace!("Configuring Swapchain..."); let _ = unsafe { self.surface .configure_swapchain(gpu.device(), swapchain_config) .map_err(|_| "Failed to create swapchain and backbuffer")? }; Ok(()) } pub fn acquire_frame(&mut self, gpu: &Gpu) -> Result, AcquireError> { trace!("Acquiring Frame..."); let image = unsafe { match self.surface.acquire_image(core::u64::MAX) { Ok((image, suboptimal)) => { match suboptimal { Some(_) => return Err(AcquireError::OutOfDate), None => image, }}, Err(err) => return Err(err), }}; //println!("Frame nb : {}", self.frames.len()); //frames number diminish sometimes at resize... let mut frame = self.frames.pop_back().unwrap(); trace!("Creating Framebuffer..."); let framebuffer = unsafe { use gfx_hal::device::Device; use std::borrow::Borrow; gpu.device() .create_framebuffer(&self.render_pass, vec![image.borrow()], self.extent.clone().to_extent()) .unwrap() //TODO improve that }; frame.link_swapchain_image(gpu, image, framebuffer); Ok(frame) } pub fn present_frame(&mut self, mut frame: Frame, gpu: &mut Gpu) -> Result<(), &'static str> { use gfx_hal::queue::CommandQueue; let image = frame .unlink_swapchain_image() .unwrap(); //TODO improve that trace!("Presenting Frame..."); let _ = unsafe { gpu.queue_mut() .present(&mut self.surface, image, Some(&frame.signal_semaphores[0])) .map_err(|_| "Failed to present into the swapchain")?; }; self.frames.push_front(frame); Ok(()) } } fn generate_swapchain_config(capabilities: SurfaceCapabilities, format: &Format, extent: &Extent2D) -> Result<(SwapchainConfig, usize), &'static str> { trace!("Generating Swapchain configuration..."); let present_mode = { use gfx_hal::window::PresentMode; [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE] .iter() .cloned() .find(|pm| capabilities.present_modes.contains(*pm)) .ok_or("No PresentMode values specified!")? }; let composite_alpha_mode = { use gfx_hal::window::CompositeAlphaMode; [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] .iter() .cloned() .find(|ca| capabilities.composite_alpha_modes.contains(*ca)) .ok_or("No CompositeAlpha values specified!")? }; let image_count = { use std::cmp::{min, max}; use gfx_hal::window::PresentMode; let count = match present_mode { PresentMode::MAILBOX => 3, _ => 2, //TODO to be checked }; max(capabilities.image_count.start().clone(), min(capabilities.image_count.end().clone(), count)) }; warn!("Hard-coding number of layers per image !"); let image_layers = 1; let image_usage = { use gfx_hal::image::Usage; if capabilities.usage.contains(Usage::COLOR_ATTACHMENT) { Usage::COLOR_ATTACHMENT } else { Err("The Surface isn't capable of supporting color!")? }}; Ok (( SwapchainConfig { present_mode, composite_alpha_mode, format: format.clone(), extent: extent.clone(), image_count: image_count.clone(), image_layers, image_usage, }, image_count as usize )) }