710: Implement read_buffer r=kvark a=fintelia

I'm not sure about the exact interface that makes sense, but this PR sketches out how read_buffer (and eventually also read_texture) could be implemented. 

Resolves #694

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
Co-authored-by: Jonathan Behrens <fintelia@gmail.com>
This commit is contained in:
bors[bot]
2021-02-06 20:53:26 +00:00
committed by GitHub
4 changed files with 48 additions and 13 deletions

View File

@@ -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

View File

@@ -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<u8>,
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

View File

@@ -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,
}

View File

@@ -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<Output=Result<Self, super::BufferAsyncError>> + 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)
}
}