From 7a61d3f480f51b8f989b8d5f86292a249d3ac17a Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 2 Dec 2024 18:01:45 +0100 Subject: [PATCH] extensions: Remove `read_into_defaulted_vector()` to let caller pass `pNext` 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. --- Changelog.md | 11 ++ ash/src/extensions/ext/tooling_info.rs | 38 +++++- ash/src/extensions/khr/cooperative_matrix.rs | 47 +++++-- .../khr/pipeline_executable_properties.rs | 129 ++++++++++++++---- ash/src/prelude.rs | 37 ----- 5 files changed, 181 insertions(+), 81 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2f72e646a..68c452f0d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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. diff --git a/ash/src/extensions/ext/tooling_info.rs b/ash/src/extensions/ext/tooling_info.rs index 0f00f08b8..e756b6276 100644 --- a/ash/src/extensions/ext/tooling_info.rs +++ b/ash/src/extensions/ext/tooling_info.rs @@ -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 { + 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) + } + /// + /// + /// 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>> { - 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(()) } } diff --git a/ash/src/extensions/khr/cooperative_matrix.rs b/ash/src/extensions/khr/cooperative_matrix.rs index d8c0ef510..1a15eda34 100644 --- a/ash/src/extensions/khr/cooperative_matrix.rs +++ b/ash/src/extensions/khr/cooperative_matrix.rs @@ -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 { + 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) + } + /// + /// + /// 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>> { - 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(()) } } diff --git a/ash/src/extensions/khr/pipeline_executable_properties.rs b/ash/src/extensions/khr/pipeline_executable_properties.rs index d10b2c6ff..a42f7baf7 100644 --- a/ash/src/extensions/khr/pipeline_executable_properties.rs +++ b/ash/src/extensions/khr/pipeline_executable_properties.rs @@ -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 { + 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) + } + /// + /// + /// 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>> { - 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 { + 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) } /// + /// + /// 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>> { - 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 { + 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) } /// + /// + /// 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>> { - 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(()) } } diff --git a/ash/src/prelude.rs b/ash/src/prelude.rs index 1341960f4..74aa3f186 100644 --- a/ash/src/prelude.rs +++ b/ash/src/prelude.rs @@ -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, - T: Default + Clone, ->( - f: impl Fn(&mut N, *mut T) -> vk::Result, -) -> VkResult> -where - >::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 + Copy>( f: &mut core::fmt::Formatter<'_>,