Skip to main content

wde_wgpu/resources/
buffer.rs

1//! GPU buffer helper utilities built on top of `wgpu::Buffer`.
2
3use std::fmt::Formatter;
4use wde_logger::prelude::*;
5use wgpu::{BufferView, util::DeviceExt};
6
7use crate::{command_buffer::CommandBuffer, instance::RenderInstanceData};
8
9/// Buffer usages.
10pub type BufferUsage = wgpu::BufferUsages;
11
12/// Buffer binding types.
13pub type BufferBindingType = wgpu::BufferBindingType;
14
15/// Map the buffer.
16pub type BufferViewMut = wgpu::BufferViewMut;
17
18/// Create and manage GPU buffers.
19///
20/// # Examples
21/// Write once, render many:
22/// ```rust,no_run
23/// use wde_wgpu::{buffer::{Buffer, BufferUsage}, instance::RenderInstanceData};
24///
25/// let vertices: &[f32] = &[0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0];
26/// let mut vertex_buffer = Buffer::new(
27///     instance,
28///     "triangle-vertices",
29///     vertices.len() * std::mem::size_of::<f32>(),
30///     BufferUsage::VERTEX | BufferUsage::COPY_DST,
31///     None,
32/// );
33/// vertex_buffer.write(instance, bytemuck::cast_slice(vertices), 0);
34/// ```
35///
36/// Read back GPU results:
37/// ```rust,no_run
38/// use wde_wgpu::{buffer::{Buffer, BufferUsage}, instance::RenderInstanceData};
39///
40/// let readback = Buffer::new(
41///     instance,
42///     "readback",
43///     1024,
44///     BufferUsage::MAP_READ | BufferUsage::COPY_DST,
45///     None,
46/// );
47/// readback.map_read(instance, |view| {
48///     let floats: &[f32] = bytemuck::cast_slice(&view);
49///     println!("first value = {}", floats.get(0).unwrap_or(&0.0));
50/// });
51/// ```
52///
53/// Copy between GPU resources:
54/// ```rust,no_run
55/// use wde_wgpu::{buffer::{Buffer, BufferUsage}, instance::RenderInstanceData};
56///
57/// let src = Buffer::new(instance, "src", 256, BufferUsage::COPY_SRC, Some(&[1u8; 256]));
58/// let dst = Buffer::new(instance, "dst", 256, BufferUsage::COPY_DST, None);
59/// dst.copy_from_buffer(instance, &src);
60/// ```
61pub struct Buffer {
62    pub label: String,
63    pub buffer: wgpu::Buffer
64}
65
66impl std::fmt::Debug for Buffer {
67    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
68        f.debug_struct("Buffer")
69            .field("label", &self.label)
70            .field("buffer_size", &self.buffer.size())
71            .finish()
72    }
73}
74
75impl Buffer {
76    /// Create a new buffer.
77    ///
78    /// # Arguments
79    ///
80    /// * `instance` - The render instance.
81    /// * `label` - The label of the buffer.
82    /// * `size` - The size of the buffer.
83    /// * `usage` - The usage of the buffer (vertex, index, uniform, storage).
84    /// * `content` - The content of the buffer.
85    pub fn new(
86        instance: &RenderInstanceData,
87        label: &str,
88        size: usize,
89        usage: BufferUsage,
90        content: Option<&[u8]>
91    ) -> Self {
92        event!(LogLevel::TRACE, "Creating new buffer {}.", label);
93
94        // In case the content is not provided, create an empty buffer.
95        match content {
96            Some(content) => {
97                // Create buffer
98                let buffer =
99                    instance
100                        .device
101                        .create_buffer_init(&wgpu::util::BufferInitDescriptor {
102                            label: Some(format!("{}-buffer", label).as_str()),
103                            contents: content,
104                            usage
105                        });
106
107                Buffer {
108                    label: label.to_string(),
109                    buffer
110                }
111            }
112            None => {
113                // Create empty buffer of the given size
114                let buffer = instance.device.create_buffer(&wgpu::BufferDescriptor {
115                    label: Some(format!("{}-buffer", label).as_str()),
116                    size: size as u64,
117                    usage,
118                    mapped_at_creation: false
119                });
120
121                Buffer {
122                    label: label.to_string(),
123                    buffer
124                }
125            }
126        }
127    }
128
129    /// Copy data to the buffer from another buffer.
130    /// Note that the buffer must have the COPY_DST usage.
131    ///
132    /// # Arguments
133    ///
134    /// * `instance` - The render instance.
135    /// * `buffer` - The buffer to copy from.
136    pub fn copy_from_buffer(&self, instance: &RenderInstanceData<'_>, buffer: &Buffer) {
137        // Create command encoder
138        let mut command_buffer = CommandBuffer::new(
139            instance,
140            &format!("copy-from-{}-to-{}", buffer.label, self.label)
141        );
142
143        // Copy buffer
144        command_buffer.copy_buffer_to_buffer(buffer, self);
145
146        // Submit commands
147        command_buffer.submit(instance);
148    }
149
150    /// Copy data to the buffer from another buffer offset by a given offset.
151    /// Note that the buffer must have the COPY_DST usage.
152    ///
153    /// # Arguments
154    ///
155    /// * `instance` - The render instance.
156    /// * `buffer` - The buffer to copy from.
157    /// * `source_offset` - The offset in the source buffer to start copying from.
158    /// * `destination_offset` - The offset in the destination buffer to start copying to.
159    /// * `size` - The size of the data to copy.
160    pub fn copy_from_buffer_offset(
161        &self,
162        instance: &RenderInstanceData<'_>,
163        buffer: &Buffer,
164        source_offset: u64,
165        destination_offset: u64,
166        size: u64
167    ) {
168        // Create command encoder
169        let mut command_buffer = CommandBuffer::new(
170            instance,
171            &format!("copy-from-{}-to-{}", buffer.label, self.label)
172        );
173
174        // Copy buffer
175        command_buffer.copy_buffer_to_buffer_offset(
176            buffer,
177            self,
178            source_offset,
179            destination_offset,
180            size
181        );
182
183        // Submit commands
184        command_buffer.submit(instance);
185    }
186
187    /// Copy data to the buffer from a texture.
188    /// Note that the buffer must have the COPY_DST usage.
189    ///
190    /// # Arguments
191    ///
192    /// * `instance` - The render instance.
193    /// * `texture` - The texture to copy from.
194    pub fn copy_from_texture(
195        &mut self,
196        instance: &RenderInstanceData<'_>,
197        texture: &wgpu::Texture
198    ) {
199        let mut command_buffer = CommandBuffer::new(instance, &format!("copy-to-{}", self.label));
200        command_buffer.copy_texture_to_buffer(texture, self, texture.size());
201        command_buffer.submit(instance);
202    }
203
204    /// Copy a single layer from a texture array into this buffer.
205    pub fn copy_from_texture_layered(
206        &mut self,
207        instance: &RenderInstanceData<'_>,
208        texture: &wgpu::Texture,
209        layer: u32
210    ) {
211        let mut command_buffer = CommandBuffer::new(instance, &format!("copy-to-{}", self.label));
212        command_buffer.copy_texture_layer_to_buffer(texture, self, layer);
213        command_buffer.submit(instance);
214    }
215
216    /// Write data to the buffer.
217    /// Note that the buffer must have the COPY_DST usage.
218    ///
219    /// # Arguments
220    ///
221    /// * `instance` - The render instance.
222    /// * `content` - The content to write to the buffer.
223    /// * `offset` - The offset to write the content to.
224    pub fn write(&self, instance: &RenderInstanceData, content: &[u8], offset: usize) {
225        instance
226            .queue
227            .write_buffer(&self.buffer, offset as u64, content);
228    }
229
230    /// Map the buffer.
231    /// The access to the buffer is read-only.
232    /// This will wait for the buffer to be mapped.
233    /// Note that the buffer must have the MAP_READ usage.
234    ///
235    /// # Arguments
236    ///
237    /// * `instance` - The render instance.
238    /// * `callback` - A closure that takes a reference to the buffer.
239    ///   The callback takes reference to the buffer.
240    pub fn map_read(&self, instance: &RenderInstanceData, callback: impl FnOnce(BufferView)) {
241        // Map buffer
242        let (sender, receiver) = std::sync::mpsc::channel();
243        let buffer_slice = self.buffer.slice(..);
244        buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
245
246        // Wait for the buffer to be mapped
247        if let Err(e) = instance.device.poll(wgpu::PollType::Wait {
248            submission_index: None,
249            timeout: None
250        }) {
251            error!(
252                "Failed to poll device while mapping buffer '{}': {:?}",
253                self.label, e
254            );
255        }
256        receiver.recv().unwrap().unwrap();
257
258        // Call callback
259        callback(buffer_slice.get_mapped_range());
260
261        // Unmap buffer
262        self.buffer.unmap();
263    }
264
265    /// Map the buffer as mutable.
266    /// This allows to write to the buffer.
267    /// This will wait for the buffer to be mapped.
268    /// Note that the buffer must have the MAP_WRITE usage.
269    ///
270    /// # Arguments
271    ///
272    /// * `instance` - The render instance.
273    /// * `callback` - A closure that takes a mutable reference to the buffer.
274    ///   The callback takes a mutable reference to the buffer.
275    pub fn map_write(&self, instance: &RenderInstanceData, callback: impl FnOnce(BufferViewMut)) {
276        // Map buffer
277        let (sender, receiver) = std::sync::mpsc::channel();
278        let buffer_slice = self.buffer.slice(..);
279        buffer_slice.map_async(wgpu::MapMode::Write, move |r| sender.send(r).unwrap());
280
281        // Wait for the buffer to be mapped
282        {
283            let _span = trace_span!("wait_for_buffer_mapping").entered();
284            if let Err(e) = instance.device.poll(wgpu::PollType::Wait {
285                submission_index: None,
286                timeout: None
287            }) {
288                error!(
289                    "Failed to poll device while mapping buffer '{}': {:?}",
290                    self.label, e
291                );
292            }
293            receiver.recv().unwrap().unwrap();
294        }
295
296        // Call callback
297        callback(buffer_slice.get_mapped_range_mut());
298
299        // Unmap buffer
300        self.buffer.unmap();
301    }
302}