ash: Add const STRUCTURE_TYPE
to all Vulkan structures for matching with match_struct!
macro (#614)
* ash: Add `const STRUCTURE_TYPE` to all Vulkan structures for matching with `match_struct!` macro In Vulkan layers extracing a structure based on its `s_type` is a common operation, but comparing against an enum value and subsequently casting to the right type is verbose and error-prone. By generating a `const STRUCTURE_TYPE` with the given value for every Vulkan structure it becomes possible to implement a macro that abstracts this logic away in a safer way. * generator: Reuse `HasStructureType::STRUCTURE_TYPE` in `s_type` initializer
This commit is contained in:
parent
6d0a29b638
commit
13fef40d48
|
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Added `VK_EXT_image_drm_format_modifier` device extension (#603)
|
||||
- Update Vulkan-Headers to 1.3.219 (#605, #608, #619)
|
||||
- Added `const STRUCTURE_TYPE` to all Vulkan structures for matching with `match_struct!` macro (#614)
|
||||
- Added `VK_EXT_sample_locations` device extension (#616)
|
||||
- Added `VK_NV_coverage_reduction_mode` device extension (#617)
|
||||
- Added `VK_KHR_ray_tracing_maintenance1` device extension (#620)
|
||||
|
|
101
ash/src/lib.rs
101
ash/src/lib.rs
|
@ -72,6 +72,107 @@ impl<'r, T> RawPtr<T> for Option<&'r T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given a mutable raw pointer to a type with an `s_type` member such as [`vk::BaseOutStructure`],
|
||||
/// match on a set of Vulkan structures. The struct will be rebound to the given variable of the
|
||||
/// type of the given Vulkan structure.
|
||||
///
|
||||
/// Note that all match bodies have to be enclosed by curly braces due to macro parsing limitations.
|
||||
/// It is unfortunately not possible to write `x @ ash::vk::SomeStruct => one_line_expression(),`.
|
||||
///
|
||||
/// ```
|
||||
/// let mut info = ash::vk::DeviceCreateInfo::default();
|
||||
/// let info: *mut ash::vk::BaseOutStructure = <*mut _>::cast(&mut info);
|
||||
/// unsafe {
|
||||
/// ash::match_out_struct!(match info {
|
||||
/// info @ ash::vk::DeviceQueueCreateInfo => {
|
||||
/// dbg!(&info); // Unreachable
|
||||
/// }
|
||||
/// info @ ash::vk::DeviceCreateInfo => {
|
||||
/// dbg!(&info);
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In addition this macro propagates implicit return values just like normal `match` blocks, as
|
||||
/// long as a default value or expression is provided in the "any" match arm
|
||||
/// (`_ => { some_value() }`). For the time being said arm must be wrapped in curly braces; an
|
||||
/// expression like `_ => None` is not yet supported.
|
||||
///
|
||||
/// ```
|
||||
/// # let mut info = ash::vk::DeviceCreateInfo::default();
|
||||
/// # let info: *mut ash::vk::BaseOutStructure = <*mut _>::cast(&mut info);
|
||||
/// let device_create_flags: Option<ash::vk::DeviceCreateFlags> = unsafe {
|
||||
/// ash::match_out_struct!(match info {
|
||||
/// info @ ash::vk::DeviceQueueCreateInfo => {
|
||||
/// dbg!(&info); // Unreachable
|
||||
/// Some(ash::vk::DeviceCreateFlags::empty())
|
||||
/// }
|
||||
/// info @ ash::vk::DeviceCreateInfo => {
|
||||
/// dbg!(&info);
|
||||
/// Some(info.flags)
|
||||
/// }
|
||||
/// _ => {
|
||||
/// None
|
||||
/// }
|
||||
/// })
|
||||
/// };
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! match_out_struct {
|
||||
(match $p:ident { $($bind:ident @ $ty:path => $body:block $(,)?)+ $(_ => $any:block $(,)?)? }) => {
|
||||
match std::ptr::addr_of!((*$p).s_type).read() {
|
||||
$(<$ty as $crate::vk::TaggedStructure>::STRUCTURE_TYPE => {
|
||||
let $bind = $p
|
||||
.cast::<$ty>()
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
$body
|
||||
}),+
|
||||
_ => { $($any)? }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Given an immutable raw pointer to a type with an `s_type` member such as [`vk::BaseInStructure`],
|
||||
/// match on a set of Vulkan structures. The struct will be rebound to the given variable of the
|
||||
/// type of the given Vulkan structure.
|
||||
///
|
||||
/// Note that all match bodies have to be enclosed by curly braces due to macro parsing limitations.
|
||||
/// It is unfortunately not possible to write `x @ ash::vk::SomeStruct => one_line_expression(),`.
|
||||
///
|
||||
/// ```
|
||||
/// let info = ash::vk::DeviceCreateInfo::default();
|
||||
/// let info: *const ash::vk::BaseInStructure = <*const _>::cast(&info);
|
||||
/// unsafe {
|
||||
/// ash::match_in_struct!(match info {
|
||||
/// info @ ash::vk::DeviceQueueCreateInfo => {
|
||||
/// dbg!(&info); // Unreachable
|
||||
/// }
|
||||
/// info @ ash::vk::DeviceCreateInfo => {
|
||||
/// dbg!(&info);
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// See the [`match_out_struct!`] documentation for an example with implicit return values.
|
||||
#[macro_export]
|
||||
macro_rules! match_in_struct {
|
||||
(match $p:ident { $($bind:ident @ $ty:path => $body:block $(,)?)+ $(_ => $any:block $(,)?)? }) => {
|
||||
match std::ptr::addr_of!((*$p).s_type).read() {
|
||||
$(<$ty as $crate::vk::TaggedStructure>::STRUCTURE_TYPE => {
|
||||
let $bind = $p
|
||||
.cast::<$ty>()
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
$body
|
||||
}),+
|
||||
_ => { $($any)? }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::vk;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -52,3 +52,10 @@ impl From<vk::Extent2D> for vk::Rect2D {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Structures implementing this trait are layout-compatible with [`vk::BaseInStructure`] and
|
||||
/// [`vk::BaseOutStructure`]. Such structures have an `s_type` field indicating its type, which
|
||||
/// must always match the value of [`TaggedStructure::STRUCTURE_TYPE`].
|
||||
pub unsafe trait TaggedStructure {
|
||||
const STRUCTURE_TYPE: vk::StructureType;
|
||||
}
|
||||
|
|
|
@ -497,7 +497,6 @@ pub trait FieldExt {
|
|||
/// array types (e.g. `[f32; 3]`) will be converted to pointer types (e.g. `&[f32; 3]`),
|
||||
/// which is needed for `C` function parameters. Set to `false` for struct definitions.
|
||||
fn type_tokens(&self, is_ffi_param: bool) -> TokenStream;
|
||||
fn is_clone(&self) -> bool;
|
||||
|
||||
/// Whether this is C's `void` type (not to be mistaken with a void _pointer_!)
|
||||
fn is_void(&self) -> bool;
|
||||
|
@ -632,10 +631,6 @@ fn discard_outmost_delimiter(stream: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
impl FieldExt for vkxml::Field {
|
||||
fn is_clone(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn param_ident(&self) -> Ident {
|
||||
let name = self.name.as_deref().unwrap_or("field");
|
||||
let name_corrected = match name {
|
||||
|
@ -1429,15 +1424,9 @@ pub fn derive_default(struct_: &vkxml::Struct, has_lifetime: bool) -> Option<Tok
|
|||
let default_fields = members.clone().map(|field| {
|
||||
let param_ident = field.param_ident();
|
||||
if is_structure_type(field) {
|
||||
let ty = field
|
||||
.type_enums
|
||||
.as_ref()
|
||||
.and_then(|ty| ty.split(',').next());
|
||||
if let Some(variant) = ty {
|
||||
let variant_ident = variant_ident("VkStructureType", variant);
|
||||
|
||||
if field.type_enums.is_some() {
|
||||
quote! {
|
||||
#param_ident: StructureType::#variant_ident
|
||||
#param_ident: Self::STRUCTURE_TYPE
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
|
@ -1567,6 +1556,13 @@ pub fn derive_setters(
|
|||
.clone()
|
||||
.find(|field| field.param_ident() == "p_next");
|
||||
|
||||
let structure_type_field = members
|
||||
.clone()
|
||||
.find(|field| field.param_ident() == "s_type");
|
||||
|
||||
// Must either have both, or none:
|
||||
assert_eq!(next_field.is_some(), structure_type_field.is_some());
|
||||
|
||||
let nofilter_count_members = [
|
||||
("VkPipelineViewportStateCreateInfo", "pViewports"),
|
||||
("VkPipelineViewportStateCreateInfo", "pScissors"),
|
||||
|
@ -1822,7 +1818,24 @@ pub fn derive_setters(
|
|||
quote!(unsafe impl #extends for #name<'_> {})
|
||||
});
|
||||
|
||||
let impl_structure_type_trait = structure_type_field.map(|s_type| {
|
||||
let value = s_type
|
||||
.type_enums
|
||||
.as_deref()
|
||||
.expect("s_type field must have a value in `vk.xml`");
|
||||
|
||||
assert!(!value.contains(','));
|
||||
|
||||
let value = variant_ident("VkStructureType", value);
|
||||
quote! {
|
||||
unsafe impl #lifetime TaggedStructure for #name #lifetime {
|
||||
const STRUCTURE_TYPE: StructureType = StructureType::#value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let q = quote! {
|
||||
#impl_structure_type_trait
|
||||
#(#impl_extend_trait)*
|
||||
#next_trait
|
||||
|
||||
|
|
Loading…
Reference in a new issue