1 use crate::instance::Instance; 2 use crate::prelude::*; 3 use crate::vk; 4 use crate::RawPtr; 5 use std::ffi::CStr; 6 #[cfg(feature = "loaded")] 7 use std::ffi::OsStr; 8 use std::mem; 9 use std::os::raw::c_char; 10 use std::os::raw::c_void; 11 use std::ptr; 12 #[cfg(feature = "loaded")] 13 use std::sync::Arc; 14 15 #[cfg(feature = "loaded")] 16 use libloading::Library; 17 18 /// Holds the Vulkan functions independent of a particular instance 19 #[derive(Clone)] 20 pub struct Entry { 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 entry_fn_1_3: vk::EntryFnV1_3, 26 #[cfg(feature = "loaded")] 27 _lib_guard: Option<Arc<Library>>, 28 } 29 30 /// Vulkan core 1.0 31 #[allow(non_camel_case_types)] 32 impl Entry { 33 /// Load default Vulkan library for the current platform 34 /// 35 /// Prefer this over [`linked`](Self::linked) when your application can gracefully handle 36 /// environments that lack Vulkan support, and when the build environment might not have Vulkan 37 /// development packages installed (e.g. the Vulkan SDK, or Ubuntu's `libvulkan-dev`). 38 /// 39 /// # Safety 40 /// 41 /// `dlopen`ing native libraries is inherently unsafe. The safety guidelines 42 /// for [`Library::new()`] and [`Library::get()`] apply here. 43 /// 44 /// No Vulkan functions loaded directly or indirectly from this [`Entry`] 45 /// may be called after it is [dropped][drop()]. 46 /// 47 /// # Example 48 /// 49 /// ```no_run 50 /// use ash::{vk, Entry}; 51 /// # fn main() -> Result<(), Box<dyn std::error::Error>> { 52 /// let entry = unsafe { Entry::load()? }; 53 /// let app_info = vk::ApplicationInfo { 54 /// api_version: vk::make_api_version(0, 1, 0, 0), 55 /// ..Default::default() 56 /// }; 57 /// let create_info = vk::InstanceCreateInfo { 58 /// p_application_info: &app_info, 59 /// ..Default::default() 60 /// }; 61 /// let instance = unsafe { entry.create_instance(&create_info, None)? }; 62 /// # Ok(()) } 63 /// ``` 64 #[cfg(feature = "loaded")] 65 #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))] load() -> Result<Self, LoadingError>66 pub unsafe fn load() -> Result<Self, LoadingError> { 67 #[cfg(windows)] 68 const LIB_PATH: &str = "vulkan-1.dll"; 69 70 #[cfg(all( 71 unix, 72 not(any(target_os = "macos", target_os = "ios", target_os = "android")) 73 ))] 74 const LIB_PATH: &str = "libvulkan.so.1"; 75 76 #[cfg(target_os = "android")] 77 const LIB_PATH: &str = "libvulkan.so"; 78 79 #[cfg(any(target_os = "macos", target_os = "ios"))] 80 const LIB_PATH: &str = "libvulkan.dylib"; 81 82 Self::load_from(LIB_PATH) 83 } 84 85 /// Load entry points from a Vulkan loader linked at compile time 86 /// 87 /// Compared to [`load`](Self::load), this is infallible, but requires that the build 88 /// environment have Vulkan development packages installed (e.g. the Vulkan SDK, or Ubuntu's 89 /// `libvulkan-dev`), and prevents the resulting binary from starting in environments that do not 90 /// support Vulkan. 91 /// 92 /// Note that instance/device functions are still fetched via `vkGetInstanceProcAddr` and 93 /// `vkGetDeviceProcAddr` for maximum performance. 94 /// 95 /// Any Vulkan function acquired directly or indirectly from this [`Entry`] may be called after it 96 /// is [dropped][drop()]. 97 /// 98 /// # Example 99 /// 100 /// ```no_run 101 /// use ash::{vk, Entry}; 102 /// # fn main() -> Result<(), Box<dyn std::error::Error>> { 103 /// let entry = Entry::linked(); 104 /// let app_info = vk::ApplicationInfo { 105 /// api_version: vk::make_api_version(0, 1, 0, 0), 106 /// ..Default::default() 107 /// }; 108 /// let create_info = vk::InstanceCreateInfo { 109 /// p_application_info: &app_info, 110 /// ..Default::default() 111 /// }; 112 /// let instance = unsafe { entry.create_instance(&create_info, None)? }; 113 /// # Ok(()) } 114 /// ``` 115 #[cfg(feature = "linked")] 116 #[cfg_attr(docsrs, doc(cfg(feature = "linked")))] linked() -> Self117 pub fn linked() -> Self { 118 // Sound because we're linking to Vulkan, which provides a vkGetInstanceProcAddr that has 119 // defined behavior in this use. 120 unsafe { 121 Self::from_static_fn(vk::StaticFn { 122 get_instance_proc_addr: vkGetInstanceProcAddr, 123 }) 124 } 125 } 126 127 /// Load Vulkan library at `path` 128 /// 129 /// # Safety 130 /// 131 /// `dlopen`ing native libraries is inherently unsafe. The safety guidelines 132 /// for [`Library::new()`] and [`Library::get()`] apply here. 133 /// 134 /// No Vulkan functions loaded directly or indirectly from this [`Entry`] 135 /// may be called after it is [dropped][drop()]. 136 #[cfg(feature = "loaded")] 137 #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))] load_from(path: impl AsRef<OsStr>) -> Result<Self, LoadingError>138 pub unsafe fn load_from(path: impl AsRef<OsStr>) -> Result<Self, LoadingError> { 139 let lib = Library::new(path) 140 .map_err(LoadingError::LibraryLoadFailure) 141 .map(Arc::new)?; 142 143 let static_fn = vk::StaticFn::load_checked(|name| { 144 lib.get(name.to_bytes_with_nul()) 145 .map(|symbol| *symbol) 146 .unwrap_or(ptr::null_mut()) 147 })?; 148 149 Ok(Self { 150 _lib_guard: Some(lib), 151 ..Self::from_static_fn(static_fn) 152 }) 153 } 154 155 /// Load entry points based on an already-loaded [`vk::StaticFn`] 156 /// 157 /// # Safety 158 /// 159 /// `static_fn` must contain valid function pointers that comply with the semantics specified 160 /// by Vulkan 1.0, which must remain valid for at least the lifetime of the returned [`Entry`]. from_static_fn(static_fn: vk::StaticFn) -> Self161 pub unsafe fn from_static_fn(static_fn: vk::StaticFn) -> Self { 162 let load_fn = |name: &std::ffi::CStr| { 163 mem::transmute((static_fn.get_instance_proc_addr)( 164 vk::Instance::null(), 165 name.as_ptr(), 166 )) 167 }; 168 let entry_fn_1_0 = vk::EntryFnV1_0::load(load_fn); 169 let entry_fn_1_1 = vk::EntryFnV1_1::load(load_fn); 170 let entry_fn_1_2 = vk::EntryFnV1_2::load(load_fn); 171 let entry_fn_1_3 = vk::EntryFnV1_3::load(load_fn); 172 173 Self { 174 static_fn, 175 entry_fn_1_0, 176 entry_fn_1_1, 177 entry_fn_1_2, 178 entry_fn_1_3, 179 #[cfg(feature = "loaded")] 180 _lib_guard: None, 181 } 182 } 183 184 #[inline] fp_v1_0(&self) -> &vk::EntryFnV1_0185 pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 { 186 &self.entry_fn_1_0 187 } 188 189 #[inline] static_fn(&self) -> &vk::StaticFn190 pub fn static_fn(&self) -> &vk::StaticFn { 191 &self.static_fn 192 } 193 194 /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceVersion.html> 195 /// 196 /// # Example 197 /// 198 /// ```no_run 199 /// # use ash::{Entry, vk}; 200 /// # fn main() -> Result<(), Box<dyn std::error::Error>> { 201 /// let entry = Entry::linked(); 202 /// match entry.try_enumerate_instance_version()? { 203 /// // Vulkan 1.1+ 204 /// Some(version) => { 205 /// let major = vk::version_major(version); 206 /// let minor = vk::version_minor(version); 207 /// let patch = vk::version_patch(version); 208 /// }, 209 /// // Vulkan 1.0 210 /// None => {}, 211 /// } 212 /// # Ok(()) } 213 /// ``` 214 #[inline] try_enumerate_instance_version(&self) -> VkResult<Option<u32>>215 pub fn try_enumerate_instance_version(&self) -> VkResult<Option<u32>> { 216 unsafe { 217 let mut api_version = 0; 218 let enumerate_instance_version: Option<vk::PFN_vkEnumerateInstanceVersion> = { 219 let name = ::std::ffi::CStr::from_bytes_with_nul_unchecked( 220 b"vkEnumerateInstanceVersion\0", 221 ); 222 mem::transmute((self.static_fn.get_instance_proc_addr)( 223 vk::Instance::null(), 224 name.as_ptr(), 225 )) 226 }; 227 if let Some(enumerate_instance_version) = enumerate_instance_version { 228 (enumerate_instance_version)(&mut api_version) 229 .result_with_success(Some(api_version)) 230 } else { 231 Ok(None) 232 } 233 } 234 } 235 236 /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCreateInstance.html> 237 /// 238 /// # Safety 239 /// 240 /// The resulting [`Instance`] and any function-pointer objects (e.g. [`Device`][crate::Device] 241 /// and [extensions][crate::extensions]) loaded from it may not be used after this [`Entry`] 242 /// object is dropped, unless it was crated using [`Entry::linked()`]. 243 /// 244 /// [`Instance`] does _not_ implement [drop][drop()] semantics and can only be destroyed via 245 /// [`destroy_instance()`][Instance::destroy_instance()]. 246 #[inline] create_instance( &self, create_info: &vk::InstanceCreateInfo, allocation_callbacks: Option<&vk::AllocationCallbacks>, ) -> VkResult<Instance>247 pub unsafe fn create_instance( 248 &self, 249 create_info: &vk::InstanceCreateInfo, 250 allocation_callbacks: Option<&vk::AllocationCallbacks>, 251 ) -> VkResult<Instance> { 252 let mut instance = mem::zeroed(); 253 (self.entry_fn_1_0.create_instance)( 254 create_info, 255 allocation_callbacks.as_raw_ptr(), 256 &mut instance, 257 ) 258 .result()?; 259 Ok(Instance::load(&self.static_fn, instance)) 260 } 261 262 /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceLayerProperties.html> 263 #[inline] enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>>264 pub fn enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>> { 265 unsafe { 266 read_into_uninitialized_vector(|count, data| { 267 (self.entry_fn_1_0.enumerate_instance_layer_properties)(count, data) 268 }) 269 } 270 } 271 272 /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html> 273 #[inline] enumerate_instance_extension_properties( &self, layer_name: Option<&CStr>, ) -> VkResult<Vec<vk::ExtensionProperties>>274 pub fn enumerate_instance_extension_properties( 275 &self, 276 layer_name: Option<&CStr>, 277 ) -> VkResult<Vec<vk::ExtensionProperties>> { 278 unsafe { 279 read_into_uninitialized_vector(|count, data| { 280 (self.entry_fn_1_0.enumerate_instance_extension_properties)( 281 layer_name.map_or(ptr::null(), |str| str.as_ptr()), 282 count, 283 data, 284 ) 285 }) 286 } 287 } 288 289 /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html> 290 #[inline] get_instance_proc_addr( &self, instance: vk::Instance, p_name: *const c_char, ) -> vk::PFN_vkVoidFunction291 pub unsafe fn get_instance_proc_addr( 292 &self, 293 instance: vk::Instance, 294 p_name: *const c_char, 295 ) -> vk::PFN_vkVoidFunction { 296 (self.static_fn.get_instance_proc_addr)(instance, p_name) 297 } 298 } 299 300 /// Vulkan core 1.1 301 #[allow(non_camel_case_types)] 302 impl Entry { 303 #[inline] fp_v1_1(&self) -> &vk::EntryFnV1_1304 pub fn fp_v1_1(&self) -> &vk::EntryFnV1_1 { 305 &self.entry_fn_1_1 306 } 307 308 #[deprecated = "This function is unavailable and therefore panics on Vulkan 1.0, please use `try_enumerate_instance_version()` instead"] 309 /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceVersion.html> 310 /// 311 /// Please use [`try_enumerate_instance_version()`][Self::try_enumerate_instance_version()] instead. 312 #[inline] enumerate_instance_version(&self) -> VkResult<u32>313 pub fn enumerate_instance_version(&self) -> VkResult<u32> { 314 unsafe { 315 let mut api_version = 0; 316 (self.entry_fn_1_1.enumerate_instance_version)(&mut api_version) 317 .result_with_success(api_version) 318 } 319 } 320 } 321 322 /// Vulkan core 1.2 323 #[allow(non_camel_case_types)] 324 impl Entry { 325 #[inline] fp_v1_2(&self) -> &vk::EntryFnV1_2326 pub fn fp_v1_2(&self) -> &vk::EntryFnV1_2 { 327 &self.entry_fn_1_2 328 } 329 } 330 331 /// Vulkan core 1.3 332 #[allow(non_camel_case_types)] 333 impl Entry { 334 #[inline] fp_v1_3(&self) -> &vk::EntryFnV1_3335 pub fn fp_v1_3(&self) -> &vk::EntryFnV1_3 { 336 &self.entry_fn_1_3 337 } 338 } 339 340 #[cfg(feature = "linked")] 341 #[cfg_attr(docsrs, doc(cfg(feature = "linked")))] 342 impl Default for Entry { 343 #[inline] default() -> Self344 fn default() -> Self { 345 Self::linked() 346 } 347 } 348 349 impl vk::StaticFn { load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint> where F: FnMut(&::std::ffi::CStr) -> *const c_void,350 pub fn load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint> 351 where 352 F: FnMut(&::std::ffi::CStr) -> *const c_void, 353 { 354 // TODO: Make this a &'static CStr once CStr::from_bytes_with_nul_unchecked is const 355 static ENTRY_POINT: &[u8] = b"vkGetInstanceProcAddr\0"; 356 357 Ok(Self { 358 get_instance_proc_addr: unsafe { 359 let cname = CStr::from_bytes_with_nul_unchecked(ENTRY_POINT); 360 let val = _f(cname); 361 if val.is_null() { 362 return Err(MissingEntryPoint); 363 } else { 364 ::std::mem::transmute(val) 365 } 366 }, 367 }) 368 } 369 } 370 371 #[derive(Clone, Debug)] 372 pub struct MissingEntryPoint; 373 impl std::fmt::Display for MissingEntryPoint { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { 375 write!(f, "Cannot load `vkGetInstanceProcAddr` symbol from library") 376 } 377 } 378 impl std::error::Error for MissingEntryPoint {} 379 380 #[cfg(feature = "linked")] 381 extern "system" { vkGetInstanceProcAddr(instance: vk::Instance, name: *const c_char) -> vk::PFN_vkVoidFunction382 fn vkGetInstanceProcAddr(instance: vk::Instance, name: *const c_char) 383 -> vk::PFN_vkVoidFunction; 384 } 385 386 #[cfg(feature = "loaded")] 387 mod loaded { 388 use std::error::Error; 389 use std::fmt; 390 391 use super::*; 392 393 #[derive(Debug)] 394 #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))] 395 pub enum LoadingError { 396 LibraryLoadFailure(libloading::Error), 397 MissingEntryPoint(MissingEntryPoint), 398 } 399 400 impl fmt::Display for LoadingError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result401 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 402 match self { 403 Self::LibraryLoadFailure(err) => fmt::Display::fmt(err, f), 404 Self::MissingEntryPoint(err) => fmt::Display::fmt(err, f), 405 } 406 } 407 } 408 409 impl Error for LoadingError { source(&self) -> Option<&(dyn Error + 'static)>410 fn source(&self) -> Option<&(dyn Error + 'static)> { 411 Some(match self { 412 Self::LibraryLoadFailure(err) => err, 413 Self::MissingEntryPoint(err) => err, 414 }) 415 } 416 } 417 418 impl From<MissingEntryPoint> for LoadingError { from(err: MissingEntryPoint) -> Self419 fn from(err: MissingEntryPoint) -> Self { 420 Self::MissingEntryPoint(err) 421 } 422 } 423 } 424 #[cfg(feature = "loaded")] 425 pub use self::loaded::*; 426