use super::{BaseOutStructure, StructureType};
pub unsafe trait TaggedStructure<'a>: Sized {
const STRUCTURE_TYPE: StructureType;
fn push<'b: 'a, T: Extends<Self> + TaggedStructure<'b>>(mut self, next: &'a mut T) -> Self {
let slf_base = unsafe { &mut *<*mut _>::cast::<BaseOutStructure<'_>>(&mut self) };
let next_base = unsafe { &mut *<*mut T>::cast::<BaseOutStructure<'_>>(next) };
assert!(
next_base.p_next.is_null(),
"push() expects a struct without an existing p_next pointer chain (equal to NULL)"
);
next_base.p_next = slf_base.p_next;
slf_base.p_next = next_base;
self
}
unsafe fn extend<'b: 'a, T: Extends<Self> + TaggedStructure<'b>>(
mut self,
next: &'a mut T,
) -> Self {
let slf_base = unsafe { &mut *<*mut _>::cast::<BaseOutStructure<'_>>(&mut self) };
let next_base = <*mut T>::cast::<BaseOutStructure<'_>>(next);
let last_next = unsafe { ptr_chain_iter(next).last().unwrap() };
unsafe { (*last_next).p_next = slf_base.p_next };
slf_base.p_next = next_base;
self
}
}
pub unsafe trait Extends<B> {}
pub(crate) unsafe fn ptr_chain_iter<'a, T: TaggedStructure<'a>>(
ptr: &mut T,
) -> impl Iterator<Item = *mut BaseOutStructure<'_>> {
let ptr = <*mut T>::cast::<BaseOutStructure<'_>>(ptr);
(0..).scan(ptr, |p_ptr, _| {
if p_ptr.is_null() {
return None;
}
let n_ptr = unsafe { (**p_ptr).p_next };
let old = *p_ptr;
*p_ptr = n_ptr;
Some(old)
})
}
#[macro_export]
macro_rules! match_out_struct {
(match $p:ident { $($bind:ident @ $ty:path => $body:block $(,)?)+ $(_ => $any:block $(,)?)? }) => {
match core::ptr::addr_of!((*$p).s_type).read() {
$(<$ty as $crate::vk::TaggedStructure>::STRUCTURE_TYPE => {
let $bind = $p
.cast::<$ty>()
.as_mut()
.unwrap();
$body
}),+
_ => { $($any)? }
}
};
}
#[macro_export]
macro_rules! match_in_struct {
(match $p:ident { $($bind:ident @ $ty:path => $body:block $(,)?)+ $(_ => $any:block $(,)?)? }) => {
match core::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 crate::vk::{self, TaggedStructure as _};
#[test]
fn test_ptr_chains() {
let mut variable_pointers = vk::PhysicalDeviceVariablePointerFeatures::default();
let mut corner = vk::PhysicalDeviceCornerSampledImageFeaturesNV::default();
let chain = vec![
<*mut _>::cast(&mut variable_pointers),
<*mut _>::cast(&mut corner),
];
let mut device_create_info = vk::DeviceCreateInfo::default()
.push(&mut corner)
.push(&mut variable_pointers);
let chain2: Vec<*mut vk::BaseOutStructure<'_>> = unsafe {
super::ptr_chain_iter(&mut device_create_info)
.skip(1)
.collect()
};
assert_eq!(chain, chain2);
}
#[test]
#[should_panic]
fn disallow_nested_ptr_chains() {
let mut generated_commands =
vk::PhysicalDeviceDeviceGeneratedCommandsFeaturesEXT::default();
let mut private_data = vk::PhysicalDevicePrivateDataFeatures {
p_next: <*mut _>::cast(&mut generated_commands),
..Default::default()
};
let _device_create_info = vk::DeviceCreateInfo::default().push(&mut private_data);
}
#[test]
fn test_nested_ptr_chains() {
let mut generated_commands =
vk::PhysicalDeviceDeviceGeneratedCommandsFeaturesEXT::default();
let mut private_data = vk::PhysicalDevicePrivateDataFeatures {
p_next: <*mut _>::cast(&mut generated_commands),
..Default::default()
};
let mut variable_pointers = vk::PhysicalDeviceVariablePointerFeatures::default();
let mut corner = vk::PhysicalDeviceCornerSampledImageFeaturesNV::default();
let chain = vec![
<*mut _>::cast(&mut private_data),
<*mut _>::cast(&mut generated_commands),
<*mut _>::cast(&mut variable_pointers),
<*mut _>::cast(&mut corner),
];
let mut device_create_info = vk::DeviceCreateInfo::default()
.push(&mut corner)
.push(&mut variable_pointers);
device_create_info = unsafe { device_create_info.extend(&mut private_data) };
let chain2: Vec<*mut vk::BaseOutStructure<'_>> = unsafe {
super::ptr_chain_iter(&mut device_create_info)
.skip(1)
.collect()
};
assert_eq!(chain, chain2);
}
#[test]
fn test_use_struct_after_pointer_chain() {
let mut layers = vec![];
let mut api =
vk::PhysicalDeviceLayeredApiPropertiesListKHR::default().layered_apis(&mut layers);
let _pdev_props = vk::PhysicalDeviceProperties2::default().push(&mut api);
dbg!(&api);
dbg!(&layers);
}
#[test]
fn test_debug_flags() {
assert_eq!(
format!(
"{:?}",
vk::AccessFlagBits::INDIRECT_COMMAND_READ
| vk::AccessFlagBits::VERTEX_ATTRIBUTE_READ
),
"INDIRECT_COMMAND_READ | VERTEX_ATTRIBUTE_READ"
);
}
#[test]
fn test_debug_enum() {
assert_eq!(format!("{:?}", vk::ChromaLocation::MIDPOINT), "MIDPOINT");
}
}