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}