use super::debug::{
DebugCallback, DebugMessageSeverity, DebugMessageType, RealDebugCallbackFn, default_callback,
trampoline,
};
use super::{Error, PhysicalDevice, Result, check};
use crate::raw::VulkanLibrary;
use crate::raw::bindings::*;
use std::ffi::{CStr, CString, c_char, c_void};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ApiVersion(pub u32);
impl ApiVersion {
pub const V1_0: Self = Self(VK_API_VERSION_1_0);
pub const V1_1: Self = Self(VK_API_VERSION_1_1);
pub const V1_2: Self = Self(VK_API_VERSION_1_2);
pub const V1_3: Self = Self(VK_API_VERSION_1_3);
pub const V1_4: Self = Self(VK_API_VERSION_1_4);
pub const fn new(variant: u32, major: u32, minor: u32, patch: u32) -> Self {
Self((variant << 29) | (major << 22) | (minor << 12) | patch)
}
pub const fn major(self) -> u32 {
(self.0 >> 22) & 0x7F
}
pub const fn minor(self) -> u32 {
(self.0 >> 12) & 0x3FF
}
pub const fn patch(self) -> u32 {
self.0 & 0xFFF
}
}
impl std::fmt::Display for ApiVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major(), self.minor(), self.patch())
}
}
impl Default for ApiVersion {
fn default() -> Self {
Self::V1_0
}
}
pub const KHRONOS_VALIDATION_LAYER: &str = "VK_LAYER_KHRONOS_validation";
pub const DEBUG_UTILS_EXTENSION: &str = "VK_EXT_debug_utils";
pub struct InstanceCreateInfo<'a> {
pub application_name: Option<&'a str>,
pub application_version: ApiVersion,
pub engine_name: Option<&'a str>,
pub engine_version: ApiVersion,
pub api_version: ApiVersion,
pub enabled_layers: &'a [&'a str],
pub enabled_extensions: &'a [&'a str],
pub debug_callback: Option<Box<DebugCallback>>,
}
impl<'a> Default for InstanceCreateInfo<'a> {
fn default() -> Self {
Self {
application_name: None,
application_version: ApiVersion::V1_0,
engine_name: None,
engine_version: ApiVersion::V1_0,
api_version: ApiVersion::V1_0,
enabled_layers: &[],
enabled_extensions: &[],
debug_callback: None,
}
}
}
impl<'a> InstanceCreateInfo<'a> {
pub fn validation() -> Self {
Self {
enabled_layers: &[KHRONOS_VALIDATION_LAYER],
enabled_extensions: &[DEBUG_UTILS_EXTENSION],
debug_callback: Some(default_callback()),
..Self::default()
}
}
}
impl<'a> std::fmt::Debug for InstanceCreateInfo<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InstanceCreateInfo")
.field("application_name", &self.application_name)
.field("application_version", &self.application_version)
.field("engine_name", &self.engine_name)
.field("engine_version", &self.engine_version)
.field("api_version", &self.api_version)
.field("enabled_layers", &self.enabled_layers)
.field("enabled_extensions", &self.enabled_extensions)
.field("debug_callback", &self.debug_callback.is_some())
.finish()
}
}
pub(crate) struct InstanceInner {
pub(crate) library: VulkanLibrary,
pub(crate) handle: VkInstance,
pub(crate) dispatch: VkInstanceDispatchTable,
debug_messenger: u64,
debug_callback_box: *mut Box<DebugCallback>,
}
unsafe impl Send for InstanceInner {}
unsafe impl Sync for InstanceInner {}
impl Drop for InstanceInner {
fn drop(&mut self) {
if self.debug_messenger != 0
&& let Some(destroy) = self.dispatch.vkDestroyDebugUtilsMessengerEXT
{
unsafe { destroy(self.handle, self.debug_messenger, std::ptr::null()) };
}
if let Some(destroy) = self.dispatch.vkDestroyInstance {
unsafe { destroy(self.handle, std::ptr::null()) };
}
if !self.debug_callback_box.is_null() {
drop(unsafe { Box::from_raw(self.debug_callback_box) });
}
}
}
#[derive(Clone)]
pub struct LayerProperties {
raw: VkLayerProperties,
}
impl LayerProperties {
pub fn name(&self) -> String {
unsafe { CStr::from_ptr(self.raw.layerName.as_ptr()) }
.to_string_lossy()
.into_owned()
}
pub fn description(&self) -> String {
unsafe { CStr::from_ptr(self.raw.description.as_ptr()) }
.to_string_lossy()
.into_owned()
}
pub fn spec_version(&self) -> ApiVersion {
ApiVersion(self.raw.specVersion)
}
pub fn implementation_version(&self) -> u32 {
self.raw.implementationVersion
}
}
impl std::fmt::Debug for LayerProperties {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LayerProperties")
.field("name", &self.name())
.field("spec_version", &self.spec_version())
.field("implementation_version", &self.implementation_version())
.finish()
}
}
#[derive(Clone)]
pub struct ExtensionProperties {
raw: VkExtensionProperties,
}
impl ExtensionProperties {
pub(crate) fn from_raw(raw: VkExtensionProperties) -> Self {
Self { raw }
}
pub fn name(&self) -> String {
unsafe { CStr::from_ptr(self.raw.extensionName.as_ptr()) }
.to_string_lossy()
.into_owned()
}
pub fn spec_version(&self) -> u32 {
self.raw.specVersion
}
}
impl std::fmt::Debug for ExtensionProperties {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ExtensionProperties")
.field("name", &self.name())
.field("spec_version", &self.spec_version())
.finish()
}
}
#[derive(Clone)]
pub struct Instance {
pub(crate) inner: Arc<InstanceInner>,
}
impl Instance {
pub fn enumerate_layer_properties() -> Result<Vec<LayerProperties>> {
let library = VulkanLibrary::new()?;
let entry = unsafe { library.load_entry() };
let enumerate = entry
.vkEnumerateInstanceLayerProperties
.ok_or(Error::MissingFunction("vkEnumerateInstanceLayerProperties"))?;
let mut count: u32 = 0;
check(unsafe { enumerate(&mut count, std::ptr::null_mut()) })?;
let mut raw: Vec<VkLayerProperties> = vec![unsafe { std::mem::zeroed() }; count as usize];
check(unsafe { enumerate(&mut count, raw.as_mut_ptr()) })?;
Ok(raw
.into_iter()
.map(|r| LayerProperties { raw: r })
.collect())
}
pub fn enumerate_extension_properties() -> Result<Vec<ExtensionProperties>> {
let library = VulkanLibrary::new()?;
let entry = unsafe { library.load_entry() };
let enumerate =
entry
.vkEnumerateInstanceExtensionProperties
.ok_or(Error::MissingFunction(
"vkEnumerateInstanceExtensionProperties",
))?;
let mut count: u32 = 0;
check(unsafe { enumerate(std::ptr::null(), &mut count, std::ptr::null_mut()) })?;
let mut raw: Vec<VkExtensionProperties> =
vec![unsafe { std::mem::zeroed() }; count as usize];
check(unsafe { enumerate(std::ptr::null(), &mut count, raw.as_mut_ptr()) })?;
Ok(raw
.into_iter()
.map(|r| ExtensionProperties { raw: r })
.collect())
}
pub fn new(info: InstanceCreateInfo<'_>) -> Result<Self> {
let library = VulkanLibrary::new()?;
let app_name_c = info.application_name.map(CString::new).transpose()?;
let engine_name_c = info.engine_name.map(CString::new).transpose()?;
let layer_cstrings: Vec<CString> = info
.enabled_layers
.iter()
.map(|s| CString::new(*s))
.collect::<std::result::Result<_, _>>()?;
let ext_cstrings: Vec<CString> = info
.enabled_extensions
.iter()
.map(|s| CString::new(*s))
.collect::<std::result::Result<_, _>>()?;
let layer_ptrs: Vec<*mut c_char> = layer_cstrings
.iter()
.map(|s| s.as_ptr() as *mut c_char)
.collect();
let ext_ptrs: Vec<*mut c_char> = ext_cstrings
.iter()
.map(|s| s.as_ptr() as *mut c_char)
.collect();
let app_info = VkApplicationInfo {
sType: VkStructureType::STRUCTURE_TYPE_APPLICATION_INFO,
pNext: std::ptr::null(),
pApplicationName: app_name_c.as_deref().map_or(std::ptr::null(), CStr::as_ptr),
applicationVersion: info.application_version.0,
pEngineName: engine_name_c
.as_deref()
.map_or(std::ptr::null(), CStr::as_ptr),
engineVersion: info.engine_version.0,
apiVersion: info.api_version.0,
};
let create_info = VkInstanceCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
pApplicationInfo: &app_info,
enabledLayerCount: layer_ptrs.len() as u32,
ppEnabledLayerNames: if layer_ptrs.is_empty() {
std::ptr::null()
} else {
layer_ptrs.as_ptr()
},
enabledExtensionCount: ext_ptrs.len() as u32,
ppEnabledExtensionNames: if ext_ptrs.is_empty() {
std::ptr::null()
} else {
ext_ptrs.as_ptr()
},
..Default::default()
};
let entry = unsafe { library.load_entry() };
let create = entry
.vkCreateInstance
.ok_or(Error::MissingFunction("vkCreateInstance"))?;
let mut handle: VkInstance = std::ptr::null_mut();
let result = unsafe { create(&create_info, std::ptr::null(), &mut handle) };
check(result)?;
let dispatch = unsafe { library.load_instance(handle) };
let mut debug_messenger: u64 = 0;
let mut debug_callback_box: *mut Box<DebugCallback> = std::ptr::null_mut();
if let Some(cb) = info.debug_callback
&& let Some(create_msgr) = dispatch.vkCreateDebugUtilsMessengerEXT
{
let leaked: *mut Box<DebugCallback> = Box::into_raw(Box::new(cb));
debug_callback_box = leaked;
let real: RealDebugCallbackFn = trampoline;
let pfn = unsafe {
std::mem::transmute::<
Option<RealDebugCallbackFn>,
PFN_vkDebugUtilsMessengerCallbackEXT,
>(Some(real))
};
let create_msgr_info = VkDebugUtilsMessengerCreateInfoEXT {
sType: VkStructureType::STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
pNext: std::ptr::null(),
flags: 0,
messageSeverity: DebugMessageSeverity::ALL.0,
messageType: DebugMessageType::ALL.0,
pfnUserCallback: pfn,
pUserData: leaked as *mut c_void,
};
let res = unsafe {
create_msgr(
handle,
&create_msgr_info,
std::ptr::null(),
&mut debug_messenger,
)
};
if let Err(e) = check(res) {
let _ = unsafe { Box::from_raw(leaked) };
if let Some(destroy) = dispatch.vkDestroyInstance {
unsafe { destroy(handle, std::ptr::null()) };
}
return Err(e);
}
}
Ok(Self {
inner: Arc::new(InstanceInner {
library,
handle,
dispatch,
debug_messenger,
debug_callback_box,
}),
})
}
pub fn raw(&self) -> VkInstance {
self.inner.handle
}
pub fn enumerate_physical_device_groups(
&self,
) -> Result<Vec<crate::safe::PhysicalDeviceGroup>> {
let enumerate = self
.inner
.dispatch
.vkEnumeratePhysicalDeviceGroups
.ok_or(Error::MissingFunction("vkEnumeratePhysicalDeviceGroups"))?;
let mut count: u32 = 0;
check(unsafe { enumerate(self.inner.handle, &mut count, std::ptr::null_mut()) })?;
let mut raw: Vec<VkPhysicalDeviceGroupProperties> = (0..count as usize)
.map(|_| VkPhysicalDeviceGroupProperties {
sType: VkStructureType::STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES,
..Default::default()
})
.collect();
check(unsafe { enumerate(self.inner.handle, &mut count, raw.as_mut_ptr()) })?;
Ok(raw
.into_iter()
.map(|r| {
let physical_devices: Vec<PhysicalDevice> = (0..r.physicalDeviceCount as usize)
.map(|i| PhysicalDevice::new(Arc::clone(&self.inner), r.physicalDevices[i]))
.collect();
crate::safe::PhysicalDeviceGroup {
instance: Arc::clone(&self.inner),
physical_devices,
subset_allocation: r.subsetAllocation != 0,
}
})
.collect())
}
pub fn enumerate_physical_devices(&self) -> Result<Vec<PhysicalDevice>> {
let enumerate = self
.inner
.dispatch
.vkEnumeratePhysicalDevices
.ok_or(Error::MissingFunction("vkEnumeratePhysicalDevices"))?;
let mut count: u32 = 0;
check(unsafe { enumerate(self.inner.handle, &mut count, std::ptr::null_mut()) })?;
let mut handles: Vec<VkPhysicalDevice> = vec![std::ptr::null_mut(); count as usize];
check(unsafe { enumerate(self.inner.handle, &mut count, handles.as_mut_ptr()) })?;
Ok(handles
.into_iter()
.map(|h| PhysicalDevice::new(Arc::clone(&self.inner), h))
.collect())
}
}