1 use crate::instance::Instance; 2 use crate::prelude::*; 3 use crate::vk; 4 use crate::RawPtr; 5 use std::error::Error; 6 use std::ffi::CStr; 7 use std::fmt; 8 use std::mem; 9 use std::os::raw::c_char; 10 use std::os::raw::c_void; 11 use std::ptr; 12 13 /// Holds a custom type `L` to load symbols from (usually a handle to a `dlopen`ed library), 14 /// the [`vkGetInstanceProcAddr`][vk::StaticFn::get_instance_proc_addr()] loader function from 15 /// this library (in [`vk::StaticFn`]), and Vulkan's "entry point" functions (resolved with `NULL` 16 /// `instance`) as listed in [`vkGetInstanceProcAddr`'s description]. 17 /// 18 /// [`vkGetInstanceProcAddr`'s description]: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html#_description 19 #[derive(Clone)] 20 pub struct EntryCustom<L> { 21 static_fn: vk::StaticFn, 22 entry_fn_1_0: vk::EntryFnV1_0, 23 entry_fn_1_1: vk::EntryFnV1_1, 24 entry_fn_1_2: vk::EntryFnV1_2, 25 lib: L, 26 } 27 28 /// Vulkan core 1.0 29 #[allow(non_camel_case_types)] 30 impl<L> EntryCustom<L> { new_custom<Load>( mut lib: L, mut load: Load, ) -> std::result::Result<Self, MissingEntryPoint> where Load: FnMut(&mut L, &::std::ffi::CStr) -> *const c_void,31 pub fn new_custom<Load>( 32 mut lib: L, 33 mut load: Load, 34 ) -> std::result::Result<Self, MissingEntryPoint> 35 where 36 Load: FnMut(&mut L, &::std::ffi::CStr) -> *const c_void, 37 { 38 // Bypass the normal StaticFn::load so we can return an error 39 let static_fn = vk::StaticFn::load_checked(|name| load(&mut lib, name))?; 40 let load_fn = |name: &std::ffi::CStr| unsafe { 41 mem::transmute(static_fn.get_instance_proc_addr(vk::Instance::null(), name.as_ptr())) 42 }; 43 let entry_fn_1_0 = vk::EntryFnV1_0::load(load_fn); 44 let entry_fn_1_1 = vk::EntryFnV1_1::load(load_fn); 45 let entry_fn_1_2 = vk::EntryFnV1_2::load(load_fn); 46 47 Ok(EntryCustom { 48 static_fn, 49 entry_fn_1_0, 50 entry_fn_1_1, 51 entry_fn_1_2, 52 lib, 53 }) 54 } 55 fp_v1_0(&self) -> &vk::EntryFnV1_056 pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 { 57 &self.entry_fn_1_0 58 } 59 static_fn(&self) -> &vk::StaticFn60 pub fn static_fn(&self) -> &vk::StaticFn { 61 &self.static_fn 62 } 63 64 #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceVersion.html>"] 65 /// ```no_run 66 /// # use ash::{Entry, vk}; 67 /// # fn main() -> Result<(), Box<dyn std::error::Error>> { 68 /// let entry = unsafe { Entry::new() }?; 69 /// match entry.try_enumerate_instance_version()? { 70 /// // Vulkan 1.1+ 71 /// Some(version) => { 72 /// let major = vk::version_major(version); 73 /// let minor = vk::version_minor(version); 74 /// let patch = vk::version_patch(version); 75 /// }, 76 /// // Vulkan 1.0 77 /// None => {}, 78 /// } 79 /// # Ok(()) } 80 /// ``` try_enumerate_instance_version(&self) -> VkResult<Option<u32>>81 pub fn try_enumerate_instance_version(&self) -> VkResult<Option<u32>> { 82 unsafe { 83 let mut api_version = 0; 84 let enumerate_instance_version: Option<vk::PFN_vkEnumerateInstanceVersion> = { 85 let name = b"vkEnumerateInstanceVersion\0".as_ptr() as *const _; 86 mem::transmute( 87 self.static_fn 88 .get_instance_proc_addr(vk::Instance::null(), name), 89 ) 90 }; 91 if let Some(enumerate_instance_version) = enumerate_instance_version { 92 (enumerate_instance_version)(&mut api_version) 93 .result_with_success(Some(api_version)) 94 } else { 95 Ok(None) 96 } 97 } 98 } 99 100 #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateInstance.html>"] 101 /// 102 /// # Safety 103 /// In order for the created [`Instance`] to be valid for the duration of its 104 /// usage, the [`Entry`](Self) this was called on must be dropped later than the 105 /// resulting [`Instance`]. create_instance( &self, create_info: &vk::InstanceCreateInfo, allocation_callbacks: Option<&vk::AllocationCallbacks>, ) -> Result<Instance, InstanceError>106 pub unsafe fn create_instance( 107 &self, 108 create_info: &vk::InstanceCreateInfo, 109 allocation_callbacks: Option<&vk::AllocationCallbacks>, 110 ) -> Result<Instance, InstanceError> { 111 let mut instance = mem::zeroed(); 112 self.entry_fn_1_0 113 .create_instance( 114 create_info, 115 allocation_callbacks.as_raw_ptr(), 116 &mut instance, 117 ) 118 .result() 119 .map_err(InstanceError::VkError)?; 120 Ok(Instance::load(&self.static_fn, instance)) 121 } 122 123 #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceLayerProperties.html>"] enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>>124 pub fn enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>> { 125 unsafe { 126 read_into_uninitialized_vector(|count, data| { 127 self.entry_fn_1_0 128 .enumerate_instance_layer_properties(count, data) 129 }) 130 } 131 } 132 133 #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>"] enumerate_instance_extension_properties( &self, ) -> VkResult<Vec<vk::ExtensionProperties>>134 pub fn enumerate_instance_extension_properties( 135 &self, 136 ) -> VkResult<Vec<vk::ExtensionProperties>> { 137 unsafe { 138 read_into_uninitialized_vector(|count, data| { 139 self.entry_fn_1_0 140 .enumerate_instance_extension_properties(ptr::null(), count, data) 141 }) 142 } 143 } 144 145 #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html>"] get_instance_proc_addr( &self, instance: vk::Instance, p_name: *const c_char, ) -> vk::PFN_vkVoidFunction146 pub unsafe fn get_instance_proc_addr( 147 &self, 148 instance: vk::Instance, 149 p_name: *const c_char, 150 ) -> vk::PFN_vkVoidFunction { 151 self.static_fn.get_instance_proc_addr(instance, p_name) 152 } 153 } 154 155 /// Vulkan core 1.1 156 #[allow(non_camel_case_types)] 157 impl<L> EntryCustom<L> { fp_v1_1(&self) -> &vk::EntryFnV1_1158 pub fn fp_v1_1(&self) -> &vk::EntryFnV1_1 { 159 &self.entry_fn_1_1 160 } 161 162 #[deprecated = "This function is unavailable and therefore panics on Vulkan 1.0, please use `try_enumerate_instance_version` instead"] 163 #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceVersion.html>"] 164 /// 165 /// Please use [`Self::try_enumerate_instance_version`] instead. enumerate_instance_version(&self) -> VkResult<u32>166 pub fn enumerate_instance_version(&self) -> VkResult<u32> { 167 unsafe { 168 let mut api_version = 0; 169 self.entry_fn_1_1 170 .enumerate_instance_version(&mut api_version) 171 .result_with_success(api_version) 172 } 173 } 174 } 175 176 /// Vulkan core 1.2 177 #[allow(non_camel_case_types)] 178 impl<L> EntryCustom<L> { fp_v1_2(&self) -> &vk::EntryFnV1_2179 pub fn fp_v1_2(&self) -> &vk::EntryFnV1_2 { 180 &self.entry_fn_1_2 181 } 182 } 183 184 #[derive(Clone, Debug)] 185 pub enum InstanceError { 186 LoadError(Vec<&'static str>), 187 VkError(vk::Result), 188 } 189 190 impl fmt::Display for InstanceError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result191 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 192 match self { 193 InstanceError::LoadError(e) => write!(f, "{}", e.join("; ")), 194 InstanceError::VkError(e) => write!(f, "{}", e), 195 } 196 } 197 } 198 199 impl Error for InstanceError {} 200 201 impl vk::StaticFn { load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint> where F: FnMut(&::std::ffi::CStr) -> *const c_void,202 pub fn load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint> 203 where 204 F: FnMut(&::std::ffi::CStr) -> *const c_void, 205 { 206 // TODO: Make this a &'static CStr once CStr::from_bytes_with_nul_unchecked is const 207 static ENTRY_POINT: &[u8] = b"vkGetInstanceProcAddr\0"; 208 209 Ok(vk::StaticFn { 210 get_instance_proc_addr: unsafe { 211 let cname = CStr::from_bytes_with_nul_unchecked(ENTRY_POINT); 212 let val = _f(cname); 213 if val.is_null() { 214 return Err(MissingEntryPoint); 215 } else { 216 ::std::mem::transmute(val) 217 } 218 }, 219 }) 220 } 221 } 222 223 #[derive(Clone, Debug)] 224 pub struct MissingEntryPoint; 225 impl std::fmt::Display for MissingEntryPoint { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { 227 write!(f, "Cannot load `vkGetInstanceProcAddr` symbol from library") 228 } 229 } 230 impl std::error::Error for MissingEntryPoint {} 231