#[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use std::{ mem::ManuallyDrop, iter, }; use core::ptr::read; use gfx_hal::{ Instance, device::Device, queue::QueueGroup, Backend, pool::CommandPool, pso::Rect, }; use gfx_backend_vulkan as vk_back; use winit::window::Window; pub mod render; #[derive(Debug)] pub struct HalState { //items need to communicate with the GPU instance: ManuallyDrop<::Instance>, surface: ManuallyDrop<::Surface>, adapter: ManuallyDrop>, device: vk_back::Device, queue_group: ManuallyDrop>, render_pass: ManuallyDrop<::RenderPass>, swapchain: ManuallyDrop<::Swapchain>, extent: gfx_hal::window::Extent2D, format: gfx_hal::format::Format, render_area: Rect, //items needed to render the images sems_image_available: Vec<::Semaphore>, sems_render_finished: Vec<::Semaphore>, fences: Vec<::Fence>, image_views: Vec<::ImageView>, framebuffers: Vec<::Framebuffer>, command_pool: ManuallyDrop<::CommandPool>, command_buffers: Vec<::CommandBuffer>, //items needed to keep track of the images image_count: usize, current_image: usize, } impl core::ops::Drop for HalState { //---------------------------------------------------------------------------------------------- fn drop(&mut self) { let _ = self.device.wait_idle(); //destroy all underlying ressources debug!("Destroying HAL ressources"); unsafe { self.command_pool.free(self.command_buffers.drain(..)); self.device.destroy_command_pool( ManuallyDrop::into_inner(read(&mut self.command_pool))); for buffer in self.framebuffers.drain(..) { self.device.destroy_framebuffer(buffer); } for view in self.image_views.drain(..) { self.device.destroy_image_view(view); } for fence in self.fences.drain(..) { self.device.destroy_fence(fence); } for sem in self.sems_image_available.drain(..) { self.device.destroy_semaphore(sem); } for sem in self.sems_render_finished.drain(..) { self.device.destroy_semaphore(sem); } self.device.destroy_swapchain(ManuallyDrop::into_inner(read(&mut self.swapchain))); self.device.destroy_render_pass(ManuallyDrop::into_inner(read(&mut self.render_pass))); ManuallyDrop::drop(&mut self.queue_group); ManuallyDrop::drop(&mut self.adapter); self.instance.destroy_surface(ManuallyDrop::into_inner(read(&mut self.surface))); ManuallyDrop::drop(&mut self.instance); } info!("HAL ressources destroyed"); } } impl HalState { //---------------------------------------------------------------------------------------------- pub fn recreate_swapchain(&mut self) -> Result<(), &'static str> { use gfx_hal::window::{ SwapchainConfig, Surface, }; debug!("Recreating swapchain"); //destroying previous swapchain unsafe { for buffer in self.framebuffers.drain(..) { self.device.destroy_framebuffer(buffer); } for view in self.image_views.drain(..) { self.device.destroy_image_view(view); } self.device.destroy_swapchain(ManuallyDrop::into_inner(read(&mut self.swapchain))); } let capabilities = self.surface.capabilities(&self.adapter.physical_device); info!("{:#?}", capabilities); //creating new swapchain let swapchain_config = SwapchainConfig::from_caps(&capabilities, self.format, self.extent); info!("{:?}", swapchain_config); let (swapchain, backbuffer) = unsafe { self.device .create_swapchain(&mut self.surface, swapchain_config, None) .map_err(|_| "Failed to create swapchain and backbuffer")? }; let image_views : Vec<_> = { use gfx_hal::{ image::{ViewKind, SubresourceRange}, format::{Swizzle, Aspects}, }; backbuffer .iter() .map(|img| unsafe { self.device .create_image_view( &img, ViewKind::D2, self.format, Swizzle::NO, SubresourceRange { aspects : Aspects::COLOR, levels : 0..1, layers : 0..1, }, ) .map_err(|_| "Could not create ImageViews") }) .collect::, &str>>()? }; let framebuffers: Vec<_> = { image_views .iter() .map(|image_view| unsafe { self.device .create_framebuffer(&self.render_pass, iter::once(image_view), self.extent.to_extent()) .map_err(|_| "Could not create FrameBuffer") }, ) .collect::, &str>>()? }; self.swapchain = ManuallyDrop::new(swapchain); self.image_views = image_views; self.framebuffers = framebuffers; Ok(()) } //---------------------------------------------------------------------------------------------- pub fn new(window: &Window) -> Result { use gfx_hal::adapter::Gpu; // create top level let instance = vk_back::Instance::create("IV", 1) .map_err(|_| "Could not create instance")?; let mut surface = unsafe { instance .create_surface(window) .map_err(|_| "Could not create Surface")? }; let (adapter, device, queue_group) = { use gfx_hal::{ window::Surface, queue::family::QueueFamily, }; // find a adapter with a suitable queue family // TODO add weight for adapters let adapter = Instance::enumerate_adapters(&instance) .into_iter(); // .find(|a| { // a.queue_families // .iter() // .any(|qf| // QueueFamily::queue_type(qf).supports_graphics() // && Surface::supports_queue_family(&surface, &qf) // )}) // .ok_or("Could not find a graphical adapter")?; debug!("Adapters : {:#?}", adapter); let adapter = Instance::enumerate_adapters(&instance) .into_iter() .find(|a| { a.queue_families .iter() .any(|qf| QueueFamily::queue_type(qf).supports_graphics() && Surface::supports_queue_family(&surface, &qf) )}) .ok_or("Could not find a graphical adapter")?; info!("Selected adapter : {}", adapter.info.name); // get the suitable queue family index let queue_family = adapter .queue_families .iter() .find(|qf| QueueFamily::queue_type(*qf).supports_graphics() && Surface::supports_queue_family(&surface, &qf)) .ok_or("Could not find suitable queue_family")?; // get the related physical device and queue family list let Gpu {device, queue_groups} = unsafe { use gfx_hal::{ adapter::PhysicalDevice, Features, }; adapter .physical_device .open(&[(&queue_family, &[1.0; 1])], Features::empty()) .map_err(|_| "Could not open physical device")? }; // retrieve the selected queue family let queue_group = queue_groups .into_iter() .find(|qg| qg.family == queue_family.id()) .ok_or("Could not take ownership of the queue")?; // check our harvest if queue_group.queues.len() <= 0 { return Err("The QueueGroup does not have any CommandQueues available"); }; (adapter, device, queue_group) }; let (swapchain, extent, backbuffer, format, image_count) = { use gfx_hal::window::{ SwapchainConfig, Surface, }; let capabilities = surface.capabilities(&adapter.physical_device); debug!("{:#?}", capabilities); // select optimal presentation mode (vsync, triple buffuring, ...) 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 found")? }; // select optimal alpha composition // let composite_alpha_mode = { // use gfx_hal::window::CompositeAlphaMode; // // [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, // CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] // .iter() // .cloned() // .find(|cam| capabilities.composite_alpha_modes.contains(*cam)) // .ok_or("No CompositeAlphaMode found")? // }; // select optimal format (sRGB) let format = { use gfx_hal::format::{Format, ChannelType}; match surface.supported_formats(&adapter.physical_device) { None => Format::Rgba8Srgb, Some(formats) => formats .iter() .find(|f| f.base_format().1 == ChannelType::Srgb) .cloned() .ok_or("Could no find suitabe format")? }}; let extent = capabilities.extents.end().clone(); // verify swapchain size let image_count = { use gfx_hal::window::PresentMode; capabilities.image_count.end() .min(capabilities.image_count.start() .max(match present_mode { PresentMode::MAILBOX => &3, PresentMode::FIFO => &2, _ => &1, })).clone() }; debug!("image count : {}", image_count); // // a surface support at least one layer // let image_layers = 1; // // // verify surface compatibility // let image_usage = { // use gfx_hal::image::Usage; // // if capabilities.usage.contains(Usage::COLOR_ATTACHMENT) { // Ok(Usage::COLOR_ATTACHMENT) // } else { // Err("This surface does not support color") // } // }?; let swapchain_config = SwapchainConfig::from_caps(&capabilities, format, extent); debug!("{:?}", swapchain_config); let (swapchain, backbuffer) = unsafe { device .create_swapchain(&mut surface, swapchain_config, None) .map_err(|_| "Failed to create swapchain and backbuffer")? }; (swapchain, extent, backbuffer, format, image_count as usize) }; // creating semaphores and fences let (sems_image_available, sems_render_finished, fences) = { let mut sems_image_available : Vec<::Semaphore> = vec![]; let mut sems_render_finished : Vec<::Semaphore> = vec![]; let mut fences :Vec<::Fence> = vec![]; for _ in 0..image_count { sems_image_available .push(device .create_semaphore() .map_err(|_| "Could not create sempahore")? ); sems_render_finished .push(device .create_semaphore() .map_err(|_| "Could not create sempahore")? ); fences .push(device .create_fence(true) .map_err(|_| "Could not create fence")? ); } (sems_image_available, sems_render_finished, fences) }; // creating RenderPass let render_pass = { use gfx_hal::{ pass::{Attachment, AttachmentOps, AttachmentLoadOp, AttachmentStoreOp, SubpassDesc}, image::Layout, }; 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 { device.create_render_pass(&[color_attachment], &[subpass], &[]) .map_err(|_| "Could not create render pass")? } }; // add ImageView "headers" to images in BackBuffer let image_views : Vec<_> = { use gfx_hal::{ image::{ViewKind, SubresourceRange}, format::{Swizzle, Aspects}, }; backbuffer .iter() .map(|img| unsafe { device .create_image_view( &img, ViewKind::D2, format, Swizzle::NO, SubresourceRange { aspects : Aspects::COLOR, levels : 0..1, layers : 0..1, }, ) .map_err(|_| "Could not create ImageViews") }) .collect::, &str>>()? }; let framebuffers: Vec<_> = { image_views .iter() .map(|image_view| unsafe { device .create_framebuffer(&render_pass, vec![image_view], extent.to_extent()) .map_err(|_| "Could not create FrameBuffer") }, ) .collect::, &str>>()? }; let mut command_pool = unsafe { use gfx_hal::pool::CommandPoolCreateFlags; device .create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) .map_err(|_| "Could not create CommandPool")? }; let command_buffers: Vec<_> = unsafe { use gfx_hal::command::Level; framebuffers .iter() .map(|_| command_pool.allocate_one(Level::Primary)) .collect() }; info!("HAL successfully initialized"); Ok( HalState { instance: ManuallyDrop::new(instance), surface: ManuallyDrop::new(surface), adapter: ManuallyDrop::new(adapter), device: device, queue_group: ManuallyDrop::new(queue_group), render_pass: ManuallyDrop::new(render_pass), swapchain: ManuallyDrop::new(swapchain), extent: extent, format: format, render_area: Rect{x: 0, y: 0, w: 1280, h: 720}, sems_image_available, sems_render_finished, fences, image_views, framebuffers, command_pool: ManuallyDrop::new(command_pool), command_buffers, image_count, current_image: 0, } ) } }