use super::EwError;
fn read_mapped(buffer: &wgpu::Buffer, count: usize) -> Result<Vec<f32>, EwError> {
let result = {
match buffer.slice(..).get_mapped_range() {
Err(e) => Err(EwError::DeviceLost(format!(
"mapped buffer range could not be read: {e}"
))),
Ok(data) => {
let floats = bytemuck::cast_slice::<u8, f32>(&data);
if floats.len() < count {
Err(EwError::DeviceLost(format!(
"mapped buffer too small: expected {count} floats, found {}",
floats.len()
)))
} else {
Ok(floats[..count].to_vec())
}
}
}
};
buffer.unmap();
result
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn map_read(
device: &wgpu::Device,
_queue: &wgpu::Queue,
buffer: &wgpu::Buffer,
count: usize,
) -> Result<Vec<f32>, EwError> {
let (tx, rx) = std::sync::mpsc::channel();
buffer
.slice(..)
.map_async(wgpu::MapMode::Read, move |result| {
let _ = tx.send(result);
});
let start = std::time::Instant::now();
loop {
if let Err(e) = device.poll(wgpu::PollType::Wait {
submission_index: None,
timeout: Some(crate::renderer::gpu::compute::GPU_WAIT_TIMEOUT),
}) {
buffer.unmap();
return Err(EwError::DeviceLost(format!("device poll failed: {e}")));
}
match rx.try_recv() {
Ok(Ok(())) => break,
Ok(Err(e)) => {
buffer.unmap();
return Err(EwError::DeviceLost(format!("buffer mapping failed: {e}")));
}
Err(std::sync::mpsc::TryRecvError::Empty) => {
if start.elapsed() >= crate::renderer::gpu::compute::GPU_WAIT_TIMEOUT {
buffer.unmap();
return Err(EwError::ChunkTimeout);
}
}
Err(std::sync::mpsc::TryRecvError::Disconnected) => {
buffer.unmap();
return Err(EwError::DeviceLost(
"readback channel disconnected unexpectedly".to_string(),
));
}
}
}
read_mapped(buffer, count)
}
#[cfg(target_arch = "wasm32")]
#[allow(
clippy::future_not_send,
reason = "wgpu web backend buffer mapping futures are !Send; wasm32 is single-threaded"
)]
pub async fn map_read(
_device: &wgpu::Device,
_queue: &wgpu::Queue,
buffer: &wgpu::Buffer,
count: usize,
) -> Result<Vec<f32>, EwError> {
use super::clock::{Raced, race_deadline, readback_deadline_ms};
let (tx, rx) = futures_channel::oneshot::channel();
buffer
.slice(..)
.map_async(wgpu::MapMode::Read, move |result| {
let _ = tx.send(result);
});
match race_deadline(rx, readback_deadline_ms()).await {
Raced::TimedOut => {
buffer.unmap();
return Err(EwError::ChunkTimeout);
}
Raced::Done(Err(_)) => {
buffer.unmap();
return Err(EwError::DeviceLost(
"buffer-mapping callback was dropped before firing".to_string(),
));
}
Raced::Done(Ok(Err(e))) => {
buffer.unmap();
return Err(EwError::DeviceLost(format!(
"wgpu buffer mapping failed: {e}"
)));
}
Raced::Done(Ok(Ok(()))) => {}
}
read_mapped(buffer, count)
}