iced_wgpu/
buffer.rs

1use std::marker::PhantomData;
2use std::num::NonZeroU64;
3use std::ops::RangeBounds;
4
5pub const MAX_WRITE_SIZE: usize = 100 * 1024;
6
7const MAX_WRITE_SIZE_U64: NonZeroU64 = NonZeroU64::new(MAX_WRITE_SIZE as u64)
8    .expect("MAX_WRITE_SIZE must be non-zero");
9
10#[derive(Debug)]
11pub struct Buffer<T> {
12    label: &'static str,
13    size: u64,
14    usage: wgpu::BufferUsages,
15    pub(crate) raw: wgpu::Buffer,
16    offsets: Vec<wgpu::BufferAddress>,
17    type_: PhantomData<T>,
18}
19
20impl<T: bytemuck::Pod> Buffer<T> {
21    pub fn new(
22        device: &wgpu::Device,
23        label: &'static str,
24        amount: usize,
25        usage: wgpu::BufferUsages,
26    ) -> Self {
27        let size = next_copy_size::<T>(amount);
28
29        let raw = device.create_buffer(&wgpu::BufferDescriptor {
30            label: Some(label),
31            size,
32            usage,
33            mapped_at_creation: false,
34        });
35
36        Self {
37            label,
38            size,
39            usage,
40            raw,
41            offsets: Vec::new(),
42            type_: PhantomData,
43        }
44    }
45
46    pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool {
47        let new_size = (std::mem::size_of::<T>() * new_count) as u64;
48
49        if self.size < new_size {
50            self.offsets.clear();
51
52            self.raw = device.create_buffer(&wgpu::BufferDescriptor {
53                label: Some(self.label),
54                size: new_size,
55                usage: self.usage,
56                mapped_at_creation: false,
57            });
58
59            self.size = new_size;
60
61            true
62        } else {
63            false
64        }
65    }
66
67    /// Returns the size of the written bytes.
68    pub fn write(
69        &mut self,
70        device: &wgpu::Device,
71        encoder: &mut wgpu::CommandEncoder,
72        belt: &mut wgpu::util::StagingBelt,
73        offset: usize,
74        contents: &[T],
75    ) -> usize {
76        let bytes: &[u8] = bytemuck::cast_slice(contents);
77        let mut bytes_written = 0;
78
79        // Split write into multiple chunks if necessary
80        while bytes_written + MAX_WRITE_SIZE < bytes.len() {
81            belt.write_buffer(
82                encoder,
83                &self.raw,
84                (offset + bytes_written) as u64,
85                MAX_WRITE_SIZE_U64,
86                device,
87            )
88            .copy_from_slice(
89                &bytes[bytes_written..bytes_written + MAX_WRITE_SIZE],
90            );
91
92            bytes_written += MAX_WRITE_SIZE;
93        }
94
95        // There will always be some bytes left, since the previous
96        // loop guarantees `bytes_written < bytes.len()`
97        let bytes_left = ((bytes.len() - bytes_written) as u64)
98            .try_into()
99            .expect("non-empty write");
100
101        // Write them
102        belt.write_buffer(
103            encoder,
104            &self.raw,
105            (offset + bytes_written) as u64,
106            bytes_left,
107            device,
108        )
109        .copy_from_slice(&bytes[bytes_written..]);
110
111        self.offsets.push(offset as u64);
112
113        bytes.len()
114    }
115
116    pub fn slice(
117        &self,
118        bounds: impl RangeBounds<wgpu::BufferAddress>,
119    ) -> wgpu::BufferSlice<'_> {
120        self.raw.slice(bounds)
121    }
122
123    /// Returns the slice calculated from the offset stored at the given index.
124    pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> {
125        self.raw.slice(self.offset_at(index)..)
126    }
127
128    /// Clears any temporary data (i.e. offsets) from the buffer.
129    pub fn clear(&mut self) {
130        self.offsets.clear();
131    }
132
133    /// Returns the offset at `index`, if it exists.
134    fn offset_at(&self, index: usize) -> &wgpu::BufferAddress {
135        self.offsets.get(index).expect("No offset at index.")
136    }
137}
138
139fn next_copy_size<T>(amount: usize) -> u64 {
140    let align_mask = wgpu::COPY_BUFFER_ALIGNMENT - 1;
141
142    (((std::mem::size_of::<T>() * amount).next_power_of_two() as u64
143        + align_mask)
144        & !align_mask)
145        .max(wgpu::COPY_BUFFER_ALIGNMENT)
146}