vltk 0.1.2

A simplified toolkit for creating applications using Vulkan
Documentation
use crate::platform::required_extension_names;
use crate::{ErrorKind, VlTkError};
use ash::{vk, Entry};
#[cfg(feature = "winit")]
use raw_window_handle::RawDisplayHandle;
use std::ffi::{c_char, c_void, CStr, CString};
use std::fmt::{Debug, Formatter};
use std::ptr;

pub struct ValidationInfo {
    pub is_enable: bool,
    pub required_validation_layers: [&'static str; 1],
}

const VALIDATION: ValidationInfo = ValidationInfo {
    is_enable: true,
    required_validation_layers: ["VK_LAYER_KHRONOS_validation"],
};

#[derive(Clone, Debug, Copy)]
/// Vertex
struct Vertex {
    pos: [f32; 4],
    color: [f32; 4],
}

/// Struct that stores Instance and Entry
pub struct VkContext {
    pub(crate) instance: ash::Instance,
    pub(crate) entry: Entry,
}

impl VkContext {}

/// Tuple-like structure for specifying Api version
pub struct ApiVersion(pub u32, pub u32, pub u32, pub u32);

/// Information for creating an instance
pub struct InstanceInformation<'a, T> {
    /// Api Version
    pub api_version: ApiVersion,
    /// Application name
    pub app_name: &'a str,
    /// Version
    pub version: u32,
    pub additional: T,
}

pub trait CreateInstance<T> {
    fn create_instance(
        &self,
        instance_info: InstanceInformation<T>,
    ) -> Result<(ash::Instance, Entry), VlTkError>;
}

pub struct DescriptionInformation {}

#[derive(Clone, Copy)]
/// Struct for accessing Vulkan
pub struct Vk {}

impl Vk {
    unsafe extern "system" fn vulkan_debug_utils_callback(
        message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
        message_type: vk::DebugUtilsMessageTypeFlagsEXT,
        p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
        _p_user_data: *mut c_void,
    ) -> vk::Bool32 {
        let severity = match message_severity {
            vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => "[Verbose]",
            vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => "[Warning]",
            vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => "[Error]",
            vk::DebugUtilsMessageSeverityFlagsEXT::INFO => "[Info]",
            _ => "[Unknown]",
        };
        let types = match message_type {
            vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[General]",
            vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[Performance]",
            vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[Validation]",
            _ => "[Unknown]",
        };
        let message = CStr::from_ptr((*p_callback_data).p_message);
        println!("[Debug]{}{}{:?}", severity, types, message);

        vk::FALSE
    }

    fn init_validation(&self, entry: &Entry) -> vk::DebugUtilsMessengerCreateInfoEXT {
        if VALIDATION.is_enable && !Self::check_validation_layer_support(entry) {
            panic!("Validation layers requested, but not available!");
        }

        vk::DebugUtilsMessengerCreateInfoEXT {
            s_type: vk::StructureType::DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
            p_next: ptr::null(),
            flags: vk::DebugUtilsMessengerCreateFlagsEXT::empty(),
            message_severity: vk::DebugUtilsMessageSeverityFlagsEXT::WARNING |
                // vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE |
                // vk::DebugUtilsMessageSeverityFlagsEXT::INFO |
                vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
            message_type: vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
                | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
                | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION,
            pfn_user_callback: Some(Self::vulkan_debug_utils_callback),
            p_user_data: ptr::null_mut(),
        }
    }

    fn check_validation_layer_support(entry: &Entry) -> bool {
        let layer_properties = entry
            .enumerate_instance_layer_properties()
            .expect("Failed to enumerate Instance Layers Properties!");
        //layer_properties.len() <= 0

        if layer_properties.is_empty() {
            eprintln!("No available layers.");
            return false;
        } else {
            println!("Instance Available Layers: ");
            for layer in layer_properties.iter() {
                let layer_name = vk_to_str(&layer.layer_name);
                println!("\t{}", layer_name);
            }
        }

        for required_layer_name in VALIDATION.required_validation_layers.iter() {
            let mut is_layer_found = false;

            for layer_property in layer_properties.iter() {
                let test_layer_name = vk_to_str(&layer_property.layer_name);
                if (*required_layer_name) == test_layer_name {
                    is_layer_found = true;
                    break;
                }
            }

            if !is_layer_found {
                return false;
            }
        }

        true
    }

    /// Create context
    pub fn create_context(&self, instance: ash::Instance, entry: Entry) -> VkContext {
        VkContext { instance, entry }
    }
}

