Add Khronos doc links and other ergonomic cleanup

* Khronos doc links are now automatically generated in vk.rs
* Added doc links to all other non-generated functions
* Implemented std::error::Error for LoadingError
* Made EntryCustom public in addition to the Entry typedef so that
  rustdoc no longer ignores it
* Moved Entry::new into EntryCustom (non-breaking)
* Added EntryCustom::try_enumerate_instance_version. The EntryV1_1 trait
  is not implemented for any type. Even if it were, the function would
  panic for Vulkan 1.0 implementations
* Added entry and instance creation example to lib docs
* Updated the Display impl for vk::Result so that it matches
  other bitmask and enum conventions
* Removed lazy_static dependency because it was no longer being
  referenced
This commit is contained in:
Aaron Loucks 2019-03-15 18:41:33 -04:00
parent b570edcee2
commit adb194891f
6 changed files with 177 additions and 56 deletions

View file

@ -11,7 +11,6 @@ documentation = "https://docs.rs/ash"
[dependencies] [dependencies]
shared_library = "0.1.9" shared_library = "0.1.9"
lazy_static = "1"
[features] [features]
default = [] default = []

View file

@ -27,12 +27,10 @@ const LIB_PATH: &'static str = "libvulkan.so";
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "ios"))]
const LIB_PATH: &'static str = "libvulkan.dylib"; const LIB_PATH: &'static str = "libvulkan.dylib";
lazy_static! { /// Function loader
static ref VK_LIB: Result<DynamicLibrary, String> =
DynamicLibrary::open(Some(&Path::new(LIB_PATH)));
}
pub type Entry = EntryCustom<Arc<DynamicLibrary>>; pub type Entry = EntryCustom<Arc<DynamicLibrary>>;
/// Function loader
#[derive(Clone)] #[derive(Clone)]
pub struct EntryCustom<L> { pub struct EntryCustom<L> {
static_fn: vk::StaticFn, static_fn: vk::StaticFn,
@ -46,6 +44,22 @@ pub enum LoadingError {
LibraryLoadError(String), LibraryLoadError(String),
} }
impl fmt::Display for LoadingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LoadingError::{:?}", self)
}
}
impl Error for LoadingError {
fn description(&self) -> &str {
"LoadingError"
}
fn cause(&self) -> Option<&Error> {
None
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum InstanceError { pub enum InstanceError {
LoadError(Vec<&'static str>), LoadError(Vec<&'static str>),
@ -125,6 +139,7 @@ pub trait EntryV1_0 {
} }
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkGetInstanceProcAddr.html>"]
fn get_instance_proc_addr( fn get_instance_proc_addr(
&self, &self,
instance: vk::Instance, instance: vk::Instance,
@ -177,6 +192,41 @@ pub trait EntryV1_1: EntryV1_0 {
} }
} }
} }
impl EntryCustom<Arc<DynamicLibrary>> {
/// ```rust
/// # #[macro_use]
/// # extern crate ash;
/// use ash::{vk, Entry, version::EntryV1_0};
/// # fn main() -> Result<(), Box<std::error::Error>> {
/// let entry = Entry::new()?;
/// let app_info = vk::ApplicationInfo {
/// api_version: vk_make_version!(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 fn new() -> Result<Entry, LoadingError> {
Self::new_custom(
|| {
DynamicLibrary::open(Some(&Path::new(LIB_PATH)))
.map_err(|err| LoadingError::LibraryLoadError(err.clone()))
.map(|dl| Arc::new(dl))
},
|vk_lib, name| unsafe {
vk_lib
.symbol(&*name.to_string_lossy())
.unwrap_or(ptr::null_mut())
},
)
}
}
impl<L> EntryCustom<L> { impl<L> EntryCustom<L> {
pub fn new_custom<Open, Load>(open: Open, mut load: Load) -> Result<Self, LoadingError> pub fn new_custom<Open, Load>(open: Open, mut load: Load) -> Result<Self, LoadingError>
where where
@ -201,21 +251,45 @@ impl<L> EntryCustom<L> {
lib, lib,
}) })
} }
}
impl Entry { #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkEnumerateInstanceVersion.html>"]
pub fn new() -> Result<Self, LoadingError> { /// ```rust
Self::new_custom( /// # #[macro_use]
|| { /// # extern crate ash;
DynamicLibrary::open(Some(&Path::new(LIB_PATH))) /// # use ash::Entry;
.map_err(|err| LoadingError::LibraryLoadError(err.clone())) /// # fn main() -> Result<(), Box<std::error::Error>> {
.map(|dl| Arc::new(dl)) /// let entry = Entry::new()?;
}, /// match entry.try_enumerate_instance_version()? {
|vk_lib, name| unsafe { /// // Vulkan 1.1+
vk_lib /// Some(version) => {
.symbol(&*name.to_string_lossy()) /// let major = vk_version_major!(version);
.unwrap_or(ptr::null_mut()) /// let minor = vk_version_minor!(version);
}, /// let patch = vk_version_patch!(version);
) /// },
/// // Vulkan 1.0
/// None => {},
/// }
/// # Ok(()) }
/// ```
pub fn try_enumerate_instance_version(&self) -> VkResult<Option<u32>> {
unsafe {
let mut api_version = 0;
let enumerate_instance_version: Option<vk::PFN_vkEnumerateInstanceVersion> = {
let name = b"vkEnumerateInstanceVersion\0".as_ptr() as *const _;
mem::transmute(
self.static_fn()
.get_instance_proc_addr(vk::Instance::null(), name),
)
};
if let Some(enumerate_instance_version) = enumerate_instance_version {
let err_code = (enumerate_instance_version)(&mut api_version);
match err_code {
vk::Result::SUCCESS => Ok(Some(api_version)),
_ => Err(err_code),
}
} else {
Ok(None)
}
}
} }
} }

