diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33bde8e4e..9da275738 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: check - args: --workspace --all-targets + args: --workspace --all-targets --all-features generated: name: Generated diff --git a/README.md b/README.md index fe21754cf..14a7d84f3 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,11 @@ pub fn create_command_pool(&self, let pool = device.create_command_pool(&pool_create_info).unwrap(); ``` +### Optional linking + +Enable the `linked` cargo feature to link your binary with the Vulkan loader directly and expose the infallible `EntryLinked`. +If your application can handle Vulkan being missing at runtime, you can instead use the default `libloading` feature to dynamically load Vulkan with `Entry`. + ## Example 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/) diff --git a/ash/Cargo.toml b/ash/Cargo.toml index 06e388a3d..0b00337be 100644 --- a/ash/Cargo.toml +++ b/ash/Cargo.toml @@ -15,9 +15,11 @@ libloading = { version = "0.7", optional = true } [features] default = ["libloading"] +linked = [] [package.metadata.release] no-dev-version = true [package.metadata.docs.rs] +all-features = true rustdoc-args = ["--cfg", "docsrs"] diff --git a/ash/build.rs b/ash/build.rs new file mode 100644 index 000000000..d93af9ef7 --- /dev/null +++ b/ash/build.rs @@ -0,0 +1,6 @@ +fn main() { + #[cfg(feature = "linked")] + { + println!("cargo:rustc-link-lib=vulkan"); + } +} diff --git a/ash/src/entry.rs b/ash/src/entry.rs index be67347e0..9d9a804ee 100644 --- a/ash/src/entry.rs +++ b/ash/src/entry.rs @@ -37,6 +37,10 @@ impl EntryCustom { { // Bypass the normal StaticFn::load so we can return an error let static_fn = vk::StaticFn::load_checked(|name| load(&mut lib, name))?; + Ok(Self::from_static_fn(lib, static_fn)) + } + + pub unsafe fn from_static_fn(lib: L, static_fn: vk::StaticFn) -> Self { let load_fn = |name: &std::ffi::CStr| unsafe { mem::transmute(static_fn.get_instance_proc_addr(vk::Instance::null(), name.as_ptr())) }; @@ -44,13 +48,13 @@ impl EntryCustom { let entry_fn_1_1 = vk::EntryFnV1_1::load(load_fn); let entry_fn_1_2 = vk::EntryFnV1_2::load(load_fn); - Ok(EntryCustom { + EntryCustom { static_fn, entry_fn_1_0, entry_fn_1_1, entry_fn_1_2, lib, - }) + } } pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 { diff --git a/ash/src/entry_libloading.rs b/ash/src/entry_libloading.rs index 16486d6c5..27b9e38fe 100644 --- a/ash/src/entry_libloading.rs +++ b/ash/src/entry_libloading.rs @@ -52,7 +52,10 @@ impl From for LoadingError { } } -/// Default function loader +/// Runtime function loader +/// +/// Prefer [`EntryLinked`](crate::EntryLinked) in code that would would otherwise panic on +/// [`Entry::new`] failing. #[cfg_attr(docsrs, doc(cfg(feature = "libloading")))] pub type Entry = EntryCustom>; diff --git a/ash/src/entry_linked.rs b/ash/src/entry_linked.rs new file mode 100644 index 000000000..6d2aca16b --- /dev/null +++ b/ash/src/entry_linked.rs @@ -0,0 +1,33 @@ +use std::os::raw::c_char; + +use crate::{entry::EntryCustom, vk}; + +/// Marker type for [`EntryLinked`] +pub struct Linked; + +/// Compile-time function loader +/// +/// Prefer this over [`Entry`](crate::Entry) in code that would otherwise panic on +/// [`Entry::new`](crate::Entry::new) failing. +#[cfg_attr(docsrs, doc(cfg(feature = "linked")))] +pub type EntryLinked = EntryCustom; + +impl EntryLinked { + 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( + Linked, + vk::StaticFn { + get_instance_proc_addr: vkGetInstanceProcAddr, + }, + ) + } + } +} + +extern "system" { + fn vkGetInstanceProcAddr(instance: vk::Instance, name: *const c_char) + -> vk::PFN_vkVoidFunction; +} diff --git a/ash/src/lib.rs b/ash/src/lib.rs index a492e0799..92402c10b 100644 --- a/ash/src/lib.rs +++ b/ash/src/lib.rs @@ -37,12 +37,16 @@ pub use crate::device::Device; pub use crate::entry::{EntryCustom, InstanceError}; #[cfg(feature = "libloading")] pub use crate::entry_libloading::{Entry, LoadingError}; +#[cfg(feature = "linked")] +pub use crate::entry_linked::EntryLinked; pub use crate::instance::Instance; mod device; mod entry; #[cfg(feature = "libloading")] mod entry_libloading; +#[cfg(feature = "linked")] +mod entry_linked; mod instance; pub mod prelude; pub mod util; diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f81b2b775..f6ed212bc 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -9,3 +9,6 @@ winit = "0.19.5" image = "0.10.4" ash = { path = "../ash" } ash-window = { path = "../ash-window" } + +[features] +linked = ["ash/linked"] diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 1eef4a764..206fc2b54 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -6,7 +6,11 @@ use ash::extensions::{ khr::{Surface, Swapchain}, }; -use ash::{vk, Entry}; +use ash::vk; +#[cfg(not(feature = "linked"))] +use ash::Entry; +#[cfg(feature = "linked")] +use ash::EntryLinked; pub use ash::{Device, EntryCustom, Instance}; use std::borrow::Cow; use std::cell::RefCell; @@ -133,6 +137,9 @@ pub fn find_memorytype_index( } pub struct ExampleBase { + #[cfg(feature = "linked")] + pub entry: EntryLinked, + #[cfg(not(feature = "linked"))] pub entry: Entry, pub instance: Instance, pub device: Device, @@ -204,6 +211,9 @@ impl ExampleBase { )) .build(&events_loop) .unwrap(); + #[cfg(feature = "linked")] + let entry = EntryLinked::new(); + #[cfg(not(feature = "linked"))] let entry = Entry::new().unwrap(); let app_name = CString::new("VulkanTriangle").unwrap();