1 // Copyright (c) 2016 The vulkano developers 2 // Licensed under the Apache License, Version 2.0 3 // <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT 5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, 6 // at your option. All files in the project carrying such 7 // notice may not be copied, modified, or distributed except 8 // according to those terms. 9 10 //! Vulkan library loading system. 11 //! 12 //! Before Vulkano can do anything, it first needs to find a library containing an implementation 13 //! of Vulkan. A Vulkan implementation is defined as a single `vkGetInstanceProcAddr` function, 14 //! which can be accessed through the `Loader` trait. 15 //! 16 //! This module provides various implementations of the `Loader` trait. 17 //! 18 //! Once you have a type that implements `Loader`, you can create a `VulkanLibrary` 19 //! from it and use this `VulkanLibrary` struct to build an `Instance`. 20 21 pub use crate::fns::EntryFunctions; 22 use crate::{ 23 instance::{InstanceExtensions, LayerProperties}, 24 ExtensionProperties, OomError, SafeDeref, Version, VulkanError, 25 }; 26 use libloading::{Error as LibloadingError, Library}; 27 use std::{ 28 error::Error, 29 ffi::{CStr, CString}, 30 fmt::{Debug, Display, Error as FmtError, Formatter}, 31 mem::transmute, 32 os::raw::c_char, 33 path::Path, 34 ptr, 35 sync::Arc, 36 }; 37 38 /// A loaded library containing a valid Vulkan implementation. 39 #[derive(Debug)] 40 pub struct VulkanLibrary { 41 loader: Box<dyn Loader>, 42 fns: EntryFunctions, 43 44 api_version: Version, 45 extension_properties: Vec<ExtensionProperties>, 46 supported_extensions: InstanceExtensions, 47 } 48 49 impl VulkanLibrary { 50 /// Loads the default Vulkan library for this system. new() -> Result<Arc<Self>, LoadingError>51 pub fn new() -> Result<Arc<Self>, LoadingError> { 52 #[cfg(target_os = "ios")] 53 #[allow(non_snake_case)] 54 fn def_loader_impl() -> Result<Box<dyn Loader>, LoadingError> { 55 let loader = crate::statically_linked_vulkan_loader!(); 56 57 Ok(Box::new(loader)) 58 } 59 60 #[cfg(not(target_os = "ios"))] 61 fn def_loader_impl() -> Result<Box<dyn Loader>, LoadingError> { 62 #[cfg(windows)] 63 fn get_path() -> &'static Path { 64 Path::new("vulkan-1.dll") 65 } 66 #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] 67 fn get_path() -> &'static Path { 68 Path::new("libvulkan.so.1") 69 } 70 #[cfg(target_os = "macos")] 71 fn get_path() -> &'static Path { 72 Path::new("libvulkan.1.dylib") 73 } 74 #[cfg(target_os = "android")] 75 fn get_path() -> &'static Path { 76 Path::new("libvulkan.so") 77 } 78 79 let loader = unsafe { DynamicLibraryLoader::new(get_path())? }; 80 81 Ok(Box::new(loader)) 82 } 83 84 def_loader_impl().and_then(VulkanLibrary::with_loader) 85 } 86 87 /// Loads a custom Vulkan library. with_loader(loader: impl Loader + 'static) -> Result<Arc<Self>, LoadingError>88 pub fn with_loader(loader: impl Loader + 'static) -> Result<Arc<Self>, LoadingError> { 89 let fns = EntryFunctions::load(|name| unsafe { 90 loader 91 .get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr()) 92 .map_or(ptr::null(), |func| func as _) 93 }); 94 95 let api_version = unsafe { Self::get_api_version(&loader)? }; 96 let extension_properties = unsafe { Self::get_extension_properties(&fns, None)? }; 97 let supported_extensions = extension_properties 98 .iter() 99 .map(|property| property.extension_name.as_str()) 100 .collect(); 101 102 Ok(Arc::new(VulkanLibrary { 103 loader: Box::new(loader), 104 fns, 105 api_version, 106 extension_properties, 107 supported_extensions, 108 })) 109 } 110 get_api_version(loader: &impl Loader) -> Result<Version, VulkanError>111 unsafe fn get_api_version(loader: &impl Loader) -> Result<Version, VulkanError> { 112 // Per the Vulkan spec: 113 // If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a 114 // Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion 115 // to determine the version of Vulkan. 116 117 let name = CStr::from_bytes_with_nul_unchecked(b"vkEnumerateInstanceVersion\0"); 118 let func = loader.get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr()); 119 120 let version = if let Some(func) = func { 121 let func: ash::vk::PFN_vkEnumerateInstanceVersion = transmute(func); 122 let mut api_version = 0; 123 func(&mut api_version).result().map_err(VulkanError::from)?; 124 Version::from(api_version) 125 } else { 126 Version { 127 major: 1, 128 minor: 0, 129 patch: 0, 130 } 131 }; 132 133 Ok(version) 134 } 135 get_extension_properties( fns: &EntryFunctions, layer: Option<&str>, ) -> Result<Vec<ExtensionProperties>, VulkanError>136 unsafe fn get_extension_properties( 137 fns: &EntryFunctions, 138 layer: Option<&str>, 139 ) -> Result<Vec<ExtensionProperties>, VulkanError> { 140 let layer_vk = layer.map(|layer| CString::new(layer).unwrap()); 141 142 loop { 143 let mut count = 0; 144 (fns.v1_0.enumerate_instance_extension_properties)( 145 layer_vk 146 .as_ref() 147 .map_or(ptr::null(), |layer| layer.as_ptr()), 148 &mut count, 149 ptr::null_mut(), 150 ) 151 .result() 152 .map_err(VulkanError::from)?; 153 154 let mut output = Vec::with_capacity(count as usize); 155 let result = (fns.v1_0.enumerate_instance_extension_properties)( 156 layer_vk 157 .as_ref() 158 .map_or(ptr::null(), |layer| layer.as_ptr()), 159 &mut count, 160 output.as_mut_ptr(), 161 ); 162 163 match result { 164 ash::vk::Result::SUCCESS => { 165 output.set_len(count as usize); 166 return Ok(output.into_iter().map(Into::into).collect()); 167 } 168 ash::vk::Result::INCOMPLETE => (), 169 err => return Err(VulkanError::from(err)), 170 } 171 } 172 } 173 174 /// Returns pointers to the raw global Vulkan functions of the library. 175 #[inline] fns(&self) -> &EntryFunctions176 pub fn fns(&self) -> &EntryFunctions { 177 &self.fns 178 } 179 180 /// Returns the highest Vulkan version that is supported for instances. 181 #[inline] api_version(&self) -> Version182 pub fn api_version(&self) -> Version { 183 self.api_version 184 } 185 186 /// Returns the extension properties reported by the core library. 187 #[inline] extension_properties(&self) -> &[ExtensionProperties]188 pub fn extension_properties(&self) -> &[ExtensionProperties] { 189 &self.extension_properties 190 } 191 192 /// Returns the extensions that are supported by the core library. 193 #[inline] supported_extensions(&self) -> &InstanceExtensions194 pub fn supported_extensions(&self) -> &InstanceExtensions { 195 &self.supported_extensions 196 } 197 198 /// Returns the list of layers that are available when creating an instance. 199 /// 200 /// On success, this function returns an iterator that produces 201 /// [`LayerProperties`](crate::instance::LayerProperties) objects. In order to enable a layer, 202 /// you need to pass its name (returned by `LayerProperties::name()`) when creating the 203 /// [`Instance`](crate::instance::Instance). 204 /// 205 /// > **Note**: The available layers may change between successive calls to this function, so 206 /// > each call may return different results. It is possible that one of the layers enumerated 207 /// > here is no longer available when you create the `Instance`. This will lead to an error 208 /// > when calling `Instance::new`. 209 /// 210 /// # Examples 211 /// 212 /// ```no_run 213 /// use vulkano::VulkanLibrary; 214 /// 215 /// let library = VulkanLibrary::new().unwrap(); 216 /// 217 /// for layer in library.layer_properties().unwrap() { 218 /// println!("Available layer: {}", layer.name()); 219 /// } 220 /// ``` layer_properties( &self, ) -> Result<impl ExactSizeIterator<Item = LayerProperties>, OomError>221 pub fn layer_properties( 222 &self, 223 ) -> Result<impl ExactSizeIterator<Item = LayerProperties>, OomError> { 224 let fns = self.fns(); 225 226 let layer_properties = unsafe { 227 loop { 228 let mut count = 0; 229 (fns.v1_0.enumerate_instance_layer_properties)(&mut count, ptr::null_mut()) 230 .result() 231 .map_err(VulkanError::from)?; 232 233 let mut properties = Vec::with_capacity(count as usize); 234 let result = (fns.v1_0.enumerate_instance_layer_properties)( 235 &mut count, 236 properties.as_mut_ptr(), 237 ); 238 239 match result { 240 ash::vk::Result::SUCCESS => { 241 properties.set_len(count as usize); 242 break properties; 243 } 244 ash::vk::Result::INCOMPLETE => (), 245 err => return Err(VulkanError::from(err).into()), 246 } 247 } 248 }; 249 250 Ok(layer_properties 251 .into_iter() 252 .map(|p| LayerProperties { props: p })) 253 } 254 255 /// Returns the extension properties that are reported by the given layer. 256 #[inline] layer_extension_properties( &self, layer: &str, ) -> Result<Vec<ExtensionProperties>, VulkanError>257 pub fn layer_extension_properties( 258 &self, 259 layer: &str, 260 ) -> Result<Vec<ExtensionProperties>, VulkanError> { 261 unsafe { Self::get_extension_properties(&self.fns, Some(layer)) } 262 } 263 264 /// Returns the extensions that are supported by the given layer. 265 #[inline] supported_layer_extensions( &self, layer: &str, ) -> Result<InstanceExtensions, VulkanError>266 pub fn supported_layer_extensions( 267 &self, 268 layer: &str, 269 ) -> Result<InstanceExtensions, VulkanError> { 270 Ok(self 271 .layer_extension_properties(layer)? 272 .iter() 273 .map(|property| property.extension_name.as_str()) 274 .collect()) 275 } 276 277 /// Returns the union of the extensions that are supported by the core library and all 278 /// the given layers. 279 #[inline] supported_extensions_with_layers<'a>( &self, layers: impl IntoIterator<Item = &'a str>, ) -> Result<InstanceExtensions, VulkanError>280 pub fn supported_extensions_with_layers<'a>( 281 &self, 282 layers: impl IntoIterator<Item = &'a str>, 283 ) -> Result<InstanceExtensions, VulkanError> { 284 layers 285 .into_iter() 286 .try_fold(self.supported_extensions, |extensions, layer| { 287 self.supported_layer_extensions(layer) 288 .map(|layer_extensions| extensions.union(&layer_extensions)) 289 }) 290 } 291 292 /// Calls `get_instance_proc_addr` on the underlying loader. 293 #[inline] get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction294 pub unsafe fn get_instance_proc_addr( 295 &self, 296 instance: ash::vk::Instance, 297 name: *const c_char, 298 ) -> ash::vk::PFN_vkVoidFunction { 299 self.loader.get_instance_proc_addr(instance, name) 300 } 301 } 302 303 /// Implemented on objects that grant access to a Vulkan implementation. 304 pub unsafe trait Loader: Send + Sync { 305 /// Calls the `vkGetInstanceProcAddr` function. The parameters are the same. 306 /// 307 /// The returned function must stay valid for as long as `self` is alive. get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction308 unsafe fn get_instance_proc_addr( 309 &self, 310 instance: ash::vk::Instance, 311 name: *const c_char, 312 ) -> ash::vk::PFN_vkVoidFunction; 313 } 314 315 unsafe impl<T> Loader for T 316 where 317 T: SafeDeref + Send + Sync, 318 T::Target: Loader, 319 { get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction320 unsafe fn get_instance_proc_addr( 321 &self, 322 instance: ash::vk::Instance, 323 name: *const c_char, 324 ) -> ash::vk::PFN_vkVoidFunction { 325 (**self).get_instance_proc_addr(instance, name) 326 } 327 } 328 329 impl Debug for dyn Loader { fmt(&self, _f: &mut Formatter<'_>) -> Result<(), FmtError>330 fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), FmtError> { 331 Ok(()) 332 } 333 } 334 335 /// Implementation of `Loader` that loads Vulkan from a dynamic library. 336 pub struct DynamicLibraryLoader { 337 _vk_lib: Library, 338 get_instance_proc_addr: ash::vk::PFN_vkGetInstanceProcAddr, 339 } 340 341 impl DynamicLibraryLoader { 342 /// Tries to load the dynamic library at the given path, and tries to 343 /// load `vkGetInstanceProcAddr` in it. 344 /// 345 /// # Safety 346 /// 347 /// - The dynamic library must be a valid Vulkan implementation. 348 /// new(path: impl AsRef<Path>) -> Result<DynamicLibraryLoader, LoadingError>349 pub unsafe fn new(path: impl AsRef<Path>) -> Result<DynamicLibraryLoader, LoadingError> { 350 let vk_lib = Library::new(path.as_ref()).map_err(LoadingError::LibraryLoadFailure)?; 351 352 let get_instance_proc_addr = *vk_lib 353 .get(b"vkGetInstanceProcAddr") 354 .map_err(LoadingError::LibraryLoadFailure)?; 355 356 Ok(DynamicLibraryLoader { 357 _vk_lib: vk_lib, 358 get_instance_proc_addr, 359 }) 360 } 361 } 362 363 unsafe impl Loader for DynamicLibraryLoader { 364 #[inline] get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction365 unsafe fn get_instance_proc_addr( 366 &self, 367 instance: ash::vk::Instance, 368 name: *const c_char, 369 ) -> ash::vk::PFN_vkVoidFunction { 370 (self.get_instance_proc_addr)(instance, name) 371 } 372 } 373 374 /// Expression that returns a loader that assumes that Vulkan is linked to the executable you're 375 /// compiling. 376 /// 377 /// If you use this macro, you must linked to a library that provides the `vkGetInstanceProcAddr` 378 /// symbol. 379 /// 380 /// This is provided as a macro and not as a regular function, because the macro contains an 381 /// `extern {}` block. 382 // TODO: should this be unsafe? 383 #[macro_export] 384 macro_rules! statically_linked_vulkan_loader { 385 () => {{ 386 extern "C" { 387 fn vkGetInstanceProcAddr( 388 instance: ash::vk::Instance, 389 pName: *const c_char, 390 ) -> ash::vk::PFN_vkVoidFunction; 391 } 392 393 struct StaticallyLinkedVulkanLoader; 394 unsafe impl Loader for StaticallyLinkedVulkanLoader { 395 unsafe fn get_instance_proc_addr( 396 &self, 397 instance: ash::vk::Instance, 398 name: *const c_char, 399 ) -> ash::vk::PFN_vkVoidFunction { 400 vkGetInstanceProcAddr(instance, name) 401 } 402 } 403 404 StaticallyLinkedVulkanLoader 405 }}; 406 } 407 408 /// Error that can happen when loading a Vulkan library. 409 #[derive(Debug)] 410 pub enum LoadingError { 411 /// Failed to load the Vulkan shared library. 412 LibraryLoadFailure(LibloadingError), 413 414 /// Not enough memory. 415 OomError(OomError), 416 } 417 418 impl Error for LoadingError { source(&self) -> Option<&(dyn Error + 'static)>419 fn source(&self) -> Option<&(dyn Error + 'static)> { 420 match self { 421 //Self::LibraryLoadFailure(err) => Some(err), 422 Self::OomError(err) => Some(err), 423 _ => None, 424 } 425 } 426 } 427 428 impl Display for LoadingError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>429 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 430 write!( 431 f, 432 "{}", 433 match self { 434 Self::LibraryLoadFailure(_) => "failed to load the Vulkan shared library", 435 Self::OomError(_) => "not enough memory available", 436 } 437 ) 438 } 439 } 440 441 impl From<VulkanError> for LoadingError { from(err: VulkanError) -> Self442 fn from(err: VulkanError) -> Self { 443 match err { 444 err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)), 445 err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), 446 _ => panic!("unexpected error: {:?}", err), 447 } 448 } 449 } 450 451 #[cfg(test)] 452 mod tests { 453 use super::{DynamicLibraryLoader, LoadingError}; 454 455 #[test] dl_open_error()456 fn dl_open_error() { 457 unsafe { 458 match DynamicLibraryLoader::new("_non_existing_library.void") { 459 Err(LoadingError::LibraryLoadFailure(_)) => (), 460 _ => panic!(), 461 } 462 } 463 } 464 } 465