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)
|
- Added `VK_EXT_image_drm_format_modifier` device extension (#603)
|
||||||
- Update Vulkan-Headers to 1.3.219 (#605, #608, #619)
|
- 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_EXT_sample_locations` device extension (#616)
|
||||||
- Added `VK_NV_coverage_reduction_mode` device extension (#617)
|
- Added `VK_NV_coverage_reduction_mode` device extension (#617)
|
||||||
- Added `VK_KHR_ray_tracing_maintenance1` device extension (#620)
|
- 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::vk;
|
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]`),
|
/// 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.
|
/// which is needed for `C` function parameters. Set to `false` for struct definitions.
|
||||||
fn type_tokens(&self, is_ffi_param: bool) -> TokenStream;
|
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_!)
|
/// Whether this is C's `void` type (not to be mistaken with a void _pointer_!)
|
||||||
fn is_void(&self) -> bool;
|
fn is_void(&self) -> bool;
|
||||||
|
@ -632,10 +631,6 @@ fn discard_outmost_delimiter(stream: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldExt for vkxml::Field {
|
impl FieldExt for vkxml::Field {
|
||||||
fn is_clone(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn param_ident(&self) -> Ident {
|
fn param_ident(&self) -> Ident {
|
||||||
let name = self.name.as_deref().unwrap_or("field");
|
let name = self.name.as_deref().unwrap_or("field");
|
||||||
let name_corrected = match name {
|
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 default_fields = members.clone().map(|field| {
|
||||||
let param_ident = field.param_ident();
|
let param_ident = field.param_ident();
|
||||||
if is_structure_type(field) {
|
if is_structure_type(field) {
|
||||||
let ty = field
|
if field.type_enums.is_some() {
|
||||||
.type_enums
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|ty| ty.split(',').next());
|
|
||||||
if let Some(variant) = ty {
|
|
||||||
let variant_ident = variant_ident("VkStructureType", variant);
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#param_ident: StructureType::#variant_ident
|
#param_ident: Self::STRUCTURE_TYPE
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -1567,6 +1556,13 @@ pub fn derive_setters(
|
||||||
.clone()
|
.clone()
|
||||||
.find(|field| field.param_ident() == "p_next");
|
.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 = [
|
let nofilter_count_members = [
|
||||||
("VkPipelineViewportStateCreateInfo", "pViewports"),
|
("VkPipelineViewportStateCreateInfo", "pViewports"),
|
||||||
("VkPipelineViewportStateCreateInfo", "pScissors"),
|
("VkPipelineViewportStateCreateInfo", "pScissors"),
|
||||||
|
@ -1822,7 +1818,24 @@ pub fn derive_setters(
|
||||||
quote!(unsafe impl #extends for #name<'_> {})
|
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! {
|
let q = quote! {
|
||||||
|
#impl_structure_type_trait
|
||||||
#(#impl_extend_trait)*
|
#(#impl_extend_trait)*
|
||||||
#next_trait
|
#next_trait
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue