easy_wgpu/
buffer.rs

1use encase::{
2    self,
3    private::{AlignmentValue, Writer},
4};
5use wgpu;
6
7/// A wrapper for `wgpu::Buffer`. Allows writing of aligned or packed data into
8/// it
9#[derive(Clone)]
10pub struct Buffer {
11    pub buffer: wgpu::Buffer,
12    pub size_bytes: usize,
13    cpu_byte_buffer: Vec<u8>,
14    offset: usize,
15    alignment: AlignmentValue,
16}
17
18impl Buffer {
19    pub fn new_empty(device: &wgpu::Device, usage: wgpu::BufferUsages, label: wgpu::Label, size_bytes: usize) -> Self {
20        let buffer = device.create_buffer(&wgpu::BufferDescriptor {
21            label,
22            size: size_bytes as u64,
23            usage,
24            mapped_at_creation: false,
25        });
26
27        let cpu_byte_buffer = Vec::new();
28
29        Self {
30            buffer,
31            size_bytes,
32            //for a packed one
33            cpu_byte_buffer,
34            offset: 0,
35            alignment: AlignmentValue::new(256),
36        }
37    }
38
39    pub fn is_empty(&self) -> bool {
40        self.size_bytes == 0
41    }
42
43    pub fn write_buffer(&mut self, queue: &wgpu::Queue, offset_bytes: usize, data: &[u8]) {
44        queue.write_buffer(&self.buffer, offset_bytes as u64, bytemuck::cast_slice(data));
45    }
46
47    /// Writes data to the `cpu_byte_buffer` and adds padding in order to align
48    /// to 256 bytes. This is useful for adding local data of each mesh into a
49    /// gpu buffer and having the bind groups with an offset into the buffer. #
50    /// Panics Will panic if the `cpu_byte_buffer` is too small to write the
51    /// current chunk
52    pub fn push_cpu_chunk_aligned<T: encase::ShaderType + encase::internal::WriteInto>(&mut self, chunk: &T) -> u32 {
53        let offset = self.offset;
54        let mut writer = Writer::new(chunk, &mut self.cpu_byte_buffer, offset).unwrap();
55        chunk.write_into(&mut writer);
56        // self.offset_packed += chunk.size().get() as usize;
57        self.offset += usize::try_from(self.alignment.round_up(chunk.size().get())).unwrap();
58        u32::try_from(offset).unwrap()
59    }
60
61    /// Writes data to the `cpu_byte_buffer` but adds no padding. This is useful
62    /// when adding lots of structures to the buffer and they need to be exposed
63    /// as a var<uniform> structs : array<MyStruct,20> to the shader. Useful for
64    /// adding a vector of lights for example # Panics
65    /// Will panic if the `cpu_byte_buffer` is too small to write the current
66    /// chunk
67    pub fn push_cpu_chunk_packed<T: encase::ShaderType + encase::internal::WriteInto>(&mut self, chunk: &T) {
68        let offset = self.offset;
69        let mut writer = Writer::new(chunk, &mut self.cpu_byte_buffer, offset).unwrap();
70        chunk.write_into(&mut writer);
71        self.offset += usize::try_from(chunk.size().get()).unwrap();
72    }
73
74    /// Uploads from the `cpu_byte_buffer` to gpu
75    pub fn upload_from_cpu_chunks(&mut self, queue: &wgpu::Queue) {
76        //write byte_buffers to gpu
77        queue.write_buffer(&self.buffer, 0, self.cpu_byte_buffer.as_slice());
78    }
79
80    /// Sets the offset back to 0 so we can start a new round of upload from the
81    /// beggining of the buffer
82    pub fn reset_chunks_offset(&mut self) {
83        self.offset = 0;
84    }
85
86    /// Sets the offset back to 0 only if we are approaching the end of the
87    /// buffer. Usually we don't want to always reset to 0 since that
88    /// subpart of the buffer may be used for rendering when we want to write to
89    /// it, rather we want to use as most of the buffer as possible without
90    pub fn reset_chunks_offset_if_necessary(&mut self) {
91        //this is quite conservative, essentially we are using only half of the buffer
92        if self.offset > self.size_bytes / 2 {
93            self.offset = 0;
94        }
95    }
96
97    pub fn offset(&self) -> usize {
98        self.offset
99    }
100}