diff --git a/src/display/app.rs b/src/display/app.rs index e85d206..5c30801 100644 --- a/src/display/app.rs +++ b/src/display/app.rs @@ -1,61 +1,22 @@ -use std::fmt::{Display, Formatter}; -use ash::vk::QueueFlags; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::{WindowId}; use crate::display::window::Window; -use crate::vulkan::{VkInstance, VkPhysicalDevice}; +use crate::vulkan::VkRenderContext; pub struct App { window: Window, - instance: Option + render_context: Option, } impl App { pub fn new(window: Window) -> Self { Self { window, - instance: None + render_context: None, } } - - fn init_vulkan(&mut self) { - let required_extensions = self.window - .required_extensions() - .expect("Unable to get required required extensions"); - - log::info!("Initializing Vulkan instance"); - let instance = VkInstance::new(&required_extensions); - log::info!("Vulkan instance created"); - log::info!("{instance}"); - - let surface = instance.create_surface(&self.window) - .expect("Unable to create surface"); - - let mut physical_devices = instance.get_physical_devices(); - physical_devices.sort_by(|a, b| b.priority().cmp(&a.priority())); - - let (physical_device, queue_family_index) = physical_devices - .iter() - .find_map(|physical_device| { - physical_device.queue_family_properties - .iter() - .enumerate() - .find_map(|(index, queue_family_property)| { - if surface.physical_device_queue_supported(physical_device, index as u32).unwrap_or(false) { - Some((physical_device, index as u32)) - } else { - None - } - }) - }) - .expect("Unable to find suitable device"); - - - self.instance = Some(instance); - - } } impl ApplicationHandler for App { @@ -64,13 +25,13 @@ impl ApplicationHandler for App { .map_err(|err| format!("Failed to create window: {}", err)) .unwrap(); - self.init_vulkan(); + self.render_context = VkRenderContext::init(&self.window).ok(); } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { match event { WindowEvent::CloseRequested => { - log::info!("The close button was pressed; stopping"); + log::debug!("The close button was pressed; stopping"); event_loop.exit(); } _ => self.window.window_event(event_loop, id, event), diff --git a/src/display/window.rs b/src/display/window.rs index f062162..98976f9 100644 --- a/src/display/window.rs +++ b/src/display/window.rs @@ -28,7 +28,7 @@ impl Window { pub fn required_extensions(&self) -> anyhow::Result> { let display_handle = self.handle .as_ref() - .ok_or_else(||anyhow::anyhow!("Window not found"))? + .ok_or_else(|| anyhow::anyhow!("Window not found"))? .display_handle()?; #[allow(unused_mut)] diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 9d2cfd4..f05a236 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,8 +1,16 @@ -pub(self) mod vk_instance; -pub(self) mod vk_physical_device; -pub(self) mod vk_logical_device; -mod utils; -mod vk_surface; +pub(self) mod vk_render_context; +pub use vk_render_context::VkRenderContext; +pub(self) mod vk_instance; pub use vk_instance::VkInstance; -pub use vk_physical_device::VkPhysicalDevice; \ No newline at end of file + +pub(self) mod vk_surface; +pub use vk_surface::VkSurface; + +pub(self) mod vk_physical_device; +pub use vk_physical_device::VkPhysicalDevice; + +pub(self) mod vk_device; +pub use vk_device::VkDevice; + +mod utils; diff --git a/src/vulkan/vk_device.rs b/src/vulkan/vk_device.rs new file mode 100644 index 0000000..af6d0ca --- /dev/null +++ b/src/vulkan/vk_device.rs @@ -0,0 +1,57 @@ +use ash::vk; +use crate::vulkan::{VkInstance, VkPhysicalDevice}; + +pub struct VkDevice { + handle: ash::Device, + queue_family_index: u32 +} + +impl VkDevice { + pub fn new_graphics_device( + instance: &VkInstance, + physical_device: &VkPhysicalDevice, + queue_family_index: u32, + ) -> anyhow::Result { + let device_extension_names_raw = [ + ash::khr::swapchain::NAME.as_ptr(), + #[cfg(any(target_os = "macos", target_os = "ios"))] + ash::khr::portability_subset::NAME.as_ptr(), + ]; + let features = vk::PhysicalDeviceFeatures { + shader_clip_distance: 1, + ..Default::default() + }; + let priorities = [1.0]; + + let queue_info = vk::DeviceQueueCreateInfo::default() + .queue_family_index(queue_family_index) + .queue_priorities(&priorities); + + let device_create_info = vk::DeviceCreateInfo::default() + .queue_create_infos(std::slice::from_ref(&queue_info)) + .enabled_extension_names(&device_extension_names_raw) + .enabled_features(&features); + + let device = instance + .create_device(physical_device, &device_create_info, None)?; + + Ok(Self { + handle: device, + queue_family_index + }) + } + + pub fn get_device_queue(&self, queue_index: u32) -> vk::Queue { + unsafe { + self.handle.get_device_queue(self.queue_family_index, queue_index) + } + } +} + +impl Drop for VkDevice { + fn drop(&mut self) { + unsafe { + self.handle.destroy_device(None); + } + } +} \ No newline at end of file diff --git a/src/vulkan/vk_instance.rs b/src/vulkan/vk_instance.rs index 738b3e4..5171f8d 100644 --- a/src/vulkan/vk_instance.rs +++ b/src/vulkan/vk_instance.rs @@ -1,7 +1,8 @@ -use std::ffi::{c_char, CString}; -use std::fmt::{Debug, Display, Formatter}; +use std::ffi::{c_char, CStr, CString}; +use std::fmt::{Display, Formatter}; use ash::{Instance, vk, Entry}; use ash::khr::surface; +use ash::prelude::VkResult; use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use crate::vulkan::utils::formatter::format_instance_layer; use crate::vulkan::utils::layers::{use_layers, LayersSelector}; @@ -19,6 +20,17 @@ impl VkInstance { ) -> Self { let entry = Entry::linked(); + log::debug!("Initializing Vulkan instance"); + + if log::log_enabled!(log::Level::Debug) { + let required_extensions = required_extensions + .iter() + .map(|str| unsafe { CStr::from_ptr(*str) }) + .map(|cstr| cstr.to_string_lossy()) + .collect::>(); + log::debug!("Required instance extensions: {}", required_extensions.join(", ")); + } + // Layers let layers = use_layers( &entry, @@ -28,6 +40,12 @@ impl VkInstance { "VK_LAYER_NV_optimus" ]) ); + if log::log_enabled!(log::Level::Info) { + let layers = layers.iter() + .map(|layer| layer.to_string_lossy()) + .collect::>(); + log::debug!("Selected debug layers : {}", layers.join(", ")) + } let layers_raw = layers.iter().map(|s| s.as_ptr()).collect::>(); // App Info @@ -58,6 +76,8 @@ impl VkInstance { .expect("Instance creation error") }; + log::debug!("Vulkan instance created"); + Self { entry, handle: instance @@ -91,11 +111,24 @@ impl VkInstance { )? }; + log::debug!("Surface created"); + Ok(VkSurface::new( surface_loader, surface, )) } + + pub fn create_device( + &self, + physical_device: &VkPhysicalDevice, + create_info: &vk::DeviceCreateInfo, + allocation_callbacks: Option<&vk::AllocationCallbacks>, + ) -> VkResult { + unsafe { + self.handle.create_device(physical_device.handle, &create_info, allocation_callbacks) + } + } } impl Drop for VkInstance { @@ -103,6 +136,7 @@ impl Drop for VkInstance { unsafe { self.handle.destroy_instance(None); } + log::debug!("Vulkan instance destroyed"); } } diff --git a/src/vulkan/vk_logical_device.rs b/src/vulkan/vk_logical_device.rs deleted file mode 100644 index 5b22cc3..0000000 --- a/src/vulkan/vk_logical_device.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct VkLogicalDevice { - -} \ No newline at end of file diff --git a/src/vulkan/vk_physical_device.rs b/src/vulkan/vk_physical_device.rs index 2f4411e..1948f03 100644 --- a/src/vulkan/vk_physical_device.rs +++ b/src/vulkan/vk_physical_device.rs @@ -1,6 +1,7 @@ use std::fmt::{Display, Formatter}; use ash::vk; use crate::vulkan::utils::formatter::{format_driver_version, format_vulkan_version}; +use crate::vulkan::vk_surface::VkSurface; pub struct VkPhysicalDevice { // Vulkan properties @@ -24,6 +25,39 @@ impl VkPhysicalDevice { queue_family_properties: device_queue_families } } + + pub fn find_queue_family_by( + &self, + queue_flags: Option, + surface: Option<&VkSurface> + ) -> Option<(u32, &vk::QueueFamilyProperties)> { + self.queue_family_properties + .iter() + .enumerate() + .find_map(|(index, queue_family_property)| { + let surface_check_passed = match surface { + Some(surface) => { + surface.physical_device_queue_supported(self, index as u32) + .unwrap_or(false) + } + None => true, + }; + + let queue_flags_check_passed = match queue_flags { + Some(queue_flags) => { + queue_family_property.queue_flags + .contains(queue_flags) + }, + None => true + }; + + if surface_check_passed && queue_flags_check_passed { + Some((index as u32, queue_family_property)) + } else { + None + } + }) + } pub fn priority(&self) -> usize { match self.properties.device_type { diff --git a/src/vulkan/vk_render_context.rs b/src/vulkan/vk_render_context.rs new file mode 100644 index 0000000..a742d64 --- /dev/null +++ b/src/vulkan/vk_render_context.rs @@ -0,0 +1,46 @@ +use ash::vk::QueueFlags; +use crate::vulkan::{VkDevice, VkInstance, VkSurface}; + +pub struct VkRenderContext { + surface: VkSurface, + instance: VkInstance, +} + +impl VkRenderContext { + pub fn init(window: &crate::display::Window) -> anyhow::Result { + let required_extensions = window + .required_extensions()?; + + let instance = VkInstance::new(&required_extensions); + let surface = instance.create_surface(&window)?; + + let mut physical_devices = instance.get_physical_devices(); + physical_devices.sort_by(|a, b| b.priority().cmp(&a.priority())); + + let (physical_device, (queue_family_index, _)) = physical_devices + .iter() + .find_map(|physical_device| { + physical_device.find_queue_family_by(Some(QueueFlags::GRAPHICS), Some(&surface)) + .and_then(|queue_index| Some((physical_device, queue_index))) + }) + .expect("Unable to find suitable device"); + + let device = VkDevice::new_graphics_device(&instance, &physical_device, queue_family_index) + .expect("Unable to create device"); + + let present_queue = device.get_device_queue(0); + + let surface_format = surface.get_physical_device_surface_formats(physical_device) + .unwrap_or_default() + .first() + .expect("Unable to get surface format"); + + let surface_capabilities = surface.get_physical_device_surface_capabilities(physical_device) + .expect("Unable to get surface capabilities"); + + Ok(Self{ + instance, + surface + }) + } +} \ No newline at end of file diff --git a/src/vulkan/vk_surface.rs b/src/vulkan/vk_surface.rs index 3472ed8..f8e48ed 100644 --- a/src/vulkan/vk_surface.rs +++ b/src/vulkan/vk_surface.rs @@ -1,22 +1,23 @@ use ash::prelude::VkResult; +use ash::vk; use crate::vulkan::VkPhysicalDevice; pub struct VkSurface { surface_loader: ash::khr::surface::Instance, - surface: ash::vk::SurfaceKHR, + surface: vk::SurfaceKHR, } impl VkSurface { pub fn new( surface_loader: ash::khr::surface::Instance, - surface: ash::vk::SurfaceKHR, + surface: vk::SurfaceKHR, ) -> Self { Self { surface_loader, surface } } - + pub fn physical_device_queue_supported(&self, physical_device: &VkPhysicalDevice, queue_index: u32) -> VkResult { unsafe { self.surface_loader.get_physical_device_surface_support( @@ -26,4 +27,40 @@ impl VkSurface { ) } } + + pub fn get_physical_device_surface_formats(&self, physical_device: &VkPhysicalDevice) -> VkResult> { + unsafe { + self.surface_loader.get_physical_device_surface_formats( + physical_device.handle, + self.surface + ) + } + } + + pub fn get_physical_device_surface_capabilities(&self, physical_device: &VkPhysicalDevice) -> VkResult { + unsafe { + self.surface_loader.get_physical_device_surface_capabilities( + physical_device.handle, + self.surface + ) + } + } + + pub fn get_physical_device_surface_present_modes(&self, physical_device: &VkPhysicalDevice) -> VkResult> { + unsafe { + self.surface_loader.get_physical_device_surface_present_modes( + physical_device.handle, + self.surface + ) + } + } } + +impl Drop for VkSurface { + fn drop(&mut self) { + unsafe { + self.surface_loader.destroy_surface(self.surface, None); + } + log::debug!("Surface destroyed"); + } +} \ No newline at end of file