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