use super::context::{GpuContext, GpuError};
use crate::pixel::{PixelBuffer, PixelFormat};
pub struct GpuBuffer {
buffer: wgpu::Buffer,
size: u64,
width: u32,
height: u32,
format: PixelFormat,
}
impl GpuBuffer {
#[must_use]
pub fn upload(ctx: &GpuContext, buf: &PixelBuffer) -> Self {
let buffer =
mabda::buffer::create_storage_buffer(ctx.device(), &buf.data, "ranga_pixel_buf", false);
Self {
size: buf.data.len() as u64,
width: buf.width,
height: buf.height,
format: buf.format,
buffer,
}
}
#[must_use = "returns the downloaded pixel buffer"]
pub fn download(&self, ctx: &GpuContext) -> Result<PixelBuffer, GpuError> {
let data = mabda::buffer::read_buffer(ctx.device(), ctx.queue(), &self.buffer, self.size)
.map_err(|e| GpuError::BufferOp(e.to_string()))?;
PixelBuffer::new(data, self.width, self.height, self.format)
.map_err(|e| GpuError::BufferOp(e.to_string()))
}
#[must_use = "returns a receiver for the downloaded pixel buffer"]
pub fn download_async(
&self,
ctx: &GpuContext,
) -> std::sync::mpsc::Receiver<Result<PixelBuffer, GpuError>> {
let (tx, rx) = std::sync::mpsc::channel();
let width = self.width;
let height = self.height;
let format = self.format;
let staging = ctx.device().create_buffer(&wgpu::BufferDescriptor {
label: Some("staging_readback_async"),
size: self.size,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let mut encoder = ctx
.device()
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
encoder.copy_buffer_to_buffer(&self.buffer, 0, &staging, 0, self.size);
ctx.queue().submit(Some(encoder.finish()));
let size = self.size as usize;
let slice = staging.slice(..);
let (map_tx, map_rx) = std::sync::mpsc::channel();
slice.map_async(wgpu::MapMode::Read, move |result| {
let _ = map_tx.send(result);
});
std::thread::spawn(move || {
let map_result = map_rx.recv();
let result = (|| -> Result<PixelBuffer, GpuError> {
map_result
.map_err(|e| GpuError::BufferOp(e.to_string()))?
.map_err(|e| GpuError::BufferOp(e.to_string()))?;
let data = {
let view = staging.slice(..);
let mapped = view.get_mapped_range();
mapped[..size].to_vec()
};
drop(staging);
PixelBuffer::new(data, width, height, format)
.map_err(|e| GpuError::BufferOp(e.to_string()))
})();
let _ = tx.send(result);
});
rx
}
#[must_use]
#[inline]
pub fn wgpu_buffer(&self) -> &wgpu::Buffer {
&self.buffer
}
#[must_use]
#[inline]
pub fn size(&self) -> u64 {
self.size
}
#[must_use]
#[inline]
pub fn width(&self) -> u32 {
self.width
}
#[must_use]
#[inline]
pub fn height(&self) -> u32 {
self.height
}
#[must_use]
#[inline]
pub fn format(&self) -> PixelFormat {
self.format
}
}