vulkan_layer/
vk_utils.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{collections::BTreeSet, ffi::CStr, fmt::Debug, mem::MaybeUninit, ptr::NonNull};
16
17use ash::vk;
18use num_traits::Zero;
19
20use crate::global_simple_intercept::{ApiVersion, Extension, Feature};
21
22/// A chain of
23/// [`VkBaseOutStructure`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBaseOutStructure.html).
24///
25/// This type provides an easy way to visit the `*mut c_void`
26/// [`pNext` chain](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fundamentals-validusage-pNext).
27/// One can convert an `Option<&mut vk::BaseOutStructure>` to [`VulkanBaseOutStructChain`].
28///
29/// # Examples
30///
31/// ```
32/// use ash::vk;
33/// use vulkan_layer::VulkanBaseOutStructChain;
34///
35/// let mut timeline_semaphore_feature = vk::PhysicalDeviceTimelineSemaphoreFeatures::builder();
36/// let mut ycbcr_feature = vk::PhysicalDeviceSamplerYcbcrConversionFeatures::builder();
37/// let features2 = vk::PhysicalDeviceFeatures2::builder()
38///     .push_next(&mut timeline_semaphore_feature)
39///     .push_next(&mut ycbcr_feature);
40/// let out_chain = features2.p_next.cast::<vk::BaseOutStructure>();
41/// let mut out_chain: VulkanBaseOutStructChain = unsafe { out_chain.as_mut() }.into();
42/// // `push_next` prepends the element to the pNext chain.
43/// assert_eq!(
44///     out_chain.next().unwrap().s_type,
45///     vk::StructureType::PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES
46/// );
47/// assert_eq!(
48///     out_chain.next().unwrap().s_type,
49///     vk::StructureType::PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES
50/// );
51/// assert!(out_chain.next().is_none());
52/// ```
53pub struct VulkanBaseOutStructChain<'a> {
54    next: Option<&'a mut vk::BaseOutStructure>,
55}
56
57impl<'a> From<Option<&'a mut vk::BaseOutStructure>> for VulkanBaseOutStructChain<'a> {
58    fn from(next: Option<&'a mut vk::BaseOutStructure>) -> Self {
59        Self { next }
60    }
61}
62
63impl<'a> Iterator for VulkanBaseOutStructChain<'a> {
64    type Item = &'a mut vk::BaseOutStructure;
65
66    fn next(&mut self) -> Option<Self::Item> {
67        self.next.take().inspect(|element| {
68            self.next = unsafe { element.p_next.as_mut() };
69        })
70    }
71}
72
73/// A chain of
74/// [`VkBaseInStructure`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBaseInStructure.html).
75///
76/// This type provides an easy way to visit the `*const c_void`
77/// [`pNext` chain](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fundamentals-validusage-pNext).
78/// One can convert an `Option<&vk::BaseInStructure>` to [`VulkanBaseInStructChain`].
79///
80/// # Examples
81/// ```
82/// use ash::vk;
83/// use vulkan_layer::VulkanBaseInStructChain;
84///
85/// let mut external_memory_image_info = vk::ExternalMemoryImageCreateInfo::builder();
86/// let mut image_format_list_info = vk::ImageFormatListCreateInfo::builder();
87/// let image_create_info = vk::ImageCreateInfo::builder()
88///     .push_next(&mut external_memory_image_info)
89///     .push_next(&mut image_format_list_info);
90/// let in_chain = image_create_info.p_next.cast::<vk::BaseInStructure>();
91/// let mut in_chain: VulkanBaseInStructChain = unsafe { in_chain.as_ref() }.into();
92/// // `push_next` prepends the element to the pNext chain.
93/// assert_eq!(
94///     in_chain.next().unwrap().s_type,
95///     vk::StructureType::IMAGE_FORMAT_LIST_CREATE_INFO
96/// );
97/// assert_eq!(
98///     in_chain.next().unwrap().s_type,
99///     vk::StructureType::EXTERNAL_MEMORY_IMAGE_CREATE_INFO
100/// );
101/// assert!(in_chain.next().is_none());
102/// ```
103pub struct VulkanBaseInStructChain<'a> {
104    next: Option<&'a vk::BaseInStructure>,
105}
106
107impl<'a> From<Option<&'a vk::BaseInStructure>> for VulkanBaseInStructChain<'a> {
108    fn from(next: Option<&'a vk::BaseInStructure>) -> Self {
109        Self { next }
110    }
111}
112
113impl<'a> Iterator for VulkanBaseInStructChain<'a> {
114    type Item = &'a vk::BaseInStructure;
115
116    fn next(&mut self) -> Option<Self::Item> {
117        self.next.take().inspect(|element| {
118            self.next = unsafe { element.p_next.as_ref() };
119        })
120    }
121}
122
123/// Used to extend an iterator of [`Feature`].
124pub trait IsCommandEnabled {
125    /// Given a list of [`Feature`]s where the command is defined, the Vulkan API version and a set
126    /// of enabled extensions, returns if the command is enabled.
127    ///
128    /// One command with the same name can appear in different features, e.g.
129    /// [`vkCmdBindVertexBuffers2EXT`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdBindVertexBuffers2EXT.html)
130    /// is defined in both
131    /// [`VK_EXT_extended_dynamic_state`](<https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state.html#:~:text=New%20Commands-,vkCmdBindVertexBuffers2EXT,-vkCmdSetCullModeEXT>)
132    /// and
133    /// [`VK_EXT_shader_object`](<https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_shader_object.html#:~:text=vkCmdBindShadersEXT-,vkCmdBindVertexBuffers2EXT,-vkCmdSetAlphaToCoverageEnableEXT>).
134    /// `vkGetDeviceProcAddr` should return a valid function pointer `vkCmdBindVertexBuffers2EXT` if
135    /// either extension is enabled.
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// use ash::vk;
141    /// use std::collections::BTreeSet;
142    /// use vulkan_layer::{
143    ///     unstable_api::{ApiVersion, Feature, IsCommandEnabled},
144    ///     Extension,
145    /// };
146    ///
147    /// let features: Vec<Feature> = vec![
148    ///     Extension::EXTExtendedDynamicState.into(),
149    ///     Extension::EXTShaderObject.into(),
150    /// ];
151    /// assert!(features.is_command_enabled(
152    ///     &ApiVersion::V1_1,
153    ///     &BTreeSet::from([Extension::EXTExtendedDynamicState.into()])
154    /// ));
155    /// assert!(features.is_command_enabled(
156    ///     &ApiVersion::V1_1,
157    ///     &BTreeSet::from([Extension::EXTShaderObject.into()])
158    /// ));
159    /// assert!(!features.is_command_enabled(&ApiVersion::V1_1, &BTreeSet::new()));
160    /// ```
161    #[allow(clippy::wrong_self_convention)]
162    fn is_command_enabled(
163        self,
164        api_version: &ApiVersion,
165        enabled_extensions: &BTreeSet<Extension>,
166    ) -> bool;
167}
168
169impl<'a, T: IntoIterator<Item = &'a Feature>> IsCommandEnabled for T {
170    fn is_command_enabled(
171        self,
172        api_version: &ApiVersion,
173        enabled_extensions: &BTreeSet<Extension>,
174    ) -> bool {
175        self.into_iter().any(|feature| match feature {
176            Feature::Extension(extension) => enabled_extensions.contains(extension),
177            Feature::Core(version) => version <= api_version,
178        })
179    }
180}
181
182/// Similar to [`std::slice::from_raw_parts`], but allow null pointer for 0 length slice.
183///
184/// # Safety
185/// Follow the safety requirements for [`std::slice::from_raw_parts`] with one major difference: if
186/// `len` is `0`, `data` can be a null pointer, unlike [`std::slice::from_raw_parts`].
187#[deny(unsafe_op_in_unsafe_fn)]
188pub(crate) unsafe fn slice_from_raw_parts<'a, T>(
189    data: *const T,
190    len: impl TryInto<usize, Error = impl Debug>,
191) -> &'a [T] {
192    let len: usize = len
193        .try_into()
194        .expect("length must be able to cast to usize");
195    if len == 0 {
196        return &[];
197    }
198    // Safety: since `len` isn't 0 at this point, the caller guarantees that the safety requirement
199    // for `std::slice::from_raw_parts` is met.
200    unsafe { std::slice::from_raw_parts(data, len) }
201}
202
203/// Convert from a slice of i8 pointers to an iterator of strings.
204///
205/// Usually used to parse the C style C string array like `VkInstanceInfo::ppEnabledExtensionNames`.
206///
207/// # Safety
208/// Every entry of `cstr_ptrs` must meet the safety requirement of [`std::ffi::CStr::from_ptr`].
209///
210/// # Panic
211/// When iterating the returned iterator, it will panic if the pointer doesn't point to a valid
212/// UTF-8 string.
213#[deny(unsafe_op_in_unsafe_fn)]
214pub(crate) unsafe fn slice_to_owned_strings(
215    cstr_ptrs: &[*const i8],
216) -> impl Iterator<Item = String> + '_ {
217    cstr_ptrs.iter().map(|cstr_ptr| {
218        let cstr = unsafe { CStr::from_ptr(*cstr_ptr) };
219        cstr.to_str()
220            .unwrap_or_else(|e| {
221                panic!(
222                    "Failed to decode the string {}: {}",
223                    cstr.to_string_lossy(),
224                    e
225                )
226            })
227            .to_owned()
228    })
229}
230
231/// Clone the slice to a C style out array with proper `VkResult` return value.
232///
233/// The caller can use this function to handle the output array in Vulkan with slice.
234///
235/// If `p_out` is `NULL`, write the length to `p_out`. If `p_out` is not `NULL`, `p_count` describes
236/// the length of the output array. This function will clone the slice to the output array, and
237/// rewrite the `p_count` to indicate how many elements are written to the out array. If the length
238/// of the slice is larger than the out array, `VK_INCOMPLETE` will be returned. For all other
239/// cases, `VK_SUCCESS` is returned.
240///
241/// # Safety
242/// `p_count` must be a valid pointer to `U`. If `p_count` doesn't reference 0, and `p_out` is not
243/// null, `p_out` must be a valid pointer to `*p_count` number of `T`'s.
244///
245/// # Examples
246///
247/// Obtain the length with a NULL `p_out`.
248///
249/// ```
250/// use ash::vk;
251/// use std::ptr::NonNull;
252/// use vulkan_layer::fill_vk_out_array;
253///
254/// let mut count = 0;
255/// let out_array = &[0, 1, 2, 3];
256/// assert_eq!(
257///     unsafe {
258///         fill_vk_out_array(
259///             out_array,
260///             NonNull::new(&mut count as *mut _).unwrap(),
261///             std::ptr::null_mut(),
262///         )
263///     },
264///     vk::Result::SUCCESS
265/// );
266/// assert_eq!(count as usize, out_array.len());
267/// ```
268///
269/// Short output array results in a `VK_INCOMPLETE`.
270///
271/// ```
272/// use ash::vk;
273/// use std::ptr::NonNull;
274/// use vulkan_layer::fill_vk_out_array;
275///
276/// let mut out_array = [0; 2];
277/// let mut count = out_array.len() as u32;
278/// let out_slice = &[3, 1, 44];
279///
280/// assert_eq!(
281///     unsafe {
282///         fill_vk_out_array(
283///             out_slice,
284///             NonNull::new(&mut count as *mut _).unwrap(),
285///             out_array.as_mut_ptr(),
286///         )
287///     },
288///     vk::Result::INCOMPLETE
289/// );
290/// assert_eq!(count as usize, out_array.len());
291/// assert_eq!(out_array, out_slice[0..out_array.len()]);
292/// ```
293///
294/// Longer output array will only be partly overwritten.
295///
296/// ```
297/// use ash::vk;
298/// use std::ptr::NonNull;
299/// use vulkan_layer::fill_vk_out_array;
300///
301/// let mut out_array = [42; 3];
302/// let mut count = out_array.len() as u32;
303/// let out_slice = &[3, 1];
304///
305/// assert_eq!(
306///     unsafe {
307///         fill_vk_out_array(
308///             out_slice,
309///             NonNull::new(&mut count as *mut _).unwrap(),
310///             out_array.as_mut_ptr(),
311///         )
312///     },
313///     vk::Result::SUCCESS
314/// );
315/// assert_eq!(count as usize, out_slice.len());
316/// assert_eq!(out_array, [3, 1, 42]);
317/// ```
318#[deny(unsafe_op_in_unsafe_fn)]
319pub unsafe fn fill_vk_out_array<T, U>(
320    out: &[T],
321    mut p_count: NonNull<U>,
322    p_out: *mut T,
323) -> vk::Result
324where
325    T: Clone,
326    U: TryFrom<usize> + TryInto<usize> + Zero + Copy,
327    <U as TryFrom<usize>>::Error: Debug,
328    <U as TryInto<usize>>::Error: Debug,
329{
330    let count = unsafe { p_count.as_mut() };
331    let p_out = match NonNull::new(p_out) {
332        Some(p_out) => p_out,
333        None => {
334            *count = out.len().try_into().unwrap();
335            return vk::Result::SUCCESS;
336        }
337    };
338    if count.is_zero() {
339        if out.is_empty() {
340            return vk::Result::SUCCESS;
341        } else {
342            return vk::Result::INCOMPLETE;
343        }
344    }
345    let out_slice =
346        unsafe { std::slice::from_raw_parts_mut(p_out.as_ptr(), (*count).try_into().unwrap()) };
347    if out_slice.len() < out.len() {
348        *count = out_slice.len().try_into().unwrap();
349        out_slice.clone_from_slice(&out[..out_slice.len()]);
350        vk::Result::INCOMPLETE
351    } else {
352        *count = out.len().try_into().unwrap();
353        out_slice[..out.len()].clone_from_slice(out);
354        vk::Result::SUCCESS
355    }
356}
357
358/// Returns `None` if the pointer is null, or else returns a unique reference to the value wrapped
359/// in `Some`. In contrast to `as_mut`, this does not require that the value has to be initialized.
360///
361/// Polyfill of
362/// [`as_uninit_mut`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut) for
363/// stable Rust.
364///
365/// # Safety
366///
367/// Refers to
368/// [`as_uninit_mut`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut).
369#[deny(unsafe_op_in_unsafe_fn)]
370pub(crate) unsafe fn ptr_as_uninit_mut<'a, T>(p_data: *mut T) -> Option<&'a mut MaybeUninit<T>> {
371    // SAFETY: same as https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut.
372    if p_data.is_null() {
373        None
374    } else {
375        Some(unsafe { &mut *(p_data as *mut MaybeUninit<T>) })
376    }
377}
378
379#[cfg(test)]
380mod tests {
381    use super::*;
382
383    #[test]
384    fn multiple_in_struct_nodes() {
385        let mut external_memory_image_info = vk::ExternalMemoryImageCreateInfo::builder();
386        let mut image_format_list_info = vk::ImageFormatListCreateInfo::builder();
387        let image_create_info = vk::ImageCreateInfo::builder()
388            .push_next(&mut external_memory_image_info)
389            .push_next(&mut image_format_list_info);
390        let in_chain = image_create_info.p_next as *const vk::BaseInStructure;
391        let mut in_chain: VulkanBaseInStructChain = unsafe { in_chain.as_ref() }.into();
392        // `push_next` prepends the element to the pNext chain.
393        assert_eq!(
394            in_chain.next().unwrap().s_type,
395            vk::StructureType::IMAGE_FORMAT_LIST_CREATE_INFO
396        );
397        assert_eq!(
398            in_chain.next().unwrap().s_type,
399            vk::StructureType::EXTERNAL_MEMORY_IMAGE_CREATE_INFO
400        );
401        assert!(in_chain.next().is_none());
402    }
403
404    #[test]
405    fn multiple_out_struct_nodes() {
406        let mut timeline_semaphore_feature = vk::PhysicalDeviceTimelineSemaphoreFeatures::builder();
407        let mut ycbcr_feature = vk::PhysicalDeviceSamplerYcbcrConversionFeatures::builder();
408        let features2 = vk::PhysicalDeviceFeatures2::builder()
409            .push_next(&mut timeline_semaphore_feature)
410            .push_next(&mut ycbcr_feature);
411        let out_chain = features2.p_next.cast::<vk::BaseOutStructure>();
412        let mut out_chain: VulkanBaseOutStructChain = unsafe { out_chain.as_mut() }.into();
413        // `push_next` prepends the element to the pNext chain.
414        assert_eq!(
415            out_chain.next().unwrap().s_type,
416            vk::StructureType::PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES
417        );
418        assert_eq!(
419            out_chain.next().unwrap().s_type,
420            vk::StructureType::PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES
421        );
422        assert!(out_chain.next().is_none());
423    }
424
425    #[test]
426    fn test_is_command_enabled() {
427        let features: Vec<Feature> = vec![
428            Extension::EXTExtendedDynamicState.into(),
429            Extension::EXTShaderObject.into(),
430        ];
431        assert!(features.is_command_enabled(
432            &ApiVersion::V1_1,
433            &BTreeSet::from([Extension::EXTExtendedDynamicState])
434        ));
435        assert!(features.is_command_enabled(
436            &ApiVersion::V1_1,
437            &BTreeSet::from([Extension::EXTShaderObject])
438        ));
439        assert!(!features.is_command_enabled(&ApiVersion::V1_1, &BTreeSet::new()));
440    }
441
442    #[test]
443    fn test_fill_vk_out_array_null_out_ptr() {
444        let data = &[1, 2, 3, 4];
445        let mut count = 0;
446        assert_eq!(
447            unsafe {
448                fill_vk_out_array(
449                    data,
450                    NonNull::new(&mut count as *mut _).unwrap(),
451                    std::ptr::null_mut(),
452                )
453            },
454            vk::Result::SUCCESS
455        );
456        assert_eq!(count as usize, data.len());
457    }
458
459    #[test]
460    fn test_fill_vk_out_array_empty_out_ptr() {
461        let mut count = 0;
462        assert_eq!(
463            unsafe {
464                fill_vk_out_array(
465                    &[1, 2, 3, 4],
466                    NonNull::new(&mut count as *mut _).unwrap(),
467                    NonNull::dangling().as_ptr(),
468                )
469            },
470            vk::Result::INCOMPLETE
471        );
472        assert_eq!(count, 0);
473
474        let data: &[i32] = &[];
475        assert_eq!(
476            unsafe {
477                fill_vk_out_array(
478                    data,
479                    NonNull::new(&mut count as *mut _).unwrap(),
480                    NonNull::dangling().as_ptr(),
481                )
482            },
483            vk::Result::SUCCESS
484        );
485        assert_eq!(count, 0);
486    }
487
488    #[test]
489    fn test_fill_vk_out_array_shorter_out_ptr() {
490        let data = &[1, 2, 3, 4];
491        let mut out_array = [0; 2];
492        let mut count = out_array.len() as u32;
493        assert_eq!(
494            unsafe {
495                fill_vk_out_array(
496                    data,
497                    NonNull::new(&mut count as *mut _).unwrap(),
498                    out_array.as_mut_ptr(),
499                )
500            },
501            vk::Result::INCOMPLETE
502        );
503        assert_eq!(count as usize, out_array.len());
504        assert_eq!(out_array, data[0..out_array.len()]);
505    }
506
507    #[test]
508    fn test_fill_vk_out_array_longer_out_ptr() {
509        let data = &[1, 2];
510        let mut out_array = [10; 4];
511        let mut count = out_array.len() as u32;
512        assert_eq!(
513            unsafe {
514                fill_vk_out_array(
515                    data,
516                    NonNull::new(&mut count as *mut _).unwrap(),
517                    out_array.as_mut_ptr(),
518                )
519            },
520            vk::Result::SUCCESS
521        );
522        assert_eq!(count as usize, data.len());
523        assert_eq!(out_array, [1, 2, 10, 10]);
524    }
525
526    #[test]
527    fn test_ptr_as_uninit_mut_null_ptr() {
528        let ptr: *mut u8 = std::ptr::null_mut();
529        assert!(unsafe { ptr_as_uninit_mut(ptr) }.is_none());
530    }
531
532    #[test]
533    fn test_ptr_as_uninit_mut_uninit_value() {
534        let expected_value: u8 = 28;
535        let mut uninit_data = MaybeUninit::<u8>::uninit();
536        // Miri will test if we read into uninitialized data.
537        let result_uninit = unsafe { ptr_as_uninit_mut(uninit_data.as_mut_ptr()) }
538            .expect("not null pointer should result in Some");
539        assert_eq!(result_uninit.as_mut_ptr(), uninit_data.as_mut_ptr());
540        result_uninit.write(expected_value);
541        assert_eq!(unsafe { uninit_data.assume_init() }, expected_value);
542    }
543
544    // The following 2 tests guarantee that when invalid UTF-8 strings are passed in,
545    // slice_to_owned_strings only panics when the returned iterator is consumed.
546    #[test]
547    fn test_slice_to_owned_strings_invalid_utf8_str_shouldnt_panic_before_iter_consumed() {
548        let invalid_input = b"\xc3\x28\x00";
549        let invalid_input: &[i8] = bytemuck::cast_slice(invalid_input);
550        let invalid_inputs = [invalid_input.as_ptr()];
551        let _ = unsafe { slice_to_owned_strings(&invalid_inputs) };
552    }
553
554    #[test]
555    #[should_panic]
556    fn test_slice_to_owned_strings_invalid_utf8_str() {
557        let invalid_input = b"\xc3\x28\x00";
558        let invalid_input: &[i8] = bytemuck::cast_slice(invalid_input);
559        let invalid_inputs = [invalid_input.as_ptr()];
560        let output = unsafe { slice_to_owned_strings(&invalid_inputs) };
561        output.for_each(|_| {});
562    }
563}