Skip to content

Commit

Permalink
extensions: Remove read_into_defaulted_vector() to let caller pass …
Browse files Browse the repository at this point in the history
…`pNext` (#966)

We've often discussed `pNext` chains in output arrays (i.e. #465, #588,
 #744), and I always wondered why `read_into_defaulted_vector()` still
existed: turns out this helper function was only hiding a few more
remaining cases where the caller was previously _not_ able to manually
extend the `pNext` chain to request arbitrary more structures to be
filled with information.

Replace these remaining cases with a separate `_len()` getter, and have
the main function take a caller-allocated `&mut [T]` slice where they
can initialize the target struct including `pNext` pointer chains.
  • Loading branch information
MarijnS95 authored Dec 4, 2024
1 parent eca7251 commit 7dcf57b
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 81 deletions.
11 changes: 11 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `VK_EXT_metal_objects` device extension (#942)
- Added `VK_AMD_anti_lag` device extension (#943)

### Changed

- Enable passing mutable arrays of `pNext`-initialized structs for these remaining extension functions: (#966)
- `VK_EXT_tooling_info`: `get_physical_device_tool_properties()`;
- `VK_KHR_cooperative_matrix`: `get_physical_device_cooperative_matrix_properties()`;
- `VK_KHR_pipeline_executable_properties`:
- `get_pipeline_executable_internal_representations()`;
- `get_pipeline_executable_properties()`;
- `get_pipeline_executable_statistics()`.
The expected length of this array can be queried with the respective `*_len()` variant of these functions.

## [0.38.0] - 2024-04-01

With over two years of collecting breaking changes (since the `0.37.0` release in March 2022), April 2024 marks the next breaking release of `ash`. This release introduces an overhaul of all Vulkan structures, restructures modules around extensions, and separates extension wrappers between `Instance` and `Device` functions. The crate contains all bindings defined by the latest `1.3.281` Vulkan specification, and many old and new extensions have received a hand-written extension wrapper. For a full overview of all individual changes, see the list at the end of this post.
Expand Down
38 changes: 33 additions & 5 deletions ash/src/extensions/ext/tooling_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,45 @@
use crate::prelude::*;
use crate::vk;
use alloc::vec::Vec;
use core::mem;
use core::ptr;

impl crate::ext::tooling_info::Instance {
/// Retrieve the number of elements to pass to [`get_physical_device_tool_properties()`][Self::get_physical_device_tool_properties()]
#[inline]
pub unsafe fn get_physical_device_tool_properties_len(
&self,
physical_device: vk::PhysicalDevice,
) -> VkResult<usize> {
let mut count = mem::MaybeUninit::uninit();
(self.fp.get_physical_device_tool_properties_ext)(
physical_device,
count.as_mut_ptr(),
ptr::null_mut(),
)
.assume_init_on_success(count)
.map(|c| c as usize)
}

/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceToolPropertiesEXT.html>
///
/// Call [`get_physical_device_tool_properties_len()`][Self::get_physical_device_tool_properties_len()] to query the number of elements to pass to `out`.
/// Be sure to [`Default::default()`]-initialize these elements and optionally set their `p_next` pointer.
#[inline]
#[doc(alias = "vkGetPhysicalDeviceToolPropertiesEXT")]
pub unsafe fn get_physical_device_tool_properties(
&self,
physical_device: vk::PhysicalDevice,
) -> VkResult<Vec<vk::PhysicalDeviceToolPropertiesEXT<'_>>> {
read_into_defaulted_vector(|count, data| {
(self.fp.get_physical_device_tool_properties_ext)(physical_device, count, data)
})
out: &mut [vk::PhysicalDeviceToolPropertiesEXT<'_>],
) -> VkResult<()> {
let mut count = out.len() as u32;
(self.fp.get_physical_device_tool_properties_ext)(
physical_device,
&mut count,
out.as_mut_ptr(),
)
.result()?;
assert_eq!(count as usize, out.len());
Ok(())
}
}
47 changes: 36 additions & 11 deletions ash/src/extensions/khr/cooperative_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,48 @@
use crate::prelude::*;
use crate::vk;
use alloc::vec::Vec;
use core::mem;
use core::ptr;

impl crate::khr::cooperative_matrix::Instance {
/// Retrieve the number of elements to pass to [`get_physical_device_cooperative_matrix_properties()`][Self::get_physical_device_cooperative_matrix_properties()]
#[inline]
pub unsafe fn get_physical_device_cooperative_matrix_properties_len(
&self,
physical_device: vk::PhysicalDevice,
) -> VkResult<usize> {
let mut count = mem::MaybeUninit::uninit();
(self
.fp
.get_physical_device_cooperative_matrix_properties_khr)(
physical_device,
count.as_mut_ptr(),
ptr::null_mut(),
)
.assume_init_on_success(count)
.map(|c| c as usize)
}

/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR.html>
///
/// Call [`get_physical_device_cooperative_matrix_properties_len()`][Self::get_physical_device_cooperative_matrix_properties_len()] to query the number of elements to pass to `out`.
/// Be sure to [`Default::default()`]-initialize these elements and optionally set their `p_next` pointer.
#[inline]
pub unsafe fn get_physical_device_cooperative_matrix_properties(
&self,
physical_device: vk::PhysicalDevice,
) -> VkResult<Vec<vk::CooperativeMatrixPropertiesKHR<'_>>> {
read_into_defaulted_vector(|count, data| {
(self
.fp
.get_physical_device_cooperative_matrix_properties_khr)(
physical_device,
count,
data,
)
})
out: &mut [vk::CooperativeMatrixPropertiesKHR<'_>],
) -> VkResult<()> {
let mut count = out.len() as u32;
(self
.fp
.get_physical_device_cooperative_matrix_properties_khr)(
physical_device,
&mut count,
out.as_mut_ptr(),
)
.result()?;
assert_eq!(count as usize, out.len());
Ok(())
}
}
129 changes: 101 additions & 28 deletions ash/src/extensions/khr/pipeline_executable_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,127 @@
use crate::prelude::*;
use crate::vk;
use alloc::vec::Vec;
use core::mem;
use core::ptr;

impl crate::khr::pipeline_executable_properties::Device {
/// Retrieve the number of elements to pass to [`get_pipeline_executable_internal_representations()`][Self::get_pipeline_executable_internal_representations()]
#[inline]
pub unsafe fn get_pipeline_executable_internal_representations_len(
&self,
executable_info: &vk::PipelineExecutableInfoKHR<'_>,
) -> VkResult<usize> {
let mut count = mem::MaybeUninit::uninit();
(self.fp.get_pipeline_executable_internal_representations_khr)(
self.handle,
executable_info,
count.as_mut_ptr(),
ptr::null_mut(),
)
.assume_init_on_success(count)
.map(|c| c as usize)
}

/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPipelineExecutableInternalRepresentationsKHR.html>
///
/// Call [`get_pipeline_executable_internal_representations_len()`][Self::get_pipeline_executable_internal_representations_len()] to query the number of elements to pass to `out`.
/// Be sure to [`Default::default()`]-initialize these elements and optionally set their `p_next` pointer.
#[inline]
#[doc(alias = "vkGetPipelineExecutableInternalRepresentationsKHR")]
pub unsafe fn get_pipeline_executable_internal_representations(
&self,
executable_info: &vk::PipelineExecutableInfoKHR<'_>,
) -> VkResult<Vec<vk::PipelineExecutableInternalRepresentationKHR<'_>>> {
read_into_defaulted_vector(|count, data| {
(self.fp.get_pipeline_executable_internal_representations_khr)(
self.handle,
executable_info,
count,
data,
)
})
out: &mut [vk::PipelineExecutableInternalRepresentationKHR<'_>],
) -> VkResult<()> {
let mut count = out.len() as u32;
(self.fp.get_pipeline_executable_internal_representations_khr)(
self.handle,
executable_info,
&mut count,
out.as_mut_ptr(),
)
.result()?;
assert_eq!(count as usize, out.len());
Ok(())
}

/// Retrieve the number of elements to pass to [`get_pipeline_executable_properties()`][Self::get_pipeline_executable_properties()]
#[inline]
pub unsafe fn get_pipeline_executable_properties_len(
&self,
pipeline_info: &vk::PipelineInfoKHR<'_>,
) -> VkResult<usize> {
let mut count = mem::MaybeUninit::uninit();
(self.fp.get_pipeline_executable_properties_khr)(
self.handle,
pipeline_info,
count.as_mut_ptr(),
ptr::null_mut(),
)
.assume_init_on_success(count)
.map(|c| c as usize)
}

/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPipelineExecutablePropertiesKHR.html>
///
/// Call [`get_pipeline_executable_properties_len()`][Self::get_pipeline_executable_properties_len()] to query the number of elements to pass to `out`.
/// Be sure to [`Default::default()`]-initialize these elements and optionally set their `p_next` pointer.
#[inline]
#[doc(alias = "vkGetPipelineExecutablePropertiesKHR")]
pub unsafe fn get_pipeline_executable_properties(
&self,
pipeline_info: &vk::PipelineInfoKHR<'_>,
) -> VkResult<Vec<vk::PipelineExecutablePropertiesKHR<'_>>> {
read_into_defaulted_vector(|count, data| {
(self.fp.get_pipeline_executable_properties_khr)(
self.handle,
pipeline_info,
count,
data,
)
})
out: &mut [vk::PipelineExecutablePropertiesKHR<'_>],
) -> VkResult<()> {
let mut count = out.len() as u32;
(self.fp.get_pipeline_executable_properties_khr)(
self.handle,
pipeline_info,
&mut count,
out.as_mut_ptr(),
)
.result()?;
assert_eq!(count as usize, out.len());
Ok(())
}

/// Retrieve the number of elements to pass to [`get_pipeline_executable_statistics()`][Self::get_pipeline_executable_statistics()]
#[inline]
pub unsafe fn get_pipeline_executable_statistics_len(
&self,
executable_info: &vk::PipelineExecutableInfoKHR<'_>,
) -> VkResult<usize> {
let mut count = mem::MaybeUninit::uninit();
(self.fp.get_pipeline_executable_statistics_khr)(
self.handle,
executable_info,
count.as_mut_ptr(),
ptr::null_mut(),
)
.assume_init_on_success(count)
.map(|c| c as usize)
}

/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPipelineExecutableStatisticsKHR.html>
///
/// Call [`get_pipeline_executable_statistics_len()`][Self::get_pipeline_executable_statistics_len()] to query the number of elements to pass to `out`.
/// Be sure to [`Default::default()`]-initialize these elements and optionally set their `p_next` pointer.
#[inline]
#[doc(alias = "vkGetPipelineExecutableStatisticsKHR")]
pub unsafe fn get_pipeline_executable_statistics(
&self,
executable_info: &vk::PipelineExecutableInfoKHR<'_>,
) -> VkResult<Vec<vk::PipelineExecutableStatisticKHR<'_>>> {
read_into_defaulted_vector(|count, data| {
(self.fp.get_pipeline_executable_statistics_khr)(
self.handle,
executable_info,
count,
data,
)
})
out: &mut [vk::PipelineExecutableStatisticKHR<'_>],
) -> VkResult<()> {
let mut count = out.len() as u32;
(self.fp.get_pipeline_executable_statistics_khr)(
self.handle,
executable_info,
&mut count,
out.as_mut_ptr(),
)
.result()?;
assert_eq!(count as usize, out.len());
Ok(())
}
}
37 changes: 0 additions & 37 deletions ash/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,43 +64,6 @@ where
}
}

/// Repeatedly calls `f` until it does not return [`vk::Result::INCOMPLETE`] anymore, ensuring all
/// available data has been read into the vector.
///
/// Items in the target vector are [`default()`][Default::default()]-initialized which is required
/// for [`vk::BaseOutStructure`]-like structs where [`vk::BaseOutStructure::s_type`] needs to be a
/// valid type and [`vk::BaseOutStructure::p_next`] a valid or [`null`][ptr::null_mut()]
/// pointer.
///
/// See for example [`vkEnumerateInstanceExtensionProperties`]: the number of available items may
/// change between calls; [`vk::Result::INCOMPLETE`] is returned when the count increased (and the
/// vector is not large enough after querying the initial size), requiring Ash to try again.
///
/// [`vkEnumerateInstanceExtensionProperties`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html
pub(crate) unsafe fn read_into_defaulted_vector<
N: Copy + Default + TryInto<usize>,
T: Default + Clone,
>(
f: impl Fn(&mut N, *mut T) -> vk::Result,
) -> VkResult<Vec<T>>
where
<N as TryInto<usize>>::Error: core::fmt::Debug,
{
loop {
let mut count = N::default();
f(&mut count, ptr::null_mut()).result()?;
let mut data = alloc::vec![Default::default(); count.try_into().expect("`N` failed to convert to `usize`")];

let err_code = f(&mut count, data.as_mut_ptr());
if err_code != vk::Result::INCOMPLETE {
break err_code.set_vec_len_on_success(
data,
count.try_into().expect("`N` failed to convert to `usize`"),
);
}
}
}

#[cfg(feature = "debug")]
pub(crate) fn debug_flags<Value: Into<u64> + Copy>(
f: &mut core::fmt::Formatter<'_>,
Expand Down

0 comments on commit 7dcf57b

Please sign in to comment.