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        // Create command encoder
200        let mut command_buffer = CommandBuffer::new(instance, &format!("copy-to-{}", self.label));
201
202        // Copy texture
203        command_buffer.copy_texture_to_buffer(texture, self, texture.size());
204
205        // Submit commands
206        command_buffer.submit(instance);
207    }
208
209    /// Write data to the buffer.
210    /// Note that the buffer must have the COPY_DST usage.
211    ///
212    /// # Arguments
213    ///
214    /// * `instance` - The render instance.
215    /// * `content` - The content to write to the buffer.
216    /// * `offset` - The offset to write the content to.
217    pub fn write(&self, instance: &RenderInstanceData, content: &[u8], offset: usize) {
218        instance
219            .queue
220            .write_buffer(&self.buffer, offset as u64, content);
221    }
222
223    /// Map the buffer.
224    /// The access to the buffer is read-only.
225    /// This will wait for the buffer to be mapped.
226    /// Note that the buffer must have the MAP_READ usage.
227    ///
228    /// # Arguments
229    ///
230    /// * `instance` - The render instance.
231    /// * `callback` - A closure that takes a reference to the buffer.
232    ///   The callback takes reference to the buffer.
233    pub fn map_read(&self, instance: &RenderInstanceData, callback: impl FnOnce(BufferView)) {
234        // Map buffer
235        let (sender, receiver) = std::sync::mpsc::channel();
236        let buffer_slice = self.buffer.slice(..);
237        buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
238
239        // Wait for the buffer to be mapped
240        if let Err(e) = instance.device.poll(wgpu::PollType::Wait {
241            submission_index: None,
242            timeout: None
243        }) {
244            error!(
245                "Failed to poll device while mapping buffer '{}': {:?}",
246                self.label, e
247            );
248        }
249        receiver.recv().unwrap().unwrap();
250
251        // Call callback
252        callback(buffer_slice.get_mapped_range());
253
254        // Unmap buffer
255        self.buffer.unmap();
256    }
257
258    /// Map the buffer as mutable.
259    /// This allows to write to the buffer.
260    /// This will wait for the buffer to be mapped.
261    /// Note that the buffer must have the MAP_WRITE usage.
262    ///
263    /// # Arguments
264    ///
265    /// * `instance` - The render instance.
266    /// * `callback` - A closure that takes a mutable reference to the buffer.
267    ///   The callback takes a mutable reference to the buffer.
268    pub fn map_write(&self, instance: &RenderInstanceData, callback: impl FnOnce(BufferViewMut)) {
269        // Map buffer
270        let (sender, receiver) = std::sync::mpsc::channel();
271        let buffer_slice = self.buffer.slice(..);
272        buffer_slice.map_async(wgpu::MapMode::Write, move |r| sender.send(r).unwrap());
273
274        // Wait for the buffer to be mapped
275        {
276            let _span = trace_span!("wait_for_buffer_mapping").entered();
277            if let Err(e) = instance.device.poll(wgpu::PollType::Wait {
278                submission_index: None,
279                timeout: None
280            }) {
281                error!(
282                    "Failed to poll device while mapping buffer '{}': {:?}",
283                    self.label, e
284                );
285            }
286            receiver.recv().unwrap().unwrap();
287        }
288
289        // Call callback
290        callback(buffer_slice.get_mapped_range_mut());
291
292        // Unmap buffer
293        self.buffer.unmap();
294    }
295}