diff --git a/src/app.rs b/src/app.rs index 15004b2..032af4e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,19 +1,17 @@ mod data; mod functions; +mod structs; use data::AppData; +use structs::*; -use crate::VALIDATION_ENABLED; use ::anyhow::{anyhow, Result}; use ::lazy_static::lazy_static; -use ::log::*; use ::nalgebra_glm as glm; use ::std::mem::size_of; -use ::thiserror::Error; use ::vulkanalia::loader::{LibloadingLoader, LIBRARY}; use ::vulkanalia::prelude::v1_0::*; -use ::vulkanalia::vk::{ExtDebugUtilsExtension, KhrSurfaceExtension, KhrSwapchainExtension}; -use ::vulkanalia::window as vk_window; +use ::vulkanalia::vk::{KhrSurfaceExtension, KhrSwapchainExtension}; use ::vulkanalia::Version; use ::winit::window::Window; @@ -53,21 +51,8 @@ impl App { let entry = Entry::new(loader).map_err(|b| anyhow!("{}", b))?; let mut data = AppData::default(); - let instance = data.create_instance(window, &entry)?; - data.surface = vk_window::create_surface(&instance, window)?; - data.pick_physical_device(&instance)?; - let device = data.create_logical_device(&instance)?; - - data.create_swapchain(window, &instance, &device)?; - data.create_swapchain_image_views(&device)?; - data.create_render_pass(&instance, &device)?; - data.create_pipeline(&device)?; - data.create_framebuffers(&device)?; - data.create_command_pool(&instance, &device)?; - data.create_vertex_buffer(&instance, &device)?; - data.create_command_buffers(&device)?; - data.create_sync_objects(&device)?; + let (instance, device) = data.create(window, &entry)?; Ok(Self { _entry: entry, @@ -84,57 +69,11 @@ impl App { /// /// # Safety /// Here be Dragons - pub unsafe fn destroy(&mut self) { - self.destroy_swapchain(); - self.device.destroy_buffer(self.data.vertex_buffer, None); - self.device - .free_memory(self.data.vertex_buffer_memory, None); - - self.data - .in_flight_fences - .iter() - .for_each(|f| self.device.destroy_fence(*f, None)); - self.data - .render_finished_semaphores - .iter() - .for_each(|s| self.device.destroy_semaphore(*s, None)); - self.data - .image_available_semaphores - .iter() - .for_each(|s| self.device.destroy_semaphore(*s, None)); - self.device - .destroy_command_pool(self.data.command_pool, None); - self.device.destroy_device(None); - self.instance.destroy_surface_khr(self.data.surface, None); - - if VALIDATION_ENABLED { - self.instance - .destroy_debug_utils_messenger_ext(self.data.messenger, None); + pub fn destroy(&mut self) { + unsafe { + self.data.destroy_swapchain(&self.device); + self.data.destroy(&self.instance, &self.device); } - - self.instance.destroy_instance(None); - } - - /// Cleanup the swapchain and related objects - /// - /// # Safety - /// Here be Dragons - unsafe fn destroy_swapchain(&mut self) { - self.data - .framebuffers - .iter() - .for_each(|f| self.device.destroy_framebuffer(*f, None)); - self.device - .free_command_buffers(self.data.command_pool, &self.data.command_buffers); - self.device.destroy_pipeline(self.data.pipeline, None); - self.device - .destroy_pipeline_layout(self.data.pipeline_layout, None); - self.device.destroy_render_pass(self.data.render_pass, None); - self.data - .swapchain_image_views - .iter() - .for_each(|v| self.device.destroy_image_view(*v, None)); - self.device.destroy_swapchain_khr(self.data.swapchain, None); } /// Renders a frame for our Vulkan app. @@ -196,7 +135,8 @@ impl App { let changed = result == Ok(vk::SuccessCode::SUBOPTIMAL_KHR) || result == Err(vk::ErrorCode::OUT_OF_DATE_KHR); if self.resized || changed { - self.recreate_swapchain(window)?; + self.data + .recreate_swapchain(window, &self.instance, &self.device)?; } else if let Err(e) = result { return Err(anyhow!(e)); } @@ -213,138 +153,10 @@ impl App { /// Here be Dragons fn recreate_swapchain(&mut self, window: &Window) -> Result<()> { unsafe { - self.device.device_wait_idle()?; - self.destroy_swapchain(); - self.data - .create_swapchain(window, &self.instance, &self.device)?; - self.data.create_swapchain_image_views(&self.device)?; - self.data.create_render_pass(&self.instance, &self.device)?; - self.data.create_pipeline(&self.device)?; - self.data.create_framebuffers(&self.device)?; - self.data.create_command_buffers(&self.device)?; - - self.data - .images_in_flight - .resize(self.data.swapchain_images.len(), vk::Fence::null()); + .recreate_swapchain(window, &self.instance, &self.device)?; } Ok(()) } } - -#[derive(Debug, Error)] -#[error("Missing {0}.")] -pub struct SuitabilityError(pub &'static str); - -#[derive(Copy, Clone, Debug)] -pub(crate) struct QueueFamilyIndices { - graphics: u32, - present: u32, -} - -impl QueueFamilyIndices { - unsafe fn get( - instance: &Instance, - data: &AppData, - physical_device: vk::PhysicalDevice, - ) -> Result { - let properties = instance.get_physical_device_queue_family_properties(physical_device); - - let graphics = properties - .iter() - .position(|p| p.queue_flags.contains(vk::QueueFlags::GRAPHICS)) - .map(|i| i as u32); - - let mut present = None; - for (index, _properties) in properties.iter().enumerate() { - if instance.get_physical_device_surface_support_khr( - physical_device, - index as u32, - data.surface, - )? { - present = Some(index as u32); - break; - } - } - - if let (Some(graphics), Some(present)) = (graphics, present) { - Ok(Self { graphics, present }) - } else { - Err(anyhow!(SuitabilityError( - "Missing required queue families." - ))) - } - } -} - -#[derive(Clone, Debug)] -pub(crate) struct SwapchainSupport { - capabilities: vk::SurfaceCapabilitiesKHR, - formats: Vec, - present_modes: Vec, -} - -impl SwapchainSupport { - unsafe fn get( - instance: &Instance, - data: &AppData, - physical_device: vk::PhysicalDevice, - ) -> Result { - Ok(Self { - capabilities: instance - .get_physical_device_surface_capabilities_khr(physical_device, data.surface)?, - formats: instance - .get_physical_device_surface_formats_khr(physical_device, data.surface)?, - present_modes: instance - .get_physical_device_surface_present_modes_khr(physical_device, data.surface)?, - }) - } -} - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub(crate) struct Vertex { - pos: glm::Vec2, - color: glm::Vec3, -} - -impl Vertex { - fn new(pos: glm::Vec2, color: glm::Vec3) -> Self { - Self { pos, color } - } - - pub fn binding_description() -> vk::VertexInputBindingDescription { - vk::VertexInputBindingDescription::builder() - .binding(0) - .stride(size_of::() as u32) - .input_rate(vk::VertexInputRate::VERTEX) - .build() - } - - pub fn attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { - let pos = vk::VertexInputAttributeDescription::builder() - .binding(0) - .location(0) - .format(vk::Format::R32G32_SFLOAT) - .offset(0) - .build(); - - let color = vk::VertexInputAttributeDescription::builder() - .binding(0) - .location(1) - .format(vk::Format::R32G32B32_SFLOAT) - .offset(size_of::() as u32) - .build(); - - [pos, color] - } -} - -lazy_static! { - pub(crate) static ref VERTICES: Vec = vec![ - Vertex::new(glm::vec2(0.0, -0.5), glm::vec3(1.0, 0.0, 0.0)), - Vertex::new(glm::vec2(0.5, 0.5), glm::vec3(0.0, 1.0, 0.0)), - Vertex::new(glm::vec2(-0.5, 0.5), glm::vec3(0.0, 0.0, 1.0)), - ]; -} diff --git a/src/app/data.rs b/src/app/data.rs index 63ff77e..dc50477 100644 --- a/src/app/data.rs +++ b/src/app/data.rs @@ -37,7 +37,7 @@ extern "system" fn debug_callback( /// The Vulkan handles and associated properties used by our Vulkan app. #[derive(Clone, Debug, Default)] -pub(super) struct AppData { +pub(crate) struct AppData { // Debug pub(super) messenger: vk::DebugUtilsMessengerEXT, // Surface @@ -73,6 +73,34 @@ pub(super) struct AppData { } impl AppData { + //================================================ + // Create / Destroy Data + //================================================ + + pub(super) unsafe fn create( + &mut self, + window: &Window, + entry: &Entry, + ) -> Result<(Instance, Device)> { + let instance = self.create_instance(window, entry)?; + self.surface = vk_window::create_surface(&instance, window)?; + + self.pick_physical_device(&instance)?; + let device = self.create_logical_device(&instance)?; + + self.create_swapchain(window, &instance, &device)?; + self.create_swapchain_image_views(&device)?; + self.create_render_pass(&instance, &device)?; + self.create_pipeline(&device)?; + self.create_framebuffers(&device)?; + self.create_command_pool(&instance, &device)?; + self.create_vertex_buffer(&instance, &device)?; + self.create_command_buffers(&device)?; + self.create_sync_objects(&device)?; + + Ok((instance, device)) + } + pub(super) unsafe fn create_instance( &mut self, window: &Window, @@ -157,6 +185,52 @@ impl AppData { Ok(instance) } + /// Cleanup the swapchain and related objects + /// + /// # Safety + /// Here be Dragons + pub(super) unsafe fn destroy_swapchain(&mut self, device: &Device) { + self.framebuffers + .iter() + .for_each(|f| device.destroy_framebuffer(*f, None)); + device.free_command_buffers(self.command_pool, &self.command_buffers); + device.destroy_pipeline(self.pipeline, None); + device.destroy_pipeline_layout(self.pipeline_layout, None); + device.destroy_render_pass(self.render_pass, None); + self.swapchain_image_views + .iter() + .for_each(|v| device.destroy_image_view(*v, None)); + device.destroy_swapchain_khr(self.swapchain, None); + } + + /// Destroys our Vulkan app, in reverse order of creation + /// + /// # Safety + /// Here be Dragons + pub unsafe fn destroy(&mut self, instance: &Instance, device: &Device) { + device.destroy_buffer(self.vertex_buffer, None); + device.free_memory(self.vertex_buffer_memory, None); + + self.in_flight_fences + .iter() + .for_each(|f| device.destroy_fence(*f, None)); + self.render_finished_semaphores + .iter() + .for_each(|s| device.destroy_semaphore(*s, None)); + self.image_available_semaphores + .iter() + .for_each(|s| device.destroy_semaphore(*s, None)); + device.destroy_command_pool(self.command_pool, None); + device.destroy_device(None); + instance.destroy_surface_khr(self.surface, None); + + if VALIDATION_ENABLED { + instance.destroy_debug_utils_messenger_ext(self.messenger, None); + } + + instance.destroy_instance(None); + } + //================================================ // Physical Device //================================================ @@ -316,6 +390,31 @@ impl AppData { Ok(()) } + /// Recreates the swapchain + /// + /// # Safety + /// Here be Dragons + pub(super) unsafe fn recreate_swapchain( + &mut self, + window: &Window, + instance: &Instance, + device: &Device, + ) -> Result<()> { + device.device_wait_idle()?; + self.destroy_swapchain(device); + self.create_swapchain(window, instance, device)?; + self.create_swapchain_image_views(device)?; + self.create_render_pass(instance, device)?; + self.create_pipeline(device)?; + self.create_framebuffers(device)?; + self.create_command_buffers(device)?; + + self.images_in_flight + .resize(self.swapchain_images.len(), vk::Fence::null()); + + Ok(()) + } + pub(super) unsafe fn create_swapchain_image_views(&mut self, device: &Device) -> Result<()> { self.swapchain_image_views = self .swapchain_images diff --git a/src/app/structs.rs b/src/app/structs.rs new file mode 100644 index 0000000..ff010cb --- /dev/null +++ b/src/app/structs.rs @@ -0,0 +1,134 @@ +use ::anyhow::{anyhow, Result}; +use ::log::*; +use ::nalgebra_glm as glm; +use ::std::mem::size_of; +use ::thiserror::Error; +use ::vulkanalia::prelude::v1_0::*; +use ::vulkanalia::vk::KhrSurfaceExtension; + +use super::AppData; + +// ---------------------------------------------------------------------------- +// Error Wrapper +// ---------------------------------------------------------------------------- + +/// Generic Error wrapper struct +#[derive(Debug, Error)] +#[error("Missing {0}.")] +pub struct SuitabilityError(pub &'static str); + +// ---------------------------------------------------------------------------- +// Queue Family +// ---------------------------------------------------------------------------- + +#[derive(Copy, Clone, Debug)] +pub(crate) struct QueueFamilyIndices { + pub(crate) graphics: u32, + pub(crate) present: u32, +} + +impl QueueFamilyIndices { + pub(crate) unsafe fn get( + instance: &Instance, + data: &AppData, + physical_device: vk::PhysicalDevice, + ) -> Result { + let properties = instance.get_physical_device_queue_family_properties(physical_device); + + let graphics = properties + .iter() + .position(|p| p.queue_flags.contains(vk::QueueFlags::GRAPHICS)) + .map(|i| i as u32); + + let mut present = None; + for (index, _properties) in properties.iter().enumerate() { + if instance.get_physical_device_surface_support_khr( + physical_device, + index as u32, + data.surface, + )? { + present = Some(index as u32); + break; + } + } + + if let (Some(graphics), Some(present)) = (graphics, present) { + Ok(Self { graphics, present }) + } else { + Err(anyhow!(SuitabilityError( + "Missing required queue families." + ))) + } + } +} + +// ---------------------------------------------------------------------------- +// Swapchain +// ---------------------------------------------------------------------------- + +#[derive(Clone, Debug)] +pub(crate) struct SwapchainSupport { + pub(crate) capabilities: vk::SurfaceCapabilitiesKHR, + pub(crate) formats: Vec, + pub(crate) present_modes: Vec, +} + +impl SwapchainSupport { + pub(crate) unsafe fn get( + instance: &Instance, + data: &AppData, + physical_device: vk::PhysicalDevice, + ) -> Result { + Ok(Self { + capabilities: instance + .get_physical_device_surface_capabilities_khr(physical_device, data.surface)?, + formats: instance + .get_physical_device_surface_formats_khr(physical_device, data.surface)?, + present_modes: instance + .get_physical_device_surface_present_modes_khr(physical_device, data.surface)?, + }) + } +} + +// ---------------------------------------------------------------------------- +// Vertex +// ---------------------------------------------------------------------------- + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct Vertex { + pos: glm::Vec2, + color: glm::Vec3, +} + +impl Vertex { + pub fn new(pos: glm::Vec2, color: glm::Vec3) -> Self { + Self { pos, color } + } + + pub fn binding_description() -> vk::VertexInputBindingDescription { + vk::VertexInputBindingDescription::builder() + .binding(0) + .stride(size_of::() as u32) + .input_rate(vk::VertexInputRate::VERTEX) + .build() + } + + pub fn attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { + let pos = vk::VertexInputAttributeDescription::builder() + .binding(0) + .location(0) + .format(vk::Format::R32G32_SFLOAT) + .offset(0) + .build(); + + let color = vk::VertexInputAttributeDescription::builder() + .binding(0) + .location(1) + .format(vk::Format::R32G32B32_SFLOAT) + .offset(size_of::() as u32) + .build(); + + [pos, color] + } +}