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)]
struct Vertex {
pos: [f32; 4],
color: [f32; 4],
}
pub struct VkContext {
pub(crate) instance: ash::Instance,
pub(crate) entry: Entry,
}
impl VkContext {}
pub struct ApiVersion(pub u32, pub u32, pub u32, pub u32);
pub struct InstanceInformation<'a, T> {
pub api_version: ApiVersion,
pub app_name: &'a str,
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)]
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::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!");
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
}
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 {
let entry = Entry::linked();
let create_info = Self::init_validation(self, &entry);
let app_name = CString::new(info.app_name).unwrap();
let engine_name = CString::new("Vulkan Engine").unwrap();
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()
};
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()
};
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 {
let entry = Entry::linked();
let create_info = Self::init_validation(self, &entry);
let app_name = CString::new(info.app_name).unwrap();
let engine_name = CString::new("Vulkan Engine").unwrap();
let surface_extensions =
match ash_window::enumerate_required_extensions(info.additional) {
Ok(ext) => ext,
Err(err) => return Err(VlTkError::new(ErrorKind::VkInitialize, err)),
};
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()
};
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()
};
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 {
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))
}
}