diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 4c88eaeb59..550a040898 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -1325,7 +1325,6 @@ impl crate::Context for Context { Ok(ptr) => BufferMappedRange { ptr, size: size as usize, - phantom: PhantomData, }, Err(err) => self.handle_error_fatal(err, "Buffer::get_mapped_range"), } @@ -1897,13 +1896,12 @@ fn default_error_handler(err: crate::Error) { } #[derive(Debug)] -pub struct BufferMappedRange<'a> { +pub struct BufferMappedRange { ptr: *mut u8, size: usize, - phantom: PhantomData<&'a ()>, } -impl<'a> crate::BufferMappedRangeSlice for BufferMappedRange<'a> { +impl crate::BufferMappedRangeSlice for BufferMappedRange { fn slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.ptr, self.size) } } @@ -1913,7 +1911,7 @@ impl<'a> crate::BufferMappedRangeSlice for BufferMappedRange<'a> { } } -impl<'a> Drop for BufferMappedRange<'a> { +impl Drop for BufferMappedRange { fn drop(&mut self) { // Intentionally left blank so that `BufferMappedRange` still // implements `Drop`, to match the web backend diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index f5fb6716d0..9c39d1b631 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -2,7 +2,6 @@ use std::{ collections::HashSet, fmt, future::Future, - marker::PhantomData, ops::Range, pin::Pin, task::{self, Poll}, @@ -1436,7 +1435,6 @@ impl crate::Context for Context { BufferMappedRange { actual_mapping, temporary_mapping, - phantom: PhantomData, } } @@ -1888,13 +1886,12 @@ impl crate::Context for Context { pub(crate) type SwapChainOutputDetail = (); #[derive(Debug)] -pub struct BufferMappedRange<'a> { +pub struct BufferMappedRange { actual_mapping: js_sys::Uint8Array, temporary_mapping: Vec, - phantom: PhantomData<&'a ()>, } -impl<'a> crate::BufferMappedRangeSlice for BufferMappedRange<'a> { +impl crate::BufferMappedRangeSlice for BufferMappedRange { fn slice(&self) -> &[u8] { &self.temporary_mapping } @@ -1904,7 +1901,7 @@ impl<'a> crate::BufferMappedRangeSlice for BufferMappedRange<'a> { } } -impl<'a> Drop for BufferMappedRange<'a> { +impl Drop for BufferMappedRange { fn drop(&mut self) { // Copy from the temporary mapping back into the array buffer that was // originally provided by the browser diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index be5bd097cc..dd9086efb5 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -1690,14 +1690,14 @@ trait BufferMappedRangeSlice { #[derive(Debug)] pub struct BufferView<'a> { slice: BufferSlice<'a>, - data: BufferMappedRange<'a>, + data: BufferMappedRange, } /// Write only view into mapped buffer. #[derive(Debug)] pub struct BufferViewMut<'a> { slice: BufferSlice<'a>, - data: BufferMappedRange<'a>, + data: BufferMappedRange, readable: bool, } diff --git a/wgpu/src/util/mod.rs b/wgpu/src/util/mod.rs index 8cfbb4f5b6..7730cf8822 100644 --- a/wgpu/src/util/mod.rs +++ b/wgpu/src/util/mod.rs @@ -6,6 +6,7 @@ mod encoder; use std::{ borrow::Cow, + future::Future, mem::{align_of, size_of}, ptr::copy_nonoverlapping, }; @@ -54,3 +55,42 @@ pub fn make_spirv<'a>(data: &'a [u8]) -> super::ShaderSource<'a> { ); super::ShaderSource::SpirV(words) } + +/// CPU accessible buffer used to download data back from the GPU. +pub struct DownloadBuffer(super::Buffer, super::BufferMappedRange); + +impl DownloadBuffer { + /// Asynchronously read the contents of a buffer. + pub fn read_buffer(device: &super::Device, queue: &super::Queue, buffer: &super::BufferSlice) -> impl Future> + Send { + let size = match buffer.size { + Some(size) => size.into(), + None => buffer.buffer.map_context.lock().total_size - buffer.offset, + }; + + let download = device.create_buffer(&super::BufferDescriptor { + size, + usage: super::BufferUsage::COPY_DST | super::BufferUsage::MAP_READ, + mapped_at_creation: false, + label: None, + }); + + let mut encoder = device.create_command_encoder(&super::CommandEncoderDescriptor { label: None }); + encoder.copy_buffer_to_buffer(buffer.buffer, buffer.offset, &download, 0, size); + let command_buffer: super::CommandBuffer = encoder.finish(); + queue.submit(Some(command_buffer)); + + let fut = download.slice(..).map_async(super::MapMode::Read); + async move { + fut.await?; + let mapped_range = super::Context::buffer_get_mapped_range(&*download.context, &download.id, 0..size); + Ok(Self(download, mapped_range)) + } + } +} + +impl std::ops::Deref for DownloadBuffer{ + type Target = [u8]; + fn deref(&self) -> &[u8] { + super::BufferMappedRangeSlice::slice(&self.1) + } +}