Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Traitify push_next and adding more utility methods for next chains #953

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.69.0
- uses: dtolnay/rust-toolchain@1.75.0
- name: Check ash, ash-window and ash-rewrite
run: cargo check -p ash -p ash-rewrite -p ash-window --all-features
- name: Check ash with no_std
Expand Down
2 changes: 1 addition & 1 deletion ash-rewrite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ categories = [
documentation = "https://docs.rs/ash"
edition = "2021"
# TODO: reevaluate, then update in ci.yml
rust-version = "1.69.0"
rust-version = "1.75.0"

[dependencies]
libloading = { version = "0.8", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion ash-window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ categories = [
"rendering::graphics-api"
]
edition = "2021"
rust-version = "1.69.0"
rust-version = "1.75.0"

[dependencies]
ash = { path = "../ash", version = "0.38", default-features = false, features = ["std"] }
Expand Down
2 changes: 1 addition & 1 deletion ash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ categories = [
"rendering::graphics-api"
]
edition = "2021"
rust-version = "1.69.0"
rust-version = "1.75.0"

[dependencies]
libloading = { version = "0.8", optional = true }
Expand Down
24 changes: 0 additions & 24 deletions ash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,27 +191,3 @@ macro_rules! match_in_struct {
}
};
}

#[cfg(test)]
mod tests {
use super::vk;
use alloc::vec::Vec;
#[test]
fn test_ptr_chains() {
let mut variable_pointers = vk::PhysicalDeviceVariablePointerFeatures::default();
let mut corner = vk::PhysicalDeviceCornerSampledImageFeaturesNV::default();
let chain = alloc::vec![
<*mut _>::cast(&mut variable_pointers),
<*mut _>::cast(&mut corner),
];
let mut device_create_info = vk::DeviceCreateInfo::default()
.push_next(&mut corner)
.push_next(&mut variable_pointers);
let chain2: Vec<*mut vk::BaseOutStructure<'_>> = unsafe {
vk::ptr_chain_iter(&mut device_create_info)
.skip(1)
.collect()
};
assert_eq!(chain, chain2);
}
}
2 changes: 2 additions & 0 deletions ash/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use core::ptr;

use crate::vk;
pub type VkResult<T> = Result<T, vk::Result>;
pub use crate::util::NextChainExt;
pub use vk::TaggedStructure;

impl vk::Result {
#[inline]
Expand Down
224 changes: 223 additions & 1 deletion ash/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::vk;
use crate::{vk, vk::TaggedStructure};
use core::ffi::c_void;
use core::iter::Iterator;
use core::marker::PhantomData;
Expand Down Expand Up @@ -140,3 +140,225 @@ pub fn read_spv<R: io::Read + io::Seek>(x: &mut R) -> io::Result<Vec<u32>> {
}
Ok(result)
}

pub trait NextChainExt<'a>: TaggedStructure<'a> {
/// Prepends the given extension struct between the root and the first pointer. This
/// method only exists on structs that can be passed to a function directly. Only
/// valid extension structs can be pushed into the chain.
/// If the chain looks like `A -> B -> C`, and you call `x.push_next(&mut D)`, then the
/// chain will look like `A -> D -> B -> C`.
///
/// For inline construction of extension structs using the builder pattern, use `with_next` instead.
fn push_next<T: vk::Extends<'a, Self>>(&mut self, next: &'a mut T) {
let next: &mut vk::BaseOutStructure<'a> = next.as_base_mut();
assert!(next.p_next.is_null());
let base: &mut vk::BaseOutStructure<'a> = self.as_base_mut();
next.p_next = base.p_next;
base.p_next = next;
}

