1use crate::context::GpuContext;
7use crate::error::{GpuError, GpuResult};
8use bytemuck::{Pod, Zeroable};
9use std::marker::PhantomData;
10use std::sync::Arc;
11use tracing::{debug, trace};
12use wgpu::{
13 Buffer, BufferAsyncError, BufferDescriptor, BufferUsages, COPY_BUFFER_ALIGNMENT, MapMode,
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum BufferElementType {
26 F32,
28 F16,
30 U8,
32 U16,
34 U32,
36 I32,
38}
39
40impl BufferElementType {
41 pub fn byte_size(self) -> usize {
43 match self {
44 Self::F32 | Self::I32 | Self::U32 => 4,
45 Self::F16 | Self::U16 => 2,
46 Self::U8 => 1,
47 }
48 }
49
50 pub fn wgsl_type(self) -> &'static str {
55 match self {
56 Self::F32 => "f32",
57 Self::F16 => "f16",
58 Self::U32 => "u32",
59 Self::I32 => "i32",
60 Self::U8 => "u32", Self::U16 => "u32", }
63 }
64}
65
66pub fn f16_to_f32_slice(data: &[half::f16]) -> Vec<f32> {
75 data.iter().map(|h| f32::from(*h)).collect()
76}
77
78pub fn f32_to_f16_slice(data: &[f32]) -> Vec<half::f16> {
84 data.iter().map(|f| half::f16::from_f32(*f)).collect()
85}
86
87pub fn from_f16_slice_widening(
100 context: &GpuContext,
101 data: &[half::f16],
102) -> GpuResult<GpuBuffer<f32>> {
103 let f32_data = f16_to_f32_slice(data);
104 GpuBuffer::<f32>::from_data(
105 context,
106 &f32_data,
107 BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
108 )
109}
110
111pub fn from_f16_slice_native(context: &GpuContext, data: &[half::f16]) -> GpuResult<GpuBuffer<u8>> {
121 let bytes: &[u8] = bytemuck::cast_slice(data);
122 GpuBuffer::<u8>::from_data(
123 context,
124 bytes,
125 BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
126 )
127}
128
129pub fn read_f16_from_f32_buffer(buf: &GpuBuffer<f32>) -> GpuResult<Vec<half::f16>> {
138 let f32_vals = buf.read_blocking()?;
139 Ok(f32_to_f16_slice(&f32_vals))
140}
141
142pub struct GpuBuffer<T: Pod> {
147 buffer: Arc<Buffer>,
149 context: GpuContext,
151 len: usize,
153 usage: BufferUsages,
155 _phantom: PhantomData<T>,
157}
158
159impl<T: Pod> GpuBuffer<T> {
160 pub fn new(context: &GpuContext, len: usize, usage: BufferUsages) -> GpuResult<Self> {
166 let size = Self::calculate_size(len)?;
167
168 trace!("Creating GPU buffer: {} elements, {} bytes", len, size);
169
170 let buffer = context.device().create_buffer(&BufferDescriptor {
171 label: Some("GpuBuffer"),
172 size,
173 usage,
174 mapped_at_creation: false,
175 });
176
177 Ok(Self {
178 buffer: Arc::new(buffer),
179 context: context.clone(),
180 len,
181 usage,
182 _phantom: PhantomData,
183 })
184 }
185
186 pub fn from_data(context: &GpuContext, data: &[T], usage: BufferUsages) -> GpuResult<Self> {
192 let mut buffer = Self::new(context, data.len(), usage | BufferUsages::COPY_DST)?;
193 buffer.write(data)?;
194 Ok(buffer)
195 }
196
197 pub fn staging(context: &GpuContext, len: usize) -> GpuResult<Self> {
203 Self::new(
204 context,
205 len,
206 BufferUsages::MAP_READ | BufferUsages::COPY_DST,
207 )
208 }
209
210 fn calculate_size(len: usize) -> GpuResult<u64> {
212 let element_size = std::mem::size_of::<T>();
213 let size = len
214 .checked_mul(element_size)
215 .ok_or_else(|| GpuError::invalid_buffer("Buffer size overflow"))?;
216
217 let aligned_size = ((size as u64 + COPY_BUFFER_ALIGNMENT - 1) / COPY_BUFFER_ALIGNMENT)
219 * COPY_BUFFER_ALIGNMENT;
220
221 Ok(aligned_size)
222 }
223
224 pub fn write(&mut self, data: &[T]) -> GpuResult<()> {
231 if data.len() != self.len {
232 return Err(GpuError::invalid_buffer(format!(
233 "Data size mismatch: expected {}, got {}",
234 self.len,
235 data.len()
236 )));
237 }
238
239 if !self.usage.contains(BufferUsages::COPY_DST) {
240 return Err(GpuError::invalid_buffer(
241 "Buffer not writable (missing COPY_DST usage)",
242 ));
243 }
244
245 let bytes = bytemuck::cast_slice(data);
246 self.context.queue().write_buffer(&self.buffer, 0, bytes);
247
248 debug!("Wrote {} bytes to GPU buffer", bytes.len());
249 Ok(())
250 }
251
252 pub async fn read(&self) -> GpuResult<Vec<T>> {
258 if !self.usage.contains(BufferUsages::MAP_READ) {
259 return Err(GpuError::invalid_buffer(
260 "Buffer not readable (missing MAP_READ usage)",
261 ));
262 }
263
264 let buffer_slice = self.buffer.slice(..);
265
266 let (tx, rx) = futures::channel::oneshot::channel();
268 buffer_slice.map_async(MapMode::Read, move |result| {
269 let _ = tx.send(result);
270 });
271
272 self.context.poll(true);
274
275 rx.await
277 .map_err(|_| GpuError::buffer_mapping("Channel closed"))?
278 .map_err(|e| GpuError::buffer_mapping(Self::map_error_to_string(e)))?;
279
280 let data = buffer_slice
282 .get_mapped_range()
283 .map_err(|e| GpuError::buffer_mapping(e.to_string()))?;
284 let mut result: Vec<T> = bytemuck::cast_slice(&data).to_vec();
285
286 result.truncate(self.len);
294
295 drop(data);
297 self.buffer.unmap();
298
299 debug!("Read {} elements from GPU buffer", result.len());
300 Ok(result)
301 }
302
303 pub async fn read_async(&self) -> GpuResult<Vec<T>> {
320 self.read().await
321 }
322
323 pub fn read_blocking(&self) -> GpuResult<Vec<T>> {
329 pollster::block_on(self.read())
330 }
331
332 pub fn copy_from(&mut self, source: &GpuBuffer<T>) -> GpuResult<()> {
338 if self.len != source.len {
339 return Err(GpuError::invalid_buffer(format!(
340 "Buffer size mismatch: {} != {}",
341 self.len, source.len
342 )));
343 }
344
345 if !source.usage.contains(BufferUsages::COPY_SRC) {
346 return Err(GpuError::invalid_buffer(
347 "Source buffer not copyable (missing COPY_SRC usage)",
348 ));
349 }
350
351 if !self.usage.contains(BufferUsages::COPY_DST) {
352 return Err(GpuError::invalid_buffer(
353 "Destination buffer not copyable (missing COPY_DST usage)",
354 ));
355 }
356
357 let mut encoder =
358 self.context
359 .device()
360 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
361 label: Some("Buffer Copy"),
362 });
363
364 let size = Self::calculate_size(self.len)?;
365 encoder.copy_buffer_to_buffer(&source.buffer, 0, &self.buffer, 0, size);
366
367 self.context.queue().submit(Some(encoder.finish()));
368
369 debug!("Copied {} elements between GPU buffers", self.len);
370 Ok(())
371 }
372
373 pub fn len(&self) -> usize {
375 self.len
376 }
377
378 pub fn is_empty(&self) -> bool {
380 self.len == 0
381 }
382
383 pub fn size_bytes(&self) -> u64 {
385 Self::calculate_size(self.len).unwrap_or(0)
386 }
387
388 pub fn buffer(&self) -> &Buffer {
390 &self.buffer
391 }
392
393 pub fn usage(&self) -> BufferUsages {
395 self.usage
396 }
397
398 fn map_error_to_string(error: BufferAsyncError) -> String {
400 error.to_string()
401 }
402}
403
404impl<T: Pod> Clone for GpuBuffer<T> {
405 fn clone(&self) -> Self {
406 Self {
407 buffer: Arc::clone(&self.buffer),
408 context: self.context.clone(),
409 len: self.len,
410 usage: self.usage,
411 _phantom: PhantomData,
412 }
413 }
414}
415
416impl<T: Pod> std::fmt::Debug for GpuBuffer<T> {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 f.debug_struct("GpuBuffer")
419 .field("len", &self.len)
420 .field("size_bytes", &self.size_bytes())
421 .field("usage", &self.usage)
422 .field("type", &std::any::type_name::<T>())
423 .finish()
424 }
425}
426
427pub struct GpuRasterBuffer<T: Pod> {
432 bands: Vec<GpuBuffer<T>>,
434 width: u32,
436 height: u32,
438}
439
440impl<T: Pod + Zeroable> GpuRasterBuffer<T> {
441 pub fn new(
447 context: &GpuContext,
448 width: u32,
449 height: u32,
450 num_bands: usize,
451 usage: BufferUsages,
452 ) -> GpuResult<Self> {
453 let pixels_per_band = (width as usize)
454 .checked_mul(height as usize)
455 .ok_or_else(|| GpuError::invalid_buffer("Raster size overflow"))?;
456
457 let bands = (0..num_bands)
458 .map(|_| GpuBuffer::new(context, pixels_per_band, usage))
459 .collect::<GpuResult<Vec<_>>>()?;
460
461 debug!(
462 "Created GPU raster buffer: {}x{} with {} bands",
463 width, height, num_bands
464 );
465
466 Ok(Self {
467 bands,
468 width,
469 height,
470 })
471 }
472
473 pub fn from_bands(
479 context: &GpuContext,
480 width: u32,
481 height: u32,
482 bands_data: &[Vec<T>],
483 usage: BufferUsages,
484 ) -> GpuResult<Self> {
485 let expected_size = (width as usize) * (height as usize);
486
487 for (i, band) in bands_data.iter().enumerate() {
488 if band.len() != expected_size {
489 return Err(GpuError::invalid_buffer(format!(
490 "Band {} size mismatch: expected {}, got {}",
491 i,
492 expected_size,
493 band.len()
494 )));
495 }
496 }
497
498 let bands = bands_data
499 .iter()
500 .map(|data| GpuBuffer::from_data(context, data, usage))
501 .collect::<GpuResult<Vec<_>>>()?;
502
503 Ok(Self {
504 bands,
505 width,
506 height,
507 })
508 }
509
510 pub fn band(&self, index: usize) -> Option<&GpuBuffer<T>> {
512 self.bands.get(index)
513 }
514
515 pub fn band_mut(&mut self, index: usize) -> Option<&mut GpuBuffer<T>> {
517 self.bands.get_mut(index)
518 }
519
520 pub fn bands(&self) -> &[GpuBuffer<T>] {
522 &self.bands
523 }
524
525 pub fn num_bands(&self) -> usize {
527 self.bands.len()
528 }
529
530 pub fn dimensions(&self) -> (u32, u32) {
532 (self.width, self.height)
533 }
534
535 pub fn width(&self) -> u32 {
537 self.width
538 }
539
540 pub fn height(&self) -> u32 {
542 self.height
543 }
544
545 pub async fn read_all_bands(&self) -> GpuResult<Vec<Vec<T>>> {
551 let mut results = Vec::with_capacity(self.bands.len());
552
553 for band in &self.bands {
554 results.push(band.read().await?);
555 }
556
557 Ok(results)
558 }
559
560 pub fn read_all_bands_blocking(&self) -> GpuResult<Vec<Vec<T>>> {
566 pollster::block_on(self.read_all_bands())
567 }
568}
569
570impl<T: Pod> std::fmt::Debug for GpuRasterBuffer<T> {
571 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
572 f.debug_struct("GpuRasterBuffer")
573 .field("width", &self.width)
574 .field("height", &self.height)
575 .field("num_bands", &self.num_bands())
576 .field("type", &std::any::type_name::<T>())
577 .finish()
578 }
579}
580
581#[cfg(test)]
582#[allow(clippy::panic)]
583mod tests {
584 use super::*;
585
586 #[tokio::test]
587 async fn test_gpu_buffer_creation() {
588 if let Ok(context) = GpuContext::new().await {
589 let buffer: GpuBuffer<f32> = GpuBuffer::new(&context, 1024, BufferUsages::STORAGE)
590 .unwrap_or_else(|e| {
591 panic!("Failed to create buffer: {}", e);
592 });
593
594 assert_eq!(buffer.len(), 1024);
595 assert!(!buffer.is_empty());
596 }
597 }
598
599 #[tokio::test]
600 #[ignore]
601 async fn test_gpu_buffer_write_read() {
602 if let Ok(context) = GpuContext::new().await {
603 let data: Vec<f32> = (0..100).map(|i| i as f32).collect();
604
605 let buffer = GpuBuffer::from_data(
606 &context,
607 &data,
608 BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
609 )
610 .unwrap_or_else(|e| {
611 panic!("Failed to create buffer: {}", e);
612 });
613
614 let mut staging = GpuBuffer::staging(&context, 100).unwrap_or_else(|e| {
616 panic!("Failed to create staging buffer: {}", e);
617 });
618
619 staging.copy_from(&buffer).unwrap_or_else(|e| {
620 panic!("Failed to copy buffer: {}", e);
621 });
622
623 let result = staging.read().await.unwrap_or_else(|e| {
624 panic!("Failed to read buffer: {}", e);
625 });
626
627 assert_eq!(result.len(), data.len());
628 for (a, b) in result.iter().zip(data.iter()) {
629 assert!((a - b).abs() < 1e-6);
630 }
631 }
632 }
633
634 #[tokio::test]
635 async fn test_read_truncates_u8_padding() {
636 if let Ok(context) = GpuContext::new().await {
644 let staging: GpuBuffer<u8> = GpuBuffer::staging(&context, 5).unwrap_or_else(|e| {
645 panic!("Failed to create staging buffer: {}", e);
646 });
647 let padded: [u8; 8] = [1, 2, 3, 4, 5, 0xAA, 0xBB, 0xCC];
648 context.queue().write_buffer(&staging.buffer, 0, &padded);
649
650 let result = staging
651 .read()
652 .await
653 .unwrap_or_else(|e| panic!("Failed to read buffer: {}", e));
654
655 assert_eq!(result.len(), 5, "read() must not return padding");
656 assert_eq!(result, vec![1, 2, 3, 4, 5]);
657 }
658 }
659
660 #[tokio::test]
661 async fn test_read_truncates_u16_padding() {
662 if let Ok(context) = GpuContext::new().await {
666 let staging: GpuBuffer<u16> = GpuBuffer::staging(&context, 3).unwrap_or_else(|e| {
667 panic!("Failed to create staging buffer: {}", e);
668 });
669 let padded: [u16; 4] = [100, 200, 300, 0xFFFF];
670 context
671 .queue()
672 .write_buffer(&staging.buffer, 0, bytemuck::cast_slice(&padded));
673
674 let result = staging
675 .read()
676 .await
677 .unwrap_or_else(|e| panic!("Failed to read buffer: {}", e));
678
679 assert_eq!(result.len(), 3, "read() must not return padding");
680 assert_eq!(result, vec![100, 200, 300]);
681 }
682 }
683
684 #[tokio::test]
685 async fn test_gpu_raster_buffer() {
686 if let Ok(context) = GpuContext::new().await {
687 let width = 64;
688 let height = 64;
689 let num_bands = 3;
690
691 let raster: GpuRasterBuffer<f32> =
692 GpuRasterBuffer::new(&context, width, height, num_bands, BufferUsages::STORAGE)
693 .unwrap_or_else(|e| {
694 panic!("Failed to create raster buffer: {}", e);
695 });
696
697 assert_eq!(raster.width(), width);
698 assert_eq!(raster.height(), height);
699 assert_eq!(raster.num_bands(), num_bands);
700 }
701 }
702}