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}