Support linking Vulkan directly (#457)

* Mark EntryCustom::new_custom as unsafe

Passing a badly-behaved `load` function can invoke undefined behavior.

* Document required feature for Entry

* Support linking Vulkan directly

This is the preferred pattern in most environments when an application
cannot function without Vulkan, as it saves the libloading dependency,
eliminates an error case, and makes the Vulkan dependency visible to
the OS.

* Rename libloading feature to "loaded"

* Link by default

* Guide users towards linking the loader directly

* Remove unnecessary error type

InstanceError::LoadError was never constructed.

* Unify entry types

Simplifies the interface and allows a bunch of code to become
monomorphic.
This commit is contained in:
Benjamin Saunders 2021-09-09 13:50:34 -07:00 committed by GitHub
parent 1b4c82e1d6
commit aa7b429f4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 266 additions and 197 deletions

View file

@ -16,7 +16,7 @@ jobs:
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --workspace --all-targets args: --workspace --all-targets --all-features
generated: generated:
name: Generated name: Generated
@ -47,6 +47,8 @@ jobs:
name: Test Suite name: Test Suite
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install Vulkan loader
run: sudo apt-get install libvulkan-dev
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:

View file

@ -219,6 +219,11 @@ pub fn create_command_pool(&self,
let pool = device.create_command_pool(&pool_create_info).unwrap(); let pool = device.create_command_pool(&pool_create_info).unwrap();
``` ```
### Optional linking
The default `linked` cargo feature will link your binary with the Vulkan loader directly and expose the infallible `Entry::new`.
If your application can handle Vulkan being missing at runtime, you can instead enable the `loaded` feature to dynamically load Vulkan with `Entry::load`.
## Example ## Example
You can find the examples [here](https://github.com/MaikKlein/ash/tree/master/examples). You can find the examples [here](https://github.com/MaikKlein/ash/tree/master/examples).
All examples currently require: the LunarG Validation layers and a Vulkan library that is visible in your `PATH`. An easy way to get started is to use the [LunarG Vulkan SDK](https://lunarg.com/vulkan-sdk/) All examples currently require: the LunarG Validation layers and a Vulkan library that is visible in your `PATH`. An easy way to get started is to use the [LunarG Vulkan SDK](https://lunarg.com/vulkan-sdk/)

View file

@ -15,7 +15,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.build(&events_loop)?; .build(&events_loop)?;
unsafe { unsafe {
let entry = ash::Entry::new()?; let entry = ash::Entry::new();
let surface_extensions = ash_window::enumerate_required_extensions(&window)?; let surface_extensions = ash_window::enumerate_required_extensions(&window)?;
let instance_extensions = surface_extensions let instance_extensions = surface_extensions
.iter() .iter()

View file

@ -1,4 +1,4 @@
use ash::{extensions::khr, prelude::*, vk, EntryCustom, Instance}; use ash::{extensions::khr, prelude::*, vk, Entry, Instance};
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use std::ffi::CStr; use std::ffi::CStr;
@ -14,8 +14,8 @@ use ash::extensions::ext; // portability extensions
/// In order for the created [`vk::SurfaceKHR`] to be valid for the duration of its /// In order for the created [`vk::SurfaceKHR`] to be valid for the duration of its
/// usage, the [`Instance`] this was called on must be dropped later than the /// usage, the [`Instance`] this was called on must be dropped later than the
/// resulting [`vk::SurfaceKHR`]. /// resulting [`vk::SurfaceKHR`].
pub unsafe fn create_surface<L>( pub unsafe fn create_surface(
entry: &EntryCustom<L>, entry: &Entry,
instance: &Instance, instance: &Instance,
window_handle: &dyn HasRawWindowHandle, window_handle: &dyn HasRawWindowHandle,
allocation_callbacks: Option<&vk::AllocationCallbacks>, allocation_callbacks: Option<&vk::AllocationCallbacks>,

View file

@ -14,7 +14,15 @@ edition = "2018"
libloading = { version = "0.7", optional = true } libloading = { version = "0.7", optional = true }
[features] [features]
default = ["libloading"] default = ["linked"]
# Link the Vulkan loader at compile time.
linked = []
# Support searching for the Vulkan loader manually at runtime.
loaded = ["libloading"]
[package.metadata.release] [package.metadata.release]
no-dev-version = true no-dev-version = true
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

24
ash/build.rs Normal file
View file

@ -0,0 +1,24 @@
fn main() {
#[cfg(feature = "linked")]
{
use std::env;
let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
let target_pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap();
println!("cargo:rerun-if-env-changed=VULKAN_SDK");
if let Ok(var) = env::var("VULKAN_SDK") {
let suffix = match (&*target_family, &*target_pointer_width) {
("windows", "32") => "Lib32",
("windows", "64") => "Lib",
_ => "lib",
};
println!("cargo:rustc-link-search={}/{}", var, suffix);
}
let lib = match &*target_family {
"windows" => "vulkan-1",
_ => "vulkan",
};
println!("cargo:rustc-link-lib={}", lib);
}
}

View file

@ -2,41 +2,137 @@ use crate::instance::Instance;
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use std::error::Error;
use std::ffi::CStr; use std::ffi::CStr;
use std::fmt; #[cfg(feature = "loaded")]
use std::ffi::OsStr;
use std::mem; use std::mem;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::ptr; use std::ptr;
#[cfg(feature = "loaded")]
use std::sync::Arc;
/// Holds a custom type `L` to load symbols from (usually a handle to a `dlopen`ed library), #[cfg(feature = "loaded")]
/// the [`vkGetInstanceProcAddr`][vk::StaticFn::get_instance_proc_addr()] loader function from use libloading::Library;
/// this library (in [`vk::StaticFn`]), and Vulkan's "entry point" functions (resolved with `NULL`
/// `instance`) as listed in [`vkGetInstanceProcAddr`'s description]. /// Holds the Vulkan functions independent of a particular instance
///
/// [`vkGetInstanceProcAddr`'s description]: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html#_description
#[derive(Clone)] #[derive(Clone)]
pub struct EntryCustom<L> { pub struct Entry {
static_fn: vk::StaticFn, static_fn: vk::StaticFn,
entry_fn_1_0: vk::EntryFnV1_0, entry_fn_1_0: vk::EntryFnV1_0,
entry_fn_1_1: vk::EntryFnV1_1, entry_fn_1_1: vk::EntryFnV1_1,
entry_fn_1_2: vk::EntryFnV1_2, entry_fn_1_2: vk::EntryFnV1_2,
lib: L, #[cfg(feature = "loaded")]
_lib_guard: Option<Arc<Library>>,
} }
/// Vulkan core 1.0 /// Vulkan core 1.0
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
impl<L> EntryCustom<L> { impl Entry {
pub fn new_custom<Load>( /// Load entry points from a Vulkan loader linked at compile time
mut lib: L, ///
mut load: Load, /// Note that instance/device functions are still fetched via `vkGetInstanceProcAddr` and
) -> std::result::Result<Self, MissingEntryPoint> /// `vkGetDeviceProcAddr` for maximum performance.
where ///
Load: FnMut(&mut L, &::std::ffi::CStr) -> *const c_void, /// ```no_run
{ /// use ash::{vk, Entry};
// Bypass the normal StaticFn::load so we can return an error /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
let static_fn = vk::StaticFn::load_checked(|name| load(&mut lib, name))?; /// let entry = Entry::new();
/// let app_info = vk::ApplicationInfo {
/// api_version: vk::make_api_version(0, 1, 0, 0),
/// ..Default::default()
/// };
/// let create_info = vk::InstanceCreateInfo {
/// p_application_info: &app_info,
/// ..Default::default()
/// };
/// let instance = unsafe { entry.create_instance(&create_info, None)? };
/// # Ok(()) }
/// ```
#[cfg(feature = "linked")]
#[cfg_attr(docsrs, doc(cfg(feature = "linked")))]
pub fn new() -> Self {
// Sound because we're linking to Vulkan, which provides a vkGetInstanceProcAddr that has
// defined behavior in this use.
unsafe {
Self::from_static_fn(vk::StaticFn {
get_instance_proc_addr: vkGetInstanceProcAddr,
})
}
}
/// Load default Vulkan library for the current platform
///
/// # Safety
/// `dlopen`ing native libraries is inherently unsafe. The safety guidelines
/// for [`Library::new()`] and [`Library::get()`] apply here.
///
/// ```no_run
/// use ash::{vk, Entry};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let entry = unsafe { Entry::load()? };
/// let app_info = vk::ApplicationInfo {
/// api_version: vk::make_api_version(0, 1, 0, 0),
/// ..Default::default()
/// };
/// let create_info = vk::InstanceCreateInfo {
/// p_application_info: &app_info,
/// ..Default::default()
/// };
/// let instance = unsafe { entry.create_instance(&create_info, None)? };
/// # Ok(()) }
/// ```
#[cfg(feature = "loaded")]
#[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
pub unsafe fn load() -> Result<Self, LoadingError> {
#[cfg(windows)]
const LIB_PATH: &str = "vulkan-1.dll";
#[cfg(all(
unix,
not(any(target_os = "macos", target_os = "ios", target_os = "android"))
))]
const LIB_PATH: &str = "libvulkan.so.1";
#[cfg(target_os = "android")]
const LIB_PATH: &str = "libvulkan.so";
#[cfg(any(target_os = "macos", target_os = "ios"))]
const LIB_PATH: &str = "libvulkan.dylib";
Self::load_from(LIB_PATH)
}
/// Load Vulkan library at `path`
///
/// # Safety
/// `dlopen`ing native libraries is inherently unsafe. The safety guidelines
/// for [`Library::new()`] and [`Library::get()`] apply here.
#[cfg(feature = "loaded")]
#[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
pub unsafe fn load_from(path: impl AsRef<OsStr>) -> Result<Self, LoadingError> {
let lib = Library::new(path)
.map_err(LoadingError::LibraryLoadFailure)
.map(Arc::new)?;
let static_fn = vk::StaticFn::load_checked(|name| {
lib.get(name.to_bytes_with_nul())
.map(|symbol| *symbol)
.unwrap_or(ptr::null_mut())
})?;
Ok(Self {
_lib_guard: Some(lib),
..Self::from_static_fn(static_fn)
})
}
/// Load entry points based on an already-loaded [`vk::StaticFn`]
///
/// # Safety
/// `static_fn` must contain valid function pointers that comply with the semantics specified by
/// Vulkan 1.0, which must remain valid for at least the lifetime of the returned [`Entry`].
pub unsafe fn from_static_fn(static_fn: vk::StaticFn) -> Self {
let load_fn = |name: &std::ffi::CStr| unsafe { let load_fn = |name: &std::ffi::CStr| unsafe {
mem::transmute(static_fn.get_instance_proc_addr(vk::Instance::null(), name.as_ptr())) mem::transmute(static_fn.get_instance_proc_addr(vk::Instance::null(), name.as_ptr()))
}; };
@ -44,13 +140,14 @@ impl<L> EntryCustom<L> {
let entry_fn_1_1 = vk::EntryFnV1_1::load(load_fn); let entry_fn_1_1 = vk::EntryFnV1_1::load(load_fn);
let entry_fn_1_2 = vk::EntryFnV1_2::load(load_fn); let entry_fn_1_2 = vk::EntryFnV1_2::load(load_fn);
Ok(EntryCustom { Self {
static_fn, static_fn,
entry_fn_1_0, entry_fn_1_0,
entry_fn_1_1, entry_fn_1_1,
entry_fn_1_2, entry_fn_1_2,
lib, #[cfg(feature = "loaded")]
}) _lib_guard: None,
}
} }
pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 { pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 {
@ -65,7 +162,7 @@ impl<L> EntryCustom<L> {
/// ```no_run /// ```no_run
/// # use ash::{Entry, vk}; /// # use ash::{Entry, vk};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let entry = unsafe { Entry::new() }?; /// let entry = Entry::new();
/// match entry.try_enumerate_instance_version()? { /// match entry.try_enumerate_instance_version()? {
/// // Vulkan 1.1+ /// // Vulkan 1.1+
/// Some(version) => { /// Some(version) => {
@ -107,7 +204,7 @@ impl<L> EntryCustom<L> {
&self, &self,
create_info: &vk::InstanceCreateInfo, create_info: &vk::InstanceCreateInfo,
allocation_callbacks: Option<&vk::AllocationCallbacks>, allocation_callbacks: Option<&vk::AllocationCallbacks>,
) -> Result<Instance, InstanceError> { ) -> VkResult<Instance> {
let mut instance = mem::zeroed(); let mut instance = mem::zeroed();
self.entry_fn_1_0 self.entry_fn_1_0
.create_instance( .create_instance(
@ -115,8 +212,7 @@ impl<L> EntryCustom<L> {
allocation_callbacks.as_raw_ptr(), allocation_callbacks.as_raw_ptr(),
&mut instance, &mut instance,
) )
.result() .result()?;
.map_err(InstanceError::VkError)?;
Ok(Instance::load(&self.static_fn, instance)) Ok(Instance::load(&self.static_fn, instance))
} }
@ -154,7 +250,7 @@ impl<L> EntryCustom<L> {
/// Vulkan core 1.1 /// Vulkan core 1.1
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
impl<L> EntryCustom<L> { impl Entry {
pub fn fp_v1_1(&self) -> &vk::EntryFnV1_1 { pub fn fp_v1_1(&self) -> &vk::EntryFnV1_1 {
&self.entry_fn_1_1 &self.entry_fn_1_1
} }
@ -175,29 +271,19 @@ impl<L> EntryCustom<L> {
/// Vulkan core 1.2 /// Vulkan core 1.2
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
impl<L> EntryCustom<L> { impl Entry {
pub fn fp_v1_2(&self) -> &vk::EntryFnV1_2 { pub fn fp_v1_2(&self) -> &vk::EntryFnV1_2 {
&self.entry_fn_1_2 &self.entry_fn_1_2
} }
} }
#[derive(Clone, Debug)] #[cfg(feature = "linked")]
pub enum InstanceError { impl Default for Entry {
LoadError(Vec<&'static str>), fn default() -> Self {
VkError(vk::Result), Self::new()
}
impl fmt::Display for InstanceError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InstanceError::LoadError(e) => write!(f, "{}", e.join("; ")),
InstanceError::VkError(e) => write!(f, "{}", e),
}
} }
} }
impl Error for InstanceError {}
impl vk::StaticFn { impl vk::StaticFn {
pub fn load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint> pub fn load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint>
where where
@ -228,3 +314,50 @@ impl std::fmt::Display for MissingEntryPoint {
} }
} }
impl std::error::Error for MissingEntryPoint {} impl std::error::Error for MissingEntryPoint {}
#[cfg(feature = "linked")]
extern "system" {
fn vkGetInstanceProcAddr(instance: vk::Instance, name: *const c_char)
-> vk::PFN_vkVoidFunction;
}
#[cfg(feature = "loaded")]
mod loaded {
use std::error::Error;
use std::fmt;
use super::*;
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
pub enum LoadingError {
LibraryLoadFailure(libloading::Error),
MissingEntryPoint(MissingEntryPoint),
}
impl fmt::Display for LoadingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LoadingError::LibraryLoadFailure(err) => fmt::Display::fmt(err, f),
LoadingError::MissingEntryPoint(err) => fmt::Display::fmt(err, f),
}
}
}
impl Error for LoadingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(match self {
LoadingError::LibraryLoadFailure(err) => err,
LoadingError::MissingEntryPoint(err) => err,
})
}
}
impl From<MissingEntryPoint> for LoadingError {
fn from(err: MissingEntryPoint) -> Self {
LoadingError::MissingEntryPoint(err)
}
}
}
#[cfg(feature = "loaded")]
pub use self::loaded::*;

View file

@ -1,101 +0,0 @@
use crate::entry::EntryCustom;
use crate::entry::MissingEntryPoint;
use libloading::Library;
use std::error::Error;
use std::ffi::OsStr;
use std::fmt;
use std::ptr;
use std::sync::Arc;
#[cfg(windows)]
const LIB_PATH: &str = "vulkan-1.dll";
#[cfg(all(
unix,
not(any(target_os = "macos", target_os = "ios", target_os = "android"))
))]
const LIB_PATH: &str = "libvulkan.so.1";
#[cfg(target_os = "android")]
const LIB_PATH: &str = "libvulkan.so";
#[cfg(any(target_os = "macos", target_os = "ios"))]
const LIB_PATH: &str = "libvulkan.dylib";
#[derive(Debug)]
pub enum LoadingError {
LibraryLoadFailure(libloading::Error),
MissingEntryPoint(MissingEntryPoint),
}
impl fmt::Display for LoadingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LoadingError::LibraryLoadFailure(err) => fmt::Display::fmt(err, f),
LoadingError::MissingEntryPoint(err) => fmt::Display::fmt(err, f),
}
}
}
impl Error for LoadingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(match self {
LoadingError::LibraryLoadFailure(err) => err,
LoadingError::MissingEntryPoint(err) => err,
})
}
}
impl From<MissingEntryPoint> for LoadingError {
fn from(err: MissingEntryPoint) -> Self {
LoadingError::MissingEntryPoint(err)
}
}
/// Default function loader
pub type Entry = EntryCustom<Arc<Library>>;
impl Entry {
/// Load default Vulkan library for the current platform
///
/// # Safety
/// `dlopen`ing native libraries is inherently unsafe. The safety guidelines
/// for [`Library::new`] and [`Library::get`] apply here.
///
/// ```no_run
/// use ash::{vk, Entry};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let entry = unsafe { Entry::new() }?;
/// let app_info = vk::ApplicationInfo {
/// api_version: vk::make_api_version(0, 1, 0, 0),
/// ..Default::default()
/// };
/// let create_info = vk::InstanceCreateInfo {
/// p_application_info: &app_info,
/// ..Default::default()
/// };
/// let instance = unsafe { entry.create_instance(&create_info, None)? };
/// # Ok(()) }
/// ```
pub unsafe fn new() -> Result<Entry, LoadingError> {
Self::with_library(LIB_PATH)
}
/// Load Vulkan library at `path`
///
/// # Safety
/// `dlopen`ing native libraries is inherently unsafe. The safety guidelines
/// for [`Library::new`] and [`Library::get`] apply here.
pub unsafe fn with_library(path: impl AsRef<OsStr>) -> Result<Entry, LoadingError> {
let lib = Library::new(path)
.map_err(LoadingError::LibraryLoadFailure)
.map(Arc::new)?;
Ok(Self::new_custom(lib, |vk_lib, name| {
vk_lib
.get(name.to_bytes_with_nul())
.map(|symbol| *symbol)
.unwrap_or(ptr::null_mut())
})?)
}
}

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct DebugReport {
} }
impl DebugReport { impl DebugReport {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let debug_report_fn = vk::ExtDebugReportFn::load(|name| unsafe { let debug_report_fn = vk::ExtDebugReportFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use crate::{vk, RawPtr}; use crate::{vk, RawPtr};
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -11,7 +11,7 @@ pub struct DebugUtils {
} }
impl DebugUtils { impl DebugUtils {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let debug_utils_fn = vk::ExtDebugUtilsFn::load(|name| unsafe { let debug_utils_fn = vk::ExtDebugUtilsFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct MetalSurface {
} }
impl MetalSurface { impl MetalSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::ExtMetalSurfaceFn::load(|name| unsafe { let surface_fn = vk::ExtMetalSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -11,7 +11,7 @@ pub struct ToolingInfo {
} }
impl ToolingInfo { impl ToolingInfo {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let tooling_info_fn = vk::ExtToolingInfoFn::load(|name| unsafe { let tooling_info_fn = vk::ExtToolingInfoFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct AndroidSurface {
} }
impl AndroidSurface { impl AndroidSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::KhrAndroidSurfaceFn::load(|name| unsafe { let surface_fn = vk::KhrAndroidSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct Display {
} }
impl Display { impl Display {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let display_fn = vk::KhrDisplayFn::load(|name| unsafe { let display_fn = vk::KhrDisplayFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
@ -12,7 +12,7 @@ pub struct GetPhysicalDeviceProperties2 {
} }
impl GetPhysicalDeviceProperties2 { impl GetPhysicalDeviceProperties2 {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let get_physical_device_properties2_fn = let get_physical_device_properties2_fn =
vk::KhrGetPhysicalDeviceProperties2Fn::load(|name| unsafe { vk::KhrGetPhysicalDeviceProperties2Fn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -11,7 +11,7 @@ pub struct PipelineExecutableProperties {
} }
impl PipelineExecutableProperties { impl PipelineExecutableProperties {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let pipeline_executable_properties_fn = let pipeline_executable_properties_fn =
vk::KhrPipelineExecutablePropertiesFn::load(|name| unsafe { vk::KhrPipelineExecutablePropertiesFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct Surface {
} }
impl Surface { impl Surface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::KhrSurfaceFn::load(|name| unsafe { let surface_fn = vk::KhrSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -11,7 +11,7 @@ pub struct TimelineSemaphore {
} }
impl TimelineSemaphore { impl TimelineSemaphore {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let timeline_semaphore_fn = vk::KhrTimelineSemaphoreFn::load(|name| unsafe { let timeline_semaphore_fn = vk::KhrTimelineSemaphoreFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct WaylandSurface {
} }
impl WaylandSurface { impl WaylandSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::KhrWaylandSurfaceFn::load(|name| unsafe { let surface_fn = vk::KhrWaylandSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct Win32Surface {
} }
impl Win32Surface { impl Win32Surface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::KhrWin32SurfaceFn::load(|name| unsafe { let surface_fn = vk::KhrWin32SurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct XcbSurface {
} }
impl XcbSurface { impl XcbSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::KhrXcbSurfaceFn::load(|name| unsafe { let surface_fn = vk::KhrXcbSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct XlibSurface {
} }
impl XlibSurface { impl XlibSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::KhrXlibSurfaceFn::load(|name| unsafe { let surface_fn = vk::KhrXlibSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct IOSSurface {
} }
impl IOSSurface { impl IOSSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::MvkIosSurfaceFn::load(|name| unsafe { let surface_fn = vk::MvkIosSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct MacOSSurface {
} }
impl MacOSSurface { impl MacOSSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::MvkMacosSurfaceFn::load(|name| unsafe { let surface_fn = vk::MvkMacosSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::vk; use crate::vk;
use crate::RawPtr; use crate::RawPtr;
use crate::{EntryCustom, Instance}; use crate::{Entry, Instance};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
@ -12,7 +12,7 @@ pub struct ViSurface {
} }
impl ViSurface { impl ViSurface {
pub fn new<L>(entry: &EntryCustom<L>, instance: &Instance) -> Self { pub fn new(entry: &Entry, instance: &Instance) -> Self {
let surface_fn = vk::NnViSurfaceFn::load(|name| unsafe { let surface_fn = vk::NnViSurfaceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr())) mem::transmute(entry.get_instance_proc_addr(instance.handle(), name.as_ptr()))
}); });

View file

@ -3,6 +3,7 @@
clippy::missing_safety_doc, clippy::missing_safety_doc,
clippy::upper_case_acronyms clippy::upper_case_acronyms
)] )]
#![cfg_attr(docsrs, feature(doc_cfg))]
//! # Vulkan API //! # Vulkan API
//! //!
//! <https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/index.html> //! <https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/index.html>
@ -12,7 +13,7 @@
//! ```no_run //! ```no_run
//! use ash::{vk, Entry}; //! use ash::{vk, Entry};
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let entry = unsafe { Entry::new() }?; //! let entry = Entry::new();
//! let app_info = vk::ApplicationInfo { //! let app_info = vk::ApplicationInfo {
//! api_version: vk::make_api_version(0, 1, 0, 0), //! api_version: vk::make_api_version(0, 1, 0, 0),
//! ..Default::default() //! ..Default::default()
@ -26,22 +27,19 @@
//! ``` //! ```
//! //!
//! ## Getting started //! ## Getting started
//! Load the Vulkan library at the default location using [`Entry::new()`][EntryCustom<_>::new()], //!
//! or at a custom location using [`Entry::with_library("path/to/vulkan")`][EntryCustom<_>::with_library()]. //! Load the Vulkan library linked at compile time using [`Entry::new()`], or load it at runtime
//! These loaders use [`libloading`]. If you wish to perform function loading yourself //! using [`Entry::load()`], which uses `libloading`. If you want to perform entry point loading
//! call [`EntryCustom::new_custom()`] with a closure turning function names //! yourself, call [`Entry::from_static_fn()`].
//! into function pointers.
pub use crate::device::Device; pub use crate::device::Device;
pub use crate::entry::{EntryCustom, InstanceError}; pub use crate::entry::Entry;
#[cfg(feature = "libloading")] #[cfg(feature = "loaded")]
pub use crate::entry_libloading::{Entry, LoadingError}; pub use crate::entry::LoadingError;
pub use crate::instance::Instance; pub use crate::instance::Instance;
mod device; mod device;
mod entry; mod entry;
#[cfg(feature = "libloading")]
mod entry_libloading;
mod instance; mod instance;
pub mod prelude; pub mod prelude;
pub mod util; pub mod util;

View file

@ -7,7 +7,7 @@ use ash::extensions::{
}; };
use ash::{vk, Entry}; use ash::{vk, Entry};
pub use ash::{Device, EntryCustom, Instance}; pub use ash::{Device, Instance};
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::default::Default; use std::default::Default;
@ -204,7 +204,7 @@ impl ExampleBase {
)) ))
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
let entry = Entry::new().unwrap(); let entry = Entry::new();
let app_name = CString::new("VulkanTriangle").unwrap(); let app_name = CString::new("VulkanTriangle").unwrap();
let layer_names = [CString::new("VK_LAYER_KHRONOS_validation").unwrap()]; let layer_names = [CString::new("VK_LAYER_KHRONOS_validation").unwrap()];