vulk-rs 0.0.2

Opinionated Rust bindings for Vulkan.
Documentation
//! # Opinionated low-level Rust bindings for Vulkan
//! Bindgen used to generate the native unsafe bindings.
//! 
//! Provides an opinionated OOP wrapper around the Vulkan API.
//! 
//! Currently heavily *work in progress*.
//! 
//! Relies on the `VK_LAYER_KHRONOS_validation` layer and `VK_EXT_debug_utils` extension in debug mode, however they are stripped out in release mode.

#[allow(unused)]

mod native;
pub mod vk {
    pub use native::VkPhysicalDeviceProperties as PhysicalDeviceProperties;
    pub use native::VkQueueFamilyProperties as QueueFamilyProperties;
    pub use native::VkPhysicalDeviceFeatures as PhysicalDeviceFeatures;

    use crate::*;

    pub struct Instance {
        __instance: native::VkInstance,
        __validation: native::VkDebugUtilsMessengerEXT,
    }

    impl Drop for Instance {
        /// Calls `vkDestroyInstance` on the Vulkan instance.
        fn drop(&mut self) {
            unsafe {
                if cfg!(debug_assertions) {
                    assert!(!self.__validation.is_null());

                    let vk_destroy_debug_utils_messenger_ext:
                        unsafe extern "system" fn(
                            native::VkInstance,
                            native::VkDebugUtilsMessengerEXT,
                            *const ::std::ffi::c_void)
                        = std::mem::transmute(native::vkGetInstanceProcAddr
                            (self.__instance,
                    std::ffi::CString::new("vkDestroyDebugUtilsMessengerEXT")
                                .unwrap()
                                .to_owned()
                                .as_ptr()));

                    vk_destroy_debug_utils_messenger_ext(self.__instance, self.__validation, std::ptr::null());
                }
                native::vkDestroyInstance(self.__instance, std::ptr::null());
            }
        }
    }


    /// A Vulkan physical device.
    /// Maps the `VkPhysicalDevice` struct.
    /// Represents a device that hasn't been initialized yet.
    pub struct PhysicalDevice {
        __physical_device: native::VkPhysicalDevice,
    }


    pub trait InstanceMethods {
        /// Enumerate the physical devices available to the instance.
        /// Maps the `vkEnumeratePhysicalDevices` function.
        fn enumerate_physical_devices(&self) -> Vec<PhysicalDevice>;
    }

    impl InstanceMethods for Instance {
        fn enumerate_physical_devices(&self) -> Vec<PhysicalDevice> {
            unsafe {
                let mut num_devices: u32 = 0;
                native::vkEnumeratePhysicalDevices(self.__instance, &mut num_devices, std::ptr::null_mut());
                assert!(num_devices > 0);

                let mut physical_devices: Vec<native::VkPhysicalDevice> = Vec::with_capacity(num_devices as usize);
                native::vkEnumeratePhysicalDevices(self.__instance, &mut num_devices, physical_devices.as_mut_ptr());
                physical_devices.set_len(num_devices as usize);

                let mut devices: Vec<PhysicalDevice> = Vec::with_capacity(num_devices as usize);

                for i in 0..num_devices {
                    dbg!(physical_devices[i as usize]);
                    let device = PhysicalDevice { __physical_device: physical_devices[i as usize] };
                    assert!(!device.__physical_device.is_null());
                    devices.push(device);
                }

                return devices;
            }
        }
    }

    pub trait PhysicalDeviceMethods {
        /// Get the properties of the physical device
        /// 
        /// Maps the `vkGetPhysicalDeviceProperties` function
        fn get_properties(&self) -> native::VkPhysicalDeviceProperties;
        /// Get the queue family properties of the physical device
        /// 
        /// Maps the `vkGetPhysicalDeviceQueueFamilyProperties` function
        fn get_queue_family_properties(&self) -> Vec<native::VkQueueFamilyProperties>;
        /// Get the features of the physical device
        /// 
        /// Maps the `vkGetPhysicalDeviceFeatures` function
        fn get_features(&self) -> native::VkPhysicalDeviceFeatures;
    }

    impl PhysicalDeviceMethods for PhysicalDevice {
        fn get_properties(&self) -> native::VkPhysicalDeviceProperties {
            unsafe {
                let mut properties: native::VkPhysicalDeviceProperties = std::mem::zeroed();
                native::vkGetPhysicalDeviceProperties(self.__physical_device, &mut properties);
                return properties;
            }
        }
        fn get_queue_family_properties(&self) -> Vec<native::VkQueueFamilyProperties> {
            unsafe {
                let mut num_queue_families: u32 = 0;
                native::vkGetPhysicalDeviceQueueFamilyProperties(self.__physical_device, &mut num_queue_families, std::ptr::null_mut());
                assert!(num_queue_families > 0);

                let mut queue_families: Vec<native::VkQueueFamilyProperties> = Vec::with_capacity(num_queue_families as usize);
                native::vkGetPhysicalDeviceQueueFamilyProperties(self.__physical_device, &mut num_queue_families, queue_families.as_mut_ptr());
                queue_families.set_len(num_queue_families as usize);

                return queue_families;
            }
        }
        fn get_features(&self) -> native::VkPhysicalDeviceFeatures {
            unsafe {
                let mut features: native::VkPhysicalDeviceFeatures = std::mem::zeroed();
                native::vkGetPhysicalDeviceFeatures(self.__physical_device, &mut features);
                return features;
            }
        }
    }