impl CreateInstance<()> for Vk {
    fn create_instance(
        &self,
        info: InstanceInformation<()>,
    ) -> Result<(ash::Instance, Entry), VlTkError> {
        unsafe {
            // Init Vulkan
            let entry = Entry::linked();
            let create_info = Self::init_validation(self, &entry);

            // Fields
            let app_name = CString::new(info.app_name).unwrap();
            let engine_name = CString::new("Vulkan Engine").unwrap();

            // Initialize descriptions
            let app_desc = vk::ApplicationInfo {
                p_application_name: app_name.as_ptr(),
                s_type: vk::StructureType::APPLICATION_INFO,
                p_next: ptr::null(),
                p_engine_name: engine_name.as_ptr(),
                api_version: vk::make_api_version(
                    info.api_version.0,
                    info.api_version.1,
                    info.api_version.2,
                    info.api_version.3,
                ),
                ..Default::default()
            };

            // Initialize validation
            let extension_names = required_extension_names();
            let requred_validation_layer_raw_names: Vec<CString> = VALIDATION
                .required_validation_layers
                .iter()
                .map(|layer_name| CString::new(*layer_name).unwrap())
                .collect();
            let enable_layer_names: Vec<*const i8> = requred_validation_layer_raw_names
                .iter()
                .map(|layer_name| layer_name.as_ptr())
                .collect();

            let len = extension_names.len();

            let instance_desc = vk::InstanceCreateInfo {
                s_type: vk::StructureType::INSTANCE_CREATE_INFO,
                p_next: if VALIDATION.is_enable {
                    &create_info as *const vk::DebugUtilsMessengerCreateInfoEXT as *const c_void
                } else {
                    ptr::null()
                },
                pp_enabled_layer_names: if VALIDATION.is_enable {
                    enable_layer_names.as_ptr()
                } else {
                    ptr::null()
                },
                p_application_info: &app_desc,
                pp_enabled_extension_names: extension_names.as_ptr(),
                enabled_extension_count: len as u32,
                ..Default::default()
            };

            // Create instance
            let instance = match entry.create_instance(&instance_desc, None) {
                Ok(instance) => instance,
                Err(err) => return Err(VlTkError::new(ErrorKind::VkInstanceInitializeError, err)),
            };

            Ok((instance, entry))
        }
    }
}

#[cfg(feature = "winit")]
impl CreateInstance<RawDisplayHandle> for Vk {
    fn create_instance(
        &self,
        info: InstanceInformation<RawDisplayHandle>,
    ) -> Result<(ash::Instance, Entry), VlTkError> {
        unsafe {
            // Init Vulkan
            let entry = Entry::linked();
            let create_info = Self::init_validation(self, &entry);

            // Fields
            let app_name = CString::new(info.app_name).unwrap();
            let engine_name = CString::new("Vulkan Engine").unwrap();

            // Get extensions
            let surface_extensions =
                match ash_window::enumerate_required_extensions(info.additional) {
                    Ok(ext) => ext,
                    Err(err) => return Err(VlTkError::new(ErrorKind::VkInitialize, err)),
                };

            // Initialize descriptions
            let app_desc = vk::ApplicationInfo {
                p_application_name: app_name.as_ptr(),
                s_type: vk::StructureType::APPLICATION_INFO,
                p_next: ptr::null(),
                p_engine_name: engine_name.as_ptr(),
                api_version: vk::make_api_version(
                    info.api_version.0,
                    info.api_version.1,
                    info.api_version.2,
                    info.api_version.3,
                ),
                ..Default::default()
            };

            // Initialize validation
            let extension_names = required_extension_names();
            let requred_validation_layer_raw_names: Vec<CString> = VALIDATION
                .required_validation_layers
                .iter()
                .map(|layer_name| CString::new(*layer_name).unwrap())
                .collect();
            let enable_layer_names: Vec<*const i8> = requred_validation_layer_raw_names
                .iter()
                .map(|layer_name| layer_name.as_ptr())
                .collect();

            let len = extension_names.len();

            let instance_desc = vk::InstanceCreateInfo {
                s_type: vk::StructureType::INSTANCE_CREATE_INFO,
                p_next: if VALIDATION.is_enable {
                    &create_info as *const vk::DebugUtilsMessengerCreateInfoEXT as *const c_void
                } else {
                    ptr::null()
                },
                pp_enabled_layer_names: if VALIDATION.is_enable {
                    enable_layer_names.as_ptr()
                } else {
                    ptr::null()
                },
                p_application_info: &app_desc,
                pp_enabled_extension_names: extension_names.as_ptr(),
                enabled_extension_count: len as u32,
                ..Default::default()
            };

            // Create instance
            let instance = match entry.create_instance(&instance_desc, None) {
                Ok(instance) => instance,
                Err(err) => return Err(VlTkError::new(ErrorKind::VkInstanceInitializeError, err)),
            };

            Ok((instance, entry))
        }
    }
}
fn vk_to_str(raw_string_array: &[c_char]) -> String {
    let raw_string = unsafe {
        let pointer = raw_string_array.as_ptr();
        CStr::from_ptr(pointer)
    };

    raw_string
        .to_str()
        .expect("Failed to convert vulkan raw string.")
        .to_owned()
}

pub struct Surface {
    surface: ash::extensions::khr::Surface,
    surface_khr: ash::vk::SurfaceKHR,
}

impl Surface {
    /// Create surface
    pub(crate) fn new(
        surface: ash::extensions::khr::Surface,
        surface_khr: ash::vk::SurfaceKHR,
    ) -> Self {
        Self {
            surface,
            surface_khr,
        }
    }

    pub fn destroy_surface(&self) {
        unsafe {
            self.surface.destroy_surface(self.surface_khr, None);
        }
    }
}

impl Debug for Surface {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{:?}", self.surface_khr))
    }
}