/// Builder method to prepends the given extension struct between the root and the first pointer.
/// This method only exists on structs that can be passed to a function directly. Only
/// valid extension structs can be pushed into the chain.
///
/// ```rust
/// use ash::prelude::*;
/// use ash::vk;
/// let mut a = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default();
/// let mut b = vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default();
/// let mut c = vk::PhysicalDeviceMultiDrawFeaturesEXT::default();
/// let base = vk::PhysicalDeviceFeatures2::default()
/// .with_next(&mut a)
/// .with_next(&mut b)
/// .with_next(&mut c);
/// let mut iter = base.iter_next_chain();
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT); // c.s_type
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR); // b.s_type
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR); // a.s_type
/// ```
/// For inline construction of extension structs, use `with_next` instead.
fn with_next<T: vk::Extends<'a, Self>>(mut self, next: &'a mut T) -> Self
where
Self: Sized,
{
self.push_next(next);
self
}
/// Returns a mutable iterator over the entire extension chain attached to `Self`
fn iter_next_chain_mut(&mut self) -> impl Iterator<Item = &'a mut TaggedObject<'a>> {
(0..).scan(self.as_base_mut().p_next, move |p_ptr, _| unsafe {
if p_ptr.is_null() {
return None;
}
let n_ptr = (**p_ptr).p_next;
let old = *p_ptr;
*p_ptr = n_ptr;
Some(TaggedObject::from_raw_mut(old))
})
}
/// Returns an iterator over the entire extension chain attached to `Self`
fn iter_next_chain(&self) -> impl Iterator<Item = &'a TaggedObject<'a>> {
(0..).scan(self.as_base().p_next, |p_ptr, _| unsafe {
if p_ptr.is_null() {
return None;
}
let n_ptr = (**p_ptr).p_next;
let old = *p_ptr;
*p_ptr = n_ptr;
Some(TaggedObject::from_raw(old))
})
}
/// Extend the next chain of the current object with multiple tagged objects
/// ```rust
/// use ash::prelude::*;
/// use ash::vk;
/// use ash::util::TaggedObject;
/// let mut a = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default();
/// let mut b = vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default();
/// let mut c = vk::PhysicalDeviceMultiDrawFeaturesEXT::default();
/// let mut base = vk::PhysicalDeviceFeatures2::default();
/// base.extend([
/// TaggedObject::from_mut(&mut a),
/// TaggedObject::from_mut(&mut b),
/// TaggedObject::from_mut(&mut c)
/// ]);
/// let mut iter = base.iter_next_chain();
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT); // c.s_type
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR); // b.s_type
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR); // a.s_type
/// ```
fn extend(&mut self, nexts: impl IntoIterator<Item = &'a mut TaggedObject<'a>>) {
for next in nexts.into_iter() {
assert!(next.as_base_mut().p_next.is_null());
// Safety: self implements TaggedStructure
let base: &mut vk::BaseOutStructure<'a> = self.as_base_mut();
next.output.p_next = base.p_next;
base.p_next = <*mut TaggedObject<'a>>::cast(next);
}
}
}

/// Blanket implementation of next chain utility methods on all base types
impl<'a, T> NextChainExt<'a> for T where T: vk::BaseTaggedStructure<'a> {}

/// Type-erased object representing a tagged Vulkan structure.
/// It is basically a [`Box<dyn Any>`], but for types implementing [`TaggedStructure`].
#[repr(C)]
pub union TaggedObject<'a> {
output: vk::BaseOutStructure<'a>,
input: vk::BaseInStructure<'a>,
}
impl vk::StructureType {
pub const ASH_DYNAMIC: Self = Self(-1);
}

/// [`TaggedObject`]s are layout-compatible with [`vk::BaseInStructure`] and
/// [`vk::BaseOutStructure`].
unsafe impl<'a> TaggedStructure<'a> for TaggedObject<'a> {
/// Querying the tag of a [`TaggedObject`] statically using [`TaggedStructure::STRUCTURE_TYPE`]
/// returns [`vk::StructureType::ASH_DYNAMIC`], since the actual tag is dynamic.
/// To query the tag of a [`TaggedObject`] dynamically, use [`TaggedObject::tag`].
const STRUCTURE_TYPE: vk::StructureType = vk::StructureType::ASH_DYNAMIC;
}

impl<'a> NextChainExt<'a> for TaggedObject<'a> {}

