bevy_render/render_resource/
buffer_vec.rs

1use core::{iter, marker::PhantomData};
2
3use crate::{
4    render_resource::Buffer,
5    renderer::{RenderDevice, RenderQueue},
6};
7use bytemuck::{must_cast_slice, NoUninit};
8use encase::{
9    internal::{WriteInto, Writer},
10    ShaderType,
11};
12use wgpu::{BindingResource, BufferAddress, BufferUsages};
13
14use super::GpuArrayBufferable;
15
16/// A structure for storing raw bytes that have already been properly formatted
17/// for use by the GPU.
18///
19/// "Properly formatted" means that item data already meets the alignment and padding
20/// requirements for how it will be used on the GPU. The item type must implement [`NoUninit`]
21/// for its data representation to be directly copyable.
22///
23/// Index, vertex, and instance-rate vertex buffers have no alignment nor padding requirements and
24/// so this helper type is a good choice for them.
25///
26/// The contained data is stored in system RAM. Calling [`reserve`](RawBufferVec::reserve)
27/// allocates VRAM from the [`RenderDevice`].
28/// [`write_buffer`](RawBufferVec::write_buffer) queues copying of the data
29/// from system RAM to VRAM.
30///
31/// Other options for storing GPU-accessible data are:
32/// * [`StorageBuffer`](crate::render_resource::StorageBuffer)
33/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer)
34/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)
35/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)
36/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)
37/// * [`BufferVec`]
38/// * [`Texture`](crate::render_resource::Texture)
39pub struct RawBufferVec<T: NoUninit> {
40    values: Vec<T>,
41    buffer: Option<Buffer>,
42    capacity: usize,
43    item_size: usize,
44    buffer_usage: BufferUsages,
45    label: Option<String>,
46    changed: bool,
47}
48
49impl<T: NoUninit> RawBufferVec<T> {
50    /// Creates a new [`RawBufferVec`] with the given [`BufferUsages`].
51    pub const fn new(buffer_usage: BufferUsages) -> Self {
52        Self {
53            values: Vec::new(),
54            buffer: None,
55            capacity: 0,
56            item_size: size_of::<T>(),
57            buffer_usage,
58            label: None,
59            changed: false,
60        }
61    }
62
63    /// Returns a handle to the buffer, if the data has been uploaded.
64    #[inline]
65    pub fn buffer(&self) -> Option<&Buffer> {
66        self.buffer.as_ref()
67    }
68
69    /// Returns the binding for the buffer if the data has been uploaded.
70    #[inline]
71    pub fn binding(&self) -> Option<BindingResource> {
72        Some(BindingResource::Buffer(
73            self.buffer()?.as_entire_buffer_binding(),
74        ))
75    }
76
77    /// Returns the amount of space that the GPU will use before reallocating.
78    #[inline]
79    pub fn capacity(&self) -> usize {
80        self.capacity
81    }
82
83    /// Returns the number of items that have been pushed to this buffer.
84    #[inline]
85    pub fn len(&self) -> usize {
86        self.values.len()
87    }
88
89    /// Returns true if the buffer is empty.
90    #[inline]
91    pub fn is_empty(&self) -> bool {
92        self.values.is_empty()
93    }
94
95    /// Adds a new value and returns its index.
96    pub fn push(&mut self, value: T) -> usize {
97        let index = self.values.len();
98        self.values.push(value);
99        index
100    }
101
102    pub fn append(&mut self, other: &mut RawBufferVec<T>) {
103        self.values.append(&mut other.values);
104    }
105
106    /// Changes the debugging label of the buffer.
107    ///
108    /// The next time the buffer is updated (via [`reserve`](Self::reserve)), Bevy will inform
109    /// the driver of the new label.
110    pub fn set_label(&mut self, label: Option<&str>) {
111        let label = label.map(str::to_string);
112
113        if label != self.label {
114            self.changed = true;
115        }
116
117        self.label = label;
118    }
119
120    /// Returns the label
121    pub fn get_label(&self) -> Option<&str> {
122        self.label.as_deref()
123    }
124
125    /// Creates a [`Buffer`] on the [`RenderDevice`] with size
126    /// at least `size_of::<T>() * capacity`, unless a such a buffer already exists.
127    ///
128    /// If a [`Buffer`] exists, but is too small, references to it will be discarded,
129    /// and a new [`Buffer`] will be created. Any previously created [`Buffer`]s
130    /// that are no longer referenced will be deleted by the [`RenderDevice`]
131    /// once it is done using them (typically 1-2 frames).
132    ///
133    /// In addition to any [`BufferUsages`] provided when
134    /// the `RawBufferVec` was created, the buffer on the [`RenderDevice`]
135    /// is marked as [`BufferUsages::COPY_DST`](BufferUsages).
136    pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
137        let size = self.item_size * capacity;
138        if capacity > self.capacity || (self.changed && size > 0) {
139            self.capacity = capacity;
140            self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
141                label: self.label.as_deref(),
142                size: size as BufferAddress,
143                usage: BufferUsages::COPY_DST | self.buffer_usage,
144                mapped_at_creation: false,
145            }));
146            self.changed = false;
147        }
148    }
149
150    /// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]
151    /// and the provided [`RenderQueue`].
152    ///
153    /// Before queuing the write, a [`reserve`](RawBufferVec::reserve) operation
154    /// is executed.
155    pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
156        if self.values.is_empty() {
157            return;
158        }
159        self.reserve(self.values.len(), device);
160        if let Some(buffer) = &self.buffer {
161            let range = 0..self.item_size * self.values.len();
162            let bytes: &[u8] = must_cast_slice(&self.values);
163            queue.write_buffer(buffer, 0, &bytes[range]);
164        }
165    }
166
167    /// Reduces the length of the buffer.
168    pub fn truncate(&mut self, len: usize) {
169        self.values.truncate(len);
170    }
171
172    /// Removes all elements from the buffer.
173    pub fn clear(&mut self) {
174        self.values.clear();
175    }
176
177    pub fn values(&self) -> &Vec<T> {
178        &self.values
179    }
180
181    pub fn values_mut(&mut self) -> &mut Vec<T> {
182        &mut self.values
183    }
184}
185
186impl<T: NoUninit> Extend<T> for RawBufferVec<T> {
187    #[inline]
188    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
189        self.values.extend(iter);
190    }
191}
192
193/// Like [`RawBufferVec`], but doesn't require that the data type `T` be
194/// [`NoUninit`].
195///
196/// This is a high-performance data structure that you should use whenever
197/// possible if your data is more complex than is suitable for [`RawBufferVec`].
198/// The [`ShaderType`] trait from the `encase` library is used to ensure that
199/// the data is correctly aligned for use by the GPU.
200///
201/// For performance reasons, unlike [`RawBufferVec`], this type doesn't allow
202/// CPU access to the data after it's been added via [`BufferVec::push`]. If you
203/// need CPU access to the data, consider another type, such as
204/// [`StorageBuffer`][super::StorageBuffer].
205pub struct BufferVec<T>
206where
207    T: ShaderType + WriteInto,
208{
209    data: Vec<u8>,
210    buffer: Option<Buffer>,
211    capacity: usize,
212    buffer_usage: BufferUsages,
213    label: Option<String>,
214    label_changed: bool,
215    phantom: PhantomData<T>,
216}
217
218impl<T> BufferVec<T>
219where
220    T: ShaderType + WriteInto,
221{
222    /// Creates a new [`BufferVec`] with the given [`BufferUsages`].
223    pub const fn new(buffer_usage: BufferUsages) -> Self {
224        Self {
225            data: vec![],
226            buffer: None,
227            capacity: 0,
228            buffer_usage,
229            label: None,
230            label_changed: false,
231            phantom: PhantomData,
232        }
233    }
234
235    /// Returns a handle to the buffer, if the data has been uploaded.
236    #[inline]
237    pub fn buffer(&self) -> Option<&Buffer> {
238        self.buffer.as_ref()
239    }
240
241    /// Returns the binding for the buffer if the data has been uploaded.
242    #[inline]
243    pub fn binding(&self) -> Option<BindingResource> {
244        Some(BindingResource::Buffer(
245            self.buffer()?.as_entire_buffer_binding(),
246        ))
247    }
248
249    /// Returns the amount of space that the GPU will use before reallocating.
250    #[inline]
251    pub fn capacity(&self) -> usize {
252        self.capacity
253    }
254
255    /// Returns the number of items that have been pushed to this buffer.
256    #[inline]
257    pub fn len(&self) -> usize {
258        self.data.len() / u64::from(T::min_size()) as usize
259    }
260
261    /// Returns true if the buffer is empty.
262    #[inline]
263    pub fn is_empty(&self) -> bool {
264        self.data.is_empty()
265    }
266
267    /// Adds a new value and returns its index.
268    pub fn push(&mut self, value: T) -> usize {
269        let element_size = u64::from(T::min_size()) as usize;
270        let offset = self.data.len();
271
272        // TODO: Consider using unsafe code to push uninitialized, to prevent
273        // the zeroing. It shows up in profiles.
274        self.data.extend(iter::repeat(0).take(element_size));
275
276        // Take a slice of the new data for `write_into` to use. This is
277        // important: it hoists the bounds check up here so that the compiler
278        // can eliminate all the bounds checks that `write_into` will emit.
279        let mut dest = &mut self.data[offset..(offset + element_size)];
280        value.write_into(&mut Writer::new(&value, &mut dest, 0).unwrap());
281
282        offset / u64::from(T::min_size()) as usize
283    }
284
285    /// Changes the debugging label of the buffer.
286    ///
287    /// The next time the buffer is updated (via [`Self::reserve`]), Bevy will inform
288    /// the driver of the new label.
289    pub fn set_label(&mut self, label: Option<&str>) {
290        let label = label.map(str::to_string);
291
292        if label != self.label {
293            self.label_changed = true;
294        }
295
296        self.label = label;
297    }
298
299    /// Returns the label
300    pub fn get_label(&self) -> Option<&str> {
301        self.label.as_deref()
302    }
303
304    /// Creates a [`Buffer`] on the [`RenderDevice`] with size
305    /// at least `size_of::<T>() * capacity`, unless such a buffer already exists.
306    ///
307    /// If a [`Buffer`] exists, but is too small, references to it will be discarded,
308    /// and a new [`Buffer`] will be created. Any previously created [`Buffer`]s
309    /// that are no longer referenced will be deleted by the [`RenderDevice`]
310    /// once it is done using them (typically 1-2 frames).
311    ///
312    /// In addition to any [`BufferUsages`] provided when
313    /// the `BufferVec` was created, the buffer on the [`RenderDevice`]
314    /// is marked as [`BufferUsages::COPY_DST`](BufferUsages).
315    pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
316        if capacity <= self.capacity && !self.label_changed {
317            return;
318        }
319
320        self.capacity = capacity;
321        let size = u64::from(T::min_size()) as usize * capacity;
322        self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
323            label: self.label.as_deref(),
324            size: size as BufferAddress,
325            usage: BufferUsages::COPY_DST | self.buffer_usage,
326            mapped_at_creation: false,
327        }));
328        self.label_changed = false;
329    }
330
331    /// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]
332    /// and the provided [`RenderQueue`].
333    ///
334    /// Before queuing the write, a [`reserve`](BufferVec::reserve) operation is
335    /// executed.
336    pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
337        if self.data.is_empty() {
338            return;
339        }
340
341        self.reserve(self.data.len() / u64::from(T::min_size()) as usize, device);
342
343        let Some(buffer) = &self.buffer else { return };
344        queue.write_buffer(buffer, 0, &self.data);
345    }
346
347    /// Reduces the length of the buffer.
348    pub fn truncate(&mut self, len: usize) {
349        self.data.truncate(u64::from(T::min_size()) as usize * len);
350    }
351
352    /// Removes all elements from the buffer.
353    pub fn clear(&mut self) {
354        self.data.clear();
355    }
356}
357
358/// Like a [`BufferVec`], but only reserves space on the GPU for elements
359/// instead of initializing them CPU-side.
360///
361/// This type is useful when you're accumulating "output slots" for a GPU
362/// compute shader to write into.
363///
364/// The type `T` need not be [`NoUninit`], unlike [`RawBufferVec`]; it only has to
365/// be [`GpuArrayBufferable`].
366pub struct UninitBufferVec<T>
367where
368    T: GpuArrayBufferable,
369{
370    buffer: Option<Buffer>,
371    len: usize,
372    capacity: usize,
373    item_size: usize,
374    buffer_usage: BufferUsages,
375    label: Option<String>,
376    label_changed: bool,
377    phantom: PhantomData<T>,
378}
379
380impl<T> UninitBufferVec<T>
381where
382    T: GpuArrayBufferable,
383{
384    /// Creates a new [`UninitBufferVec`] with the given [`BufferUsages`].
385    pub const fn new(buffer_usage: BufferUsages) -> Self {
386        Self {
387            len: 0,
388            buffer: None,
389            capacity: 0,
390            item_size: size_of::<T>(),
391            buffer_usage,
392            label: None,
393            label_changed: false,
394            phantom: PhantomData,
395        }
396    }
397
398    /// Returns the buffer, if allocated.
399    #[inline]
400    pub fn buffer(&self) -> Option<&Buffer> {
401        self.buffer.as_ref()
402    }
403
404    /// Returns the binding for the buffer if the data has been uploaded.
405    #[inline]
406    pub fn binding(&self) -> Option<BindingResource> {
407        Some(BindingResource::Buffer(
408            self.buffer()?.as_entire_buffer_binding(),
409        ))
410    }
411
412    /// Reserves space for one more element in the buffer and returns its index.
413    pub fn add(&mut self) -> usize {
414        let index = self.len;
415        self.len += 1;
416        index
417    }
418
419    /// Returns true if no elements have been added to this [`UninitBufferVec`].
420    pub fn is_empty(&self) -> bool {
421        self.len == 0
422    }
423
424    /// Removes all elements from the buffer.
425    pub fn clear(&mut self) {
426        self.len = 0;
427    }
428
429    /// Returns the length of the buffer.
430    pub fn len(&self) -> usize {
431        self.len
432    }
433
434    /// Materializes the buffer on the GPU with space for `capacity` elements.
435    ///
436    /// If the buffer is already big enough, this function doesn't reallocate
437    /// the buffer.
438    pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
439        if capacity <= self.capacity && !self.label_changed {
440            return;
441        }
442
443        self.capacity = capacity;
444        let size = self.item_size * capacity;
445        self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
446            label: self.label.as_deref(),
447            size: size as BufferAddress,
448            usage: BufferUsages::COPY_DST | self.buffer_usage,
449            mapped_at_creation: false,
450        }));
451
452        self.label_changed = false;
453    }
454
455    /// Materializes the buffer on the GPU, with an appropriate size for the
456    /// elements that have been pushed so far.
457    pub fn write_buffer(&mut self, device: &RenderDevice) {
458        if !self.is_empty() {
459            self.reserve(self.len, device);
460        }
461    }
462}