crystal-vk 0.2.0

Graphics wrapper for Vulkan
Documentation
mod debug_callback;
mod layers;

use std::{error::Error, sync::Arc};

use ash::vk;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};

use crate::{
    device::physical_device::PhysicalDevice,
    render::surface::{self, Surface},
};

pub struct Instance {
    pub(crate) handle: ash::Instance,
    pub(crate) entry: ash::Entry,
    _debug_utils_messanger: Option<debug_callback::DebugUtilsMessanger>,
    window: Option<(RawWindowHandle, RawDisplayHandle)>,
}

unsafe impl Send for Instance {}
unsafe impl Sync for Instance {}

impl std::fmt::Debug for Instance {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("")
    }
}

impl Drop for Instance {
    fn drop(&mut self) {
        unsafe {
            self._debug_utils_messanger = None;
            self.handle.destroy_instance(None);
        }
    }
}

impl Instance {
    pub fn create_surface(self: &Arc<Self>) -> Result<Arc<surface::Surface>, Box<dyn Error>> {
        surface::Surface::new(self.clone(), self.window.expect(""))
    }

    pub fn enumerate_physical_devices(
        self: &Arc<Self>,
        surface: Option<Arc<Surface>>,
    ) -> Result<Vec<Arc<PhysicalDevice>>, Box<dyn Error>> {
        let physical_devices_raw = unsafe { self.handle.enumerate_physical_devices() }?;

        let physical_devices =
            unsafe { PhysicalDevice::new(self.clone(), surface.clone(), physical_devices_raw) }?;

        Ok(physical_devices)
    }

    fn new_in(
        display: Option<(RawWindowHandle, RawDisplayHandle)>,
    ) -> Result<Arc<Self>, Box<dyn Error>> {
        let entry = unsafe { ash::Entry::load() }?;

        let mut instance_extensions = vec![
            #[cfg(debug_assertions)]
            vk::EXT_DEBUG_UTILS_NAME.as_ptr(),
            #[cfg(target_vendor = "apple")]
            vk::KHR_PORTABILITY_ENUMERATION_NAME.as_ptr(),
        ];

        let display_extensions;

        if let Some(display) = display {
            display_extensions = ash_window::enumerate_required_extensions(display.1)?;

            display_extensions
                .iter()
                .for_each(|ext| instance_extensions.push(*ext));
        }

        let flags = if cfg!(target_vendor = "apple") {
            vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR
        } else {
            vk::InstanceCreateFlags::empty()
        };

        pub(super) const API_VERSION_LATEST: u32 = u32::MAX;

        let app_info = vk::ApplicationInfo::default().api_version(API_VERSION_LATEST);
        #[cfg(not(debug_assertions))]
        let create_info = vk::InstanceCreateInfo::default()
            .flags(flags)
            .application_info(&app_info)
            .enabled_extension_names(&instance_extensions);

        #[cfg(debug_assertions)]
        let layers;
        #[cfg(debug_assertions)]
        let layers_pp: Vec<*const i8>;

        #[cfg(debug_assertions)]
        let create_info = {
            layers = layers::get_supported_validation_layers(&entry);

            if layers.is_empty() {
                println!(
                    "No validation layers found!
                Vulkan SDK should be installed for proper debug.
                Visit https://vulkan.lunarg.com/"
                );

                vk::InstanceCreateInfo::default()
                    .flags(flags)
                    .application_info(&app_info)
                    .enabled_extension_names(&instance_extensions)
            } else {
                layers_pp = layers.iter().map(|x| x.as_ptr() as *const _).collect();

                let mut create_info = vk::InstanceCreateInfo::default()
                    .flags(flags)
                    .application_info(&app_info)
                    .enabled_extension_names(&instance_extensions);

                create_info.pp_enabled_layer_names = layers_pp.as_ptr() as *const _;
                create_info.enabled_layer_count = layers_pp.len() as u32;

                create_info
            }
        };

        let instance = unsafe { entry.create_instance(&create_info, None) }?;

        let _debug_utils_messanger = {
            #[cfg(debug_assertions)]
            match debug_callback::create_debug_utils_messanger(&entry, &instance) {
                Ok(debug_utils_messanger) => Some(debug_utils_messanger),
                Err(e) => {
                    dbg!(e);
                    None
                }
            }

            #[cfg(not(debug_assertions))]
            None
        };

        Ok(Arc::new(Self {
            handle: instance,
            entry,
            _debug_utils_messanger,
            window: display,
        }))
    }

    pub fn new() -> Result<Arc<Self>, Box<dyn Error>> {
        Self::new_in(None)
    }

    pub fn new_window<T: HasWindowHandle + HasDisplayHandle>(
        window: &T,
    ) -> Result<Arc<Self>, Box<dyn Error>> {
        Self::new_in(Some((
            window.window_handle().unwrap().as_raw(),
            window.display_handle().unwrap().as_raw(),
        )))
    }
}