/// [`TaggedObject`]s can be extended with ANY tagged objects. It is up to the user to ensure that they're
/// calling it correctly.
/// ```rust
/// use ash::prelude::*;
/// use ash::vk;
/// use ash::util::TaggedObject;
/// let mut a = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default();
/// let mut b = vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default();
/// let mut c = vk::PhysicalDeviceMultiDrawFeaturesEXT::default();
/// let mut d = vk::ApplicationInfo::default();
/// let mut base = vk::PhysicalDeviceFeatures2::default();
/// let base = TaggedObject::from_mut(&mut base);
/// base.push_next(&mut a);
/// base.push_next(&mut b);
/// base.push_next(&mut c);
/// base.push_next(&mut d);
///
///
/// let mut iter = base.iter_next_chain();
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::APPLICATION_INFO); // d.s_type
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT); // c.s_type
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR); // b.s_type
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR); // a.s_type
/// ```
unsafe impl<'a, T: TaggedStructure<'a> + ?Sized> vk::Extends<'a, TaggedObject<'a>> for T {}
/// [`TaggedObject`]s can extend ANY other tagged objects.
/// ```rust
/// use ash::prelude::*;
/// use ash::vk;
/// use ash::util::TaggedObject;
/// let mut a = vk::ApplicationInfo::default();
/// let mut a = TaggedObject::from_mut(&mut a);
/// let mut base = vk::PhysicalDeviceFeatures2::default();
/// base.push_next(a);
///
/// let mut iter = base.iter_next_chain();
/// assert_eq!(iter.next().unwrap().tag(), vk::StructureType::APPLICATION_INFO); // a.s_type
/// ```
unsafe impl<'a, T: vk::BaseTaggedStructure<'a> + ?Sized> vk::Extends<'a, T> for TaggedObject<'a> {}

impl<'a> TaggedObject<'a> {
pub unsafe fn from_raw(obj: *const vk::BaseInStructure<'a>) -> &'a Self {
&*(obj as *const Self)
}

pub unsafe fn from_raw_mut(obj: *mut vk::BaseOutStructure<'a>) -> &'a mut Self {
&mut *(obj as *mut Self)
}

pub fn from_ref<T: TaggedStructure<'a> + ?Sized>(obj: &T) -> &Self {
unsafe { &*(<*const T>::cast(obj)) }
}

pub fn from_mut<T: TaggedStructure<'a> + ?Sized>(obj: &mut T) -> &mut Self {
unsafe { &mut *(<*mut T>::cast(obj)) }
}
pub fn next(&self) -> Option<&Self> {
unsafe {
if self.as_base().p_next.is_null() {
return None;
}
Some(TaggedObject::from_raw(self.as_base().p_next))
}
}
pub fn next_mut(&mut self) -> Option<&mut Self> {
unsafe {
if self.as_base().p_next.is_null() {
return None;
}
Some(TaggedObject::from_raw_mut(self.as_base_mut().p_next))
}
}
pub fn tag(&self) -> vk::StructureType {
self.as_base().s_type
}
pub fn downcast_ref<T: TaggedStructure<'a>>(&self) -> Option<&T> {
unsafe {
if self.tag() == T::STRUCTURE_TYPE {
Some(&*<*const vk::BaseInStructure<'_>>::cast(&self.input))
} else {
None
}
}
}
pub fn downcast_mut<T: TaggedStructure<'a>>(&mut self) -> Option<&mut T> {
unsafe {
if self.tag() == T::STRUCTURE_TYPE {
Some(&mut *<*mut vk::BaseOutStructure<'_>>::cast(
&mut self.output,
))
} else {
None
}
}
}
pub fn is<T: TaggedStructure<'a>>(&self) -> bool {
self.tag() == T::STRUCTURE_TYPE
}
}
20 changes: 4 additions & 16 deletions ash/src/vk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,7 @@ pub use prelude::*;
pub mod native;
mod platform_types;
pub use platform_types::*;
/// Iterates through the pointer chain. Includes the item that is passed into the function.
/// Stops at the last [`BaseOutStructure`] that has a null [`BaseOutStructure::p_next`] field.
pub(crate) unsafe fn ptr_chain_iter<T: ?Sized>(
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 = (**p_ptr).p_next;
let old = *p_ptr;
*p_ptr = n_ptr;
Some(old)
})
}

pub trait Handle: Sized {
const TYPE: ObjectType;
fn as_raw(self) -> u64;
Expand All @@ -64,3 +49,6 @@ pub trait Handle: Sized {
self.as_raw() == 0
}
}

pub unsafe trait BaseTaggedStructure<'a>: TaggedStructure<'a> {}
pub unsafe trait Extends<'a, T: ?Sized>: TaggedStructure<'a> {}
Loading