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