1use crate::{GpuDevice, GpuError, Result};
4use std::sync::Arc;
5use wgpu::{Buffer, BufferUsages};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum BufferType {
10 Staging,
12 Storage,
14 Uniform,
16 ReadBack,
18}
19
20pub struct GpuBuffer {
22 buffer: Buffer,
23 size: u64,
24 buffer_type: BufferType,
25 device: Arc<wgpu::Device>,
26}
27
28impl GpuBuffer {
29 pub fn new(device: &GpuDevice, size: u64, buffer_type: BufferType) -> Result<Self> {
41 let usage = Self::buffer_usage(buffer_type);
42
43 let buffer = device.device().create_buffer(&wgpu::BufferDescriptor {
44 label: Some(&format!("OxiMedia {buffer_type:?} Buffer")),
45 size,
46 usage,
47 mapped_at_creation: false,
48 });
49
50 Ok(Self {
51 buffer,
52 size,
53 buffer_type,
54 device: Arc::clone(device.device()),
55 })
56 }
57
58 pub fn with_data(device: &GpuDevice, data: &[u8], buffer_type: BufferType) -> Result<Self> {
70 let size = data.len() as u64;
71 let usage = Self::buffer_usage(buffer_type);
72
73 let buffer = device
74 .device()
75 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
76 label: Some(&format!("OxiMedia {buffer_type:?} Buffer")),
77 contents: data,
78 usage,
79 });
80
81 Ok(Self {
82 buffer,
83 size,
84 buffer_type,
85 device: Arc::clone(device.device()),
86 })
87 }
88
89 pub fn write(&self, queue: &wgpu::Queue, offset: u64, data: &[u8]) -> Result<()> {
102 if offset + data.len() as u64 > self.size {
103 return Err(GpuError::InvalidBufferSize {
104 expected: self.size as usize,
105 actual: (offset + data.len() as u64) as usize,
106 });
107 }
108
109 queue.write_buffer(&self.buffer, offset, data);
110 Ok(())
111 }
112
113 pub async fn read_async(&self, _device: &GpuDevice, offset: u64, size: u64) -> Result<Vec<u8>> {
126 if self.buffer_type != BufferType::ReadBack {
127 return Err(GpuError::NotSupported(
128 "Can only read from ReadBack buffers".to_string(),
129 ));
130 }
131
132 if offset + size > self.size {
133 return Err(GpuError::InvalidBufferSize {
134 expected: self.size as usize,
135 actual: (offset + size) as usize,
136 });
137 }
138
139 let slice = self.buffer.slice(offset..offset + size);
140 let (sender, receiver) = futures_channel::oneshot::channel();
141
142 slice.map_async(wgpu::MapMode::Read, move |result| {
143 let _ = sender.send(result);
144 });
145
146 self.device.poll(wgpu::Maintain::Wait);
147
148 receiver
149 .await
150 .map_err(|e| GpuError::BufferMapping(e.to_string()))?
151 .map_err(|e| GpuError::BufferMapping(e.to_string()))?;
152
153 let data = slice.get_mapped_range();
154 let result = data.to_vec();
155
156 drop(data);
157 self.buffer.unmap();
158
159 Ok(result)
160 }
161
162 pub fn read(&self, device: &GpuDevice, offset: u64, size: u64) -> Result<Vec<u8>> {
175 pollster::block_on(self.read_async(device, offset, size))
176 }
177
178 #[must_use]
180 pub fn buffer(&self) -> &Buffer {
181 &self.buffer
182 }
183
184 #[must_use]
186 pub fn size(&self) -> u64 {
187 self.size
188 }
189
190 #[must_use]
192 pub fn buffer_type(&self) -> BufferType {
193 self.buffer_type
194 }
195
196 #[allow(clippy::too_many_arguments)]
210 pub fn copy_to(
211 &self,
212 encoder: &mut wgpu::CommandEncoder,
213 dst: &Self,
214 src_offset: u64,
215 dst_offset: u64,
216 size: u64,
217 ) -> Result<()> {
218 if src_offset + size > self.size {
219 return Err(GpuError::InvalidBufferSize {
220 expected: self.size as usize,
221 actual: (src_offset + size) as usize,
222 });
223 }
224
225 if dst_offset + size > dst.size {
226 return Err(GpuError::InvalidBufferSize {
227 expected: dst.size as usize,
228 actual: (dst_offset + size) as usize,
229 });
230 }
231
232 encoder.copy_buffer_to_buffer(&self.buffer, src_offset, &dst.buffer, dst_offset, size);
233 Ok(())
234 }
235
236 fn buffer_usage(buffer_type: BufferType) -> BufferUsages {
237 match buffer_type {
238 BufferType::Staging => BufferUsages::MAP_WRITE | BufferUsages::COPY_SRC,
239 BufferType::Storage => {
240 BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC
241 }
242 BufferType::Uniform => BufferUsages::UNIFORM | BufferUsages::COPY_DST,
243 BufferType::ReadBack => BufferUsages::MAP_READ | BufferUsages::COPY_DST,
244 }
245 }
246}
247
248impl std::fmt::Debug for GpuBuffer {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 f.debug_struct("GpuBuffer")
251 .field("size", &self.size)
252 .field("buffer_type", &self.buffer_type)
253 .finish()
254 }
255}
256
257use wgpu::util::DeviceExt;