1use crate::threading::SpawnableFuture;
2use crate::wgpu::gpu_context::GpuContext;
3
4pub struct Buffer {
14 pub(crate) raw: wgpu::Buffer,
15 pub(crate) ctx: GpuContext,
16}
17
18impl Buffer {
19 pub(crate) fn new(raw: wgpu::Buffer, ctx: GpuContext) -> Self {
20 Self { raw, ctx }
21 }
22
23 pub fn write(&self, data: &[u8]) {
25 self.ctx.queue().write_buffer(&self.raw, 0, data);
26 }
27
28 pub fn write_at(&self, offset: u64, data: &[u8]) {
33 self.ctx.queue().write_buffer(&self.raw, offset, data);
34 }
35
36 pub fn size(&self) -> u64 {
38 self.raw.size()
39 }
40
41 pub fn read(&self) -> impl SpawnableFuture<Vec<u8>> {
54 readback(self.ctx.device(), self.ctx.queue(), &self.raw)
55 }
56
57 pub fn read_as<T: bytemuck::Pod + Send + 'static>(&self) -> impl SpawnableFuture<Vec<T>> {
59 let bytes = self.read();
60 async move {
61 let bytes = bytes.await;
62 bytemuck::cast_slice(&bytes).to_vec()
63 }
64 }
65
66 pub(crate) fn raw(&self) -> &wgpu::Buffer {
67 &self.raw
68 }
69}
70
71pub(crate) fn readback(
75 device: &wgpu::Device,
76 queue: &wgpu::Queue,
77 src: &wgpu::Buffer,
78) -> impl SpawnableFuture<Vec<u8>> {
79 let size = src.size();
80 let staging = crate::wgpu::buffers::BufferBuilder::empty(size)
81 .usage(crate::wgpu::flags::BufferUsages::COPY_DST | crate::wgpu::flags::BufferUsages::MAP_READ)
82 .build_raw(device);
83
84 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
85 encoder.copy_buffer_to_buffer(src, 0, &staging, 0, size);
86 let idx = queue.submit(std::iter::once(encoder.finish()));
87
88 #[cfg(not(target_arch = "wasm32"))]
89 let device = device.clone();
90
91 async move {
92 #[cfg(not(target_arch = "wasm32"))]
93 {
94 let (tx, rx) = std::sync::mpsc::channel();
95 staging.slice(..).map_async(wgpu::MapMode::Read, move |r| {
96 let _ = tx.send(r);
97 });
98 let _ = device.poll(wgpu::PollType::Wait {
105 submission_index: Some(idx),
106 timeout: None,
107 });
108 rx.recv().unwrap().unwrap();
109 let data = staging.slice(..).get_mapped_range().to_vec();
110 staging.unmap();
111 data
112 }
113
114 #[cfg(target_arch = "wasm32")]
115 {
116 let _ = idx;
117 let mapped: std::sync::Arc<std::sync::Mutex<Option<Result<(), wgpu::BufferAsyncError>>>> =
118 std::sync::Arc::new(std::sync::Mutex::new(None));
119 let waker: std::sync::Arc<std::sync::Mutex<Option<std::task::Waker>>> =
120 std::sync::Arc::new(std::sync::Mutex::new(None));
121
122 let mapped_cb = mapped.clone();
123 let waker_cb = waker.clone();
124 staging.slice(..).map_async(wgpu::MapMode::Read, move |r| {
125 *mapped_cb.lock().unwrap() = Some(r);
126 if let Some(w) = waker_cb.lock().unwrap().take() {
127 w.wake();
128 }
129 });
130
131 std::future::poll_fn(move |cx| {
132 if let Some(result) = mapped.lock().unwrap().take() {
133 result.unwrap();
134 let data = staging.slice(..).get_mapped_range().to_vec();
135 staging.unmap();
136 std::task::Poll::Ready(data)
137 } else {
138 *waker.lock().unwrap() = Some(cx.waker().clone());
139 std::task::Poll::Pending
140 }
141 })
142 .await
143 }
144 }
145}
146
147pub struct DynamicBuffer {
155 pub(crate) buffer: Buffer,
156 pub(crate) stride: u64,
157 pub(crate) element_size: u64,
158}
159
160impl DynamicBuffer {
161 pub(crate) fn new(buffer: Buffer, stride: u64, element_size: u64) -> Self {
162 Self { buffer, stride, element_size }
163 }
164
165 pub fn write_element(&self, index: u64, data: &[u8]) {
169 self.buffer.write_at(index * self.stride, data);
170 }
171
172 pub fn element_size(&self) -> u64 {
175 self.element_size
176 }
177
178 pub fn stride(&self) -> u64 {
181 self.stride
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use crate::wgpu::buffers::BufferBuilder;
189 use crate::wgpu::flags::BufferUsages;
190 use crate::wgpu::test_util::with_device;
191
192 fn ctx(device: &wgpu::Device, queue: &wgpu::Queue) -> GpuContext {
193 GpuContext::new(device.clone(), queue.clone())
194 }
195
196 #[test]
197 fn write_and_write_at_do_not_panic() {
198 with_device!(device, queue, {
199 let buffer = Buffer::new(
200 BufferBuilder::empty(16).usage(BufferUsages::UNIFORM | BufferUsages::COPY_DST).build_raw(&device),
201 ctx(&device, &queue),
202 );
203 buffer.write(&[1u8, 2, 3, 4]);
204 buffer.write_at(8, &[5u8, 6, 7, 8]);
205 assert_eq!(buffer.size(), 16);
206 });
207 }
208
209 #[test]
210 fn dynamic_buffer_write_element_does_not_panic_and_reports_its_own_sizing() {
211 with_device!(device, queue, {
212 let element_size = 16u64;
213 let count = 4u64;
214 let (usage, stride) = (
215 BufferUsages::UNIFORM | BufferUsages::COPY_DST,
216 crate::wgpu::buffers::dynamic_uniform_offset_stride_raw(&device, element_size),
217 );
218 let raw = BufferBuilder::empty(stride * count).usage(usage).build_raw(&device);
219 let dynamic = DynamicBuffer::new(Buffer::new(raw, ctx(&device, &queue)), stride, element_size);
220
221 assert_eq!(dynamic.element_size(), element_size);
222 assert_eq!(dynamic.stride(), stride);
223 assert!(dynamic.stride() >= dynamic.element_size(), "stride is alignment-padded, never smaller than the element");
224
225 dynamic.write_element(count - 1, &vec![0u8; element_size as usize]);
229 });
230 }
231
232}
233