View file

@ -1,9 +1,32 @@
#[macro_use] //! # Vulkan API
extern crate lazy_static; //!
//! <https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/index.html>
//!
//! ## Examples
//!
//! ```rust
//! # #[macro_use]
//! # extern crate ash;
//! use ash::{vk, Entry, version::EntryV1_0};
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let entry = Entry::new()?;
//! let app_info = vk::ApplicationInfo {
//! api_version: vk_make_version!(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(()) }
//! ```
//!
extern crate shared_library; extern crate shared_library;
pub use device::Device; pub use device::Device;
pub use entry::{Entry, InstanceError, LoadingError}; pub use entry::{Entry, EntryCustom, InstanceError, LoadingError};
pub use instance::Instance; pub use instance::Instance;
mod device; mod device;

View file

@ -20,24 +20,28 @@ pub trait Handle {
fn as_raw(self) -> u64; fn as_raw(self) -> u64;
fn from_raw(u64) -> Self; fn from_raw(u64) -> Self;
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_MAKE_VERSION.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_make_version { macro_rules! vk_make_version {
( $ major : expr , $ minor : expr , $ patch : expr ) => { ( $ major : expr , $ minor : expr , $ patch : expr ) => {
(($major as u32) << 22) | (($minor as u32) << 12) | $patch as u32 (($major as u32) << 22) | (($minor as u32) << 12) | $patch as u32
}; };
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_VERSION_MAJOR.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_version_major { macro_rules! vk_version_major {
( $ major : expr ) => { ( $ major : expr ) => {
($major as u32) >> 22 ($major as u32) >> 22
}; };
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_VERSION_MINOR.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_version_minor { macro_rules! vk_version_minor {
( $ minor : expr ) => { ( $ minor : expr ) => {
(($minor as u32) >> 12) & 0x3ff (($minor as u32) >> 12) & 0x3ff
}; };
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_VERSION_PATCH.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_version_patch { macro_rules! vk_version_patch {
( $ minor : expr ) => { ( $ minor : expr ) => {
@ -41349,34 +41353,24 @@ impl ::std::error::Error for Result {
impl fmt::Display for Result { impl fmt::Display for Result {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let name = match *self { let name = match *self {
Result::SUCCESS => Some("Command completed successfully"), Result::SUCCESS => Some(stringify!(SUCCESS)),
Result::NOT_READY => Some("A fence or query has not yet completed"), Result::NOT_READY => Some(stringify!(NOT_READY)),
Result::TIMEOUT => Some("A wait operation has not completed in the specified time"), Result::TIMEOUT => Some(stringify!(TIMEOUT)),
Result::EVENT_SET => Some("An event is signaled"), Result::EVENT_SET => Some(stringify!(EVENT_SET)),
Result::EVENT_RESET => Some("An event is unsignaled"), Result::EVENT_RESET => Some(stringify!(EVENT_RESET)),
Result::INCOMPLETE => Some("A return array was too small for the result"), Result::INCOMPLETE => Some(stringify!(INCOMPLETE)),
Result::ERROR_OUT_OF_HOST_MEMORY => Some("A host memory allocation has failed"), Result::ERROR_OUT_OF_HOST_MEMORY => Some(stringify!(ERROR_OUT_OF_HOST_MEMORY)),
Result::ERROR_OUT_OF_DEVICE_MEMORY => Some("A device memory allocation has failed"), Result::ERROR_OUT_OF_DEVICE_MEMORY => Some(stringify!(ERROR_OUT_OF_DEVICE_MEMORY)),
Result::ERROR_INITIALIZATION_FAILED => Some("Initialization of a object has failed"), Result::ERROR_INITIALIZATION_FAILED => Some(stringify!(ERROR_INITIALIZATION_FAILED)),
Result::ERROR_DEVICE_LOST => { Result::ERROR_DEVICE_LOST => Some(stringify!(ERROR_DEVICE_LOST)),
Some("The logical device has been lost. See <<devsandqueues-lost-device>>") Result::ERROR_MEMORY_MAP_FAILED => Some(stringify!(ERROR_MEMORY_MAP_FAILED)),
} Result::ERROR_LAYER_NOT_PRESENT => Some(stringify!(ERROR_LAYER_NOT_PRESENT)),
Result::ERROR_MEMORY_MAP_FAILED => Some("Mapping of a memory object has failed"), Result::ERROR_EXTENSION_NOT_PRESENT => Some(stringify!(ERROR_EXTENSION_NOT_PRESENT)),
Result::ERROR_LAYER_NOT_PRESENT => Some("Layer specified does not exist"), Result::ERROR_FEATURE_NOT_PRESENT => Some(stringify!(ERROR_FEATURE_NOT_PRESENT)),
Result::ERROR_EXTENSION_NOT_PRESENT => Some("Extension specified does not exist"), Result::ERROR_INCOMPATIBLE_DRIVER => Some(stringify!(ERROR_INCOMPATIBLE_DRIVER)),
Result::ERROR_FEATURE_NOT_PRESENT => { Result::ERROR_TOO_MANY_OBJECTS => Some(stringify!(ERROR_TOO_MANY_OBJECTS)),
Some("Requested feature is not available on this device") Result::ERROR_FORMAT_NOT_SUPPORTED => Some(stringify!(ERROR_FORMAT_NOT_SUPPORTED)),
} Result::ERROR_FRAGMENTED_POOL => Some(stringify!(ERROR_FRAGMENTED_POOL)),
Result::ERROR_INCOMPATIBLE_DRIVER => Some("Unable to find a Vulkan driver"),
Result::ERROR_TOO_MANY_OBJECTS => {
Some("Too many objects of the type have already been created")
}
Result::ERROR_FORMAT_NOT_SUPPORTED => {
Some("Requested format is not supported on this device")
}
Result::ERROR_FRAGMENTED_POOL => Some(
"A requested pool allocation has failed due to fragmentation of the pool\'s memory",
),
_ => None, _ => None,
}; };
if let Some(x) = name { if let Some(x) = name {

View file

@ -14,3 +14,17 @@ fn display_flags() {
fn display_enum() { fn display_enum() {
assert_eq!(vk::ChromaLocation::MIDPOINT.to_string(), "MIDPOINT"); assert_eq!(vk::ChromaLocation::MIDPOINT.to_string(), "MIDPOINT");
} }
#[test]
fn display_result() {
assert_eq!(vk::Result::SUCCESS.to_string(), "SUCCESS");
}
#[test]
fn error_result_description() {
use std::error::Error;
assert_eq!(
vk::Result::SUCCESS.description(),
"Command completed successfully"
);
}

View file

@ -179,6 +179,7 @@ pub fn handle_nondispatchable_macro() -> Tokens {
} }
pub fn vk_version_macros() -> Tokens { pub fn vk_version_macros() -> Tokens {
quote! { quote! {
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_MAKE_VERSION.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_make_version { macro_rules! vk_make_version {
($major:expr, $minor:expr, $patch:expr) => { ($major:expr, $minor:expr, $patch:expr) => {
@ -186,6 +187,7 @@ pub fn vk_version_macros() -> Tokens {
}; };
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_VERSION_MAJOR.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_version_major { macro_rules! vk_version_major {
($major:expr) => { ($major:expr) => {
@ -193,6 +195,7 @@ pub fn vk_version_macros() -> Tokens {
}; };
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_VERSION_MINOR.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_version_minor { macro_rules! vk_version_minor {
($minor:expr) => { ($minor:expr) => {
@ -200,6 +203,7 @@ pub fn vk_version_macros() -> Tokens {
}; };
} }
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_VERSION_PATCH.html>"]
#[macro_export] #[macro_export]
macro_rules! vk_version_patch { macro_rules! vk_version_patch {
($minor:expr) => { ($minor:expr) => {
@ -1206,7 +1210,7 @@ pub fn generate_enum<'a>(
} }
pub fn generate_result(ident: Ident, _enum: &vkxml::Enumeration) -> Tokens { pub fn generate_result(ident: Ident, _enum: &vkxml::Enumeration) -> Tokens {
let notation = _enum.elements.iter().filter_map(|elem| { let description_notation = _enum.elements.iter().filter_map(|elem| {
let (variant_name, notation) = match *elem { let (variant_name, notation) = match *elem {
vkxml::EnumerationElement::Enum(ref constant) => ( vkxml::EnumerationElement::Enum(ref constant) => (
constant.name.as_str(), constant.name.as_str(),
@ -1223,12 +1227,25 @@ pub fn generate_result(ident: Ident, _enum: &vkxml::Enumeration) -> Tokens {
}) })
}); });
let notation2 = notation.clone(); let display_notation = _enum.elements.iter().filter_map(|elem| {
let variant_name = match *elem {
vkxml::EnumerationElement::Enum(ref constant) => constant.name.as_str(),
_ => {
return None;
}
};
let variant_ident = variant_ident(&_enum.name, variant_name);
Some(quote! {
#ident::#variant_ident => Some(stringify!(#variant_ident))
})
});
quote! { quote! {
impl ::std::error::Error for #ident { impl ::std::error::Error for #ident {
fn description(&self) -> &str { fn description(&self) -> &str {
let name = match *self { let name = match *self {
#(#notation),*, #(#description_notation),*,
_ => None, _ => None,
}; };
name.unwrap_or("unknown error") name.unwrap_or("unknown error")
@ -1237,7 +1254,7 @@ pub fn generate_result(ident: Ident, _enum: &vkxml::Enumeration) -> Tokens {
impl fmt::Display for #ident { impl fmt::Display for #ident {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let name = match *self { let name = match *self {
#(#notation2),*, #(#display_notation),*,
_ => None, _ => None,
}; };
if let Some(x) = name { if let Some(x) = name {