    /// Create a Vulkan version number.
    /// Similar to the `VK_MAKE_VERSION` macro from C Vulkan headers.
    pub fn make_version(major: u32, minor: u32, patch: u32) -> u32 {
        return (patch<<0) | (minor<<12) | (major<<22) | (0<<29);
    }

    #[cfg(debug_assertions)]
    extern "C" fn debug_callback(
        _message_severity: native::VkDebugUtilsMessageSeverityFlagBitsEXT,
        _message_types: native::VkDebugUtilsMessageTypeFlagsEXT,
        _callback_data: *const native::VkDebugUtilsMessengerCallbackDataEXT,
        _user_data: *mut ::std::os::raw::c_void,
    ) -> native::VkBool32 {
        unsafe {
            let message = std::ffi::CStr::from_ptr((*_callback_data).pMessage);
            if (_message_severity & 
                (native::VkDebugUtilsMessageSeverityFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
                |native::VkDebugUtilsMessageSeverityFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT))
                != 0 {
                panic!("Failed validating, message from layer: {:?}", message);
            }
            println!("{}", format!("Validation layer debug: {}", message.to_str().unwrap()));
        }
        return native::VK_TRUE;
    }

    #[cfg(debug_assertions)]
    fn setup_validation_layer(instance: native::VkInstance) -> native::VkDebugUtilsMessengerEXT {
        let debug_utils_create_info = native::VkDebugUtilsMessengerCreateInfoEXT {
            sType: native::VkStructureType_VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
            pNext: std::ptr::null(),
            flags: 0,
            messageSeverity: 
                native::VkDebugUtilsMessageSeverityFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
                native::VkDebugUtilsMessageSeverityFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
                native::VkDebugUtilsMessageSeverityFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
                native::VkDebugUtilsMessageSeverityFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
            messageType: 
                native::VkDebugUtilsMessageTypeFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                native::VkDebugUtilsMessageTypeFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                native::VkDebugUtilsMessageTypeFlagBitsEXT_VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
            pfnUserCallback: Some(debug_callback),
            pUserData: std::ptr::null_mut(),
        };
        unsafe {
            let vk_create_debug_utils_messenger_ext:
                unsafe extern "system" fn(
                    native::VkInstance,
                    *const native::VkDebugUtilsMessengerCreateInfoEXT, 
                    *const ::std::ffi::c_void, 
                    *mut native::VkDebugUtilsMessengerEXT)
                -> native::VkResult
                = std::mem::transmute(native::vkGetInstanceProcAddr
                    (instance,
            std::ffi::CString::new("vkCreateDebugUtilsMessengerEXT")
                        .unwrap()
                        .to_owned()
                        .as_ptr()));
            let mut out: native::VkDebugUtilsMessengerEXT = std::mem::zeroed();
            vk_create_debug_utils_messenger_ext(instance, &debug_utils_create_info, std::ptr::null(), &mut out);
            return out;
        }
    }


    /// Create a Vulkan instance.
    /// In debug mode, the Vulkan instance will have the validation layer enabled.
    /// In release mode, the Vulkan instance will not have the validation layer enabled.
    /// The instance is automatically destroyed when it goes out of scope.
    /// The instance is asserted to be valid with an `assert!(!instance.is_null())` call.
    /// Will panic if the instance creation fails.
    /// Maps the `vkCreateInstance` function.
    pub fn create_instance() -> Instance {
        let enabled_layers: Vec<&str> =
            vec!["VK_LAYER_KHRONOS_validation\0"];
        let layer_count = enabled_layers.len();
        let enabled_layers: *const *const i8 = Box::into_raw(enabled_layers.into_boxed_slice()).cast_const() as *const *const i8;

        let enabled_exts: Vec<&str> =
            vec!["VK_EXT_debug_utils\0"];
        let ext_count = enabled_exts.len();
        let enabled_exts: *const *const i8 = Box::into_raw(enabled_exts.into_boxed_slice()).cast_const() as *const *const i8;

        let application_info = native::VkApplicationInfo {
            sType: native::VkStructureType_VK_STRUCTURE_TYPE_APPLICATION_INFO,
            pNext: std::ptr::null(),
            pApplicationName: c"vulk".as_ptr(),
            applicationVersion: make_version(0, 1, 0),
            pEngineName: c"vulk".as_ptr(),
            engineVersion: make_version(0, 1, 0),
            apiVersion: make_version(1, 3, 0),
        };
        let instance_info = native::VkInstanceCreateInfo {
            sType: native::VkStructureType_VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
            pNext: std::ptr::null(),
            flags: 0,
            pApplicationInfo: &application_info,
            enabledLayerCount: if cfg!(debug_assertions) { layer_count as u32 } else { 0 },
            ppEnabledLayerNames: if cfg!(debug_assertions) { enabled_layers } else { std::ptr::null() },
            enabledExtensionCount: if cfg!(debug_assertions) { ext_count as u32 } else { 0 },
            ppEnabledExtensionNames: if cfg!(debug_assertions) { enabled_exts } else { std::ptr::null() },
        };

        unsafe {
            let mut instance: native::VkInstance = { std::mem::zeroed() };
            native::vkCreateInstance(&instance_info, std::ptr::null(), &mut instance);
            assert!(!instance.is_null());
            #[cfg(debug_assertions)] {
                let validation = setup_validation_layer(instance);
                return Instance { __instance: instance, __validation: validation};
            }
            #[cfg(not(debug_assertions))]
            {
                return Instance { __instance: instance, __validation: std::ptr::null_mut()};
            }
        }
    }

}