ash/
util.rs

1use crate::vk;
2use core::ffi::c_void;
3use core::iter::Iterator;
4use core::marker::PhantomData;
5use core::mem::size_of;
6use core::slice;
7#[cfg(feature = "std")]
8use std::io;
9
10/// [`Align`] handles dynamic alignment. The is useful for dynamic uniform buffers where
11/// the alignment might be different. For example a 4x4 f32 matrix has a size of 64 bytes
12/// but the min alignment for a dynamic uniform buffer might be 256 bytes. A slice of `&[Mat4x4<f32>]`
13/// has a memory layout of `[[64 bytes], [64 bytes], [64 bytes]]`, but it might need to have a memory
14/// layout of `[[256 bytes], [256 bytes], [256 bytes]]`.
15/// [`Align::copy_from_slice`] will copy a slice of `&[T]` directly into the host memory without
16/// an additional allocation and with the correct alignment.
17#[derive(Debug, Clone)]
18pub struct Align<T> {
19    ptr: *mut c_void,
20    elem_size: vk::DeviceSize,
21    size: vk::DeviceSize,
22    _m: PhantomData<T>,
23}
24
25#[derive(Debug)]
26pub struct AlignIter<'a, T> {
27    align: &'a mut Align<T>,
28    current: vk::DeviceSize,
29}
30
31impl<T: Copy> Align<T> {
32    pub fn copy_from_slice(&mut self, slice: &[T]) {
33        if self.elem_size == size_of::<T>() as u64 {
34            unsafe {
35                let mapped_slice = slice::from_raw_parts_mut(self.ptr.cast(), slice.len());
36                mapped_slice.copy_from_slice(slice);
37            }
38        } else {
39            for (i, val) in self.iter_mut().enumerate().take(slice.len()) {
40                *val = slice[i];
41            }
42        }
43    }
44}
45
46fn calc_padding(adr: vk::DeviceSize, align: vk::DeviceSize) -> vk::DeviceSize {
47    (align - adr % align) % align
48}
49
50impl<T> Align<T> {
51    pub unsafe fn new(ptr: *mut c_void, alignment: vk::DeviceSize, size: vk::DeviceSize) -> Self {
52        let padding = calc_padding(size_of::<T>() as vk::DeviceSize, alignment);
53        let elem_size = size_of::<T>() as vk::DeviceSize + padding;
54        assert!(calc_padding(size, alignment) == 0, "size must be aligned");
55        Self {
56            ptr,
57            elem_size,
58            size,
59            _m: PhantomData,
60        }
61    }
62
63    pub fn iter_mut(&mut self) -> AlignIter<'_, T> {
64        AlignIter {
65            current: 0,
66            align: self,
67        }
68    }
69}
70
71impl<'a, T: Copy + 'a> Iterator for AlignIter<'a, T> {
72    type Item = &'a mut T;
73    fn next(&mut self) -> Option<Self::Item> {
74        if self.current == self.align.size {
75            return None;
76        }
77        unsafe {
78            // Need to cast to *mut u8 because () has size 0
79            let ptr = (self.align.ptr.cast::<u8>())
80                .offset(self.current as isize)
81                .cast();
82            self.current += self.align.elem_size;
83            Some(&mut *ptr)
84        }
85    }
86}
87
88/// Decode SPIR-V from bytes.
89///
90/// This function handles SPIR-V of arbitrary endianness gracefully, and returns correctly aligned
91/// storage.
92///
93/// # Examples
94/// ```no_run
95/// // Decode SPIR-V from a file
96/// let mut file = std::fs::File::open("/path/to/shader.spv").unwrap();
97/// let words = ash::util::read_spv(&mut file).unwrap();
98/// ```
99/// ```
100/// // Decode SPIR-V from memory
101/// const SPIRV: &[u8] = &[
102///     // ...
103/// #   0x03, 0x02, 0x23, 0x07,
104/// ];
105/// let words = ash::util::read_spv(&mut std::io::Cursor::new(&SPIRV[..])).unwrap();
106/// ```
107#[cfg(feature = "std")]
108pub fn read_spv<R: io::Read + io::Seek>(x: &mut R) -> io::Result<Vec<u32>> {
109    // TODO use stream_len() once it is stabilized and remove the subsequent rewind() call
110    let size = x.seek(io::SeekFrom::End(0))?;
111    x.rewind()?;
112    if size % 4 != 0 {
113        return Err(io::Error::new(
114            io::ErrorKind::InvalidData,
115            "input length not divisible by 4",
116        ));
117    }
118    if size > usize::MAX as u64 {
119        return Err(io::Error::new(io::ErrorKind::InvalidData, "input too long"));
120    }
121    let words = (size / 4) as usize;
122    // https://github.com/ash-rs/ash/issues/354:
123    // Zero-initialize the result to prevent read_exact from possibly
124    // reading uninitialized memory.
125    let mut result = vec![0u32; words];
126    x.read_exact(unsafe {
127        slice::from_raw_parts_mut(result.as_mut_ptr().cast::<u8>(), words * 4)
128    })?;
129    const MAGIC_NUMBER: u32 = 0x0723_0203;
130    if !result.is_empty() && result[0] == MAGIC_NUMBER.swap_bytes() {
131        for word in &mut result {
132            *word = word.swap_bytes();
133        }
134    }
135    if result.is_empty() || result[0] != MAGIC_NUMBER {
136        return Err(io::Error::new(
137            io::ErrorKind::InvalidData,
138            "input missing SPIR-V magic number",
139        ));
140    }
141    Ok(result)
142}