Add get_mapped_range_as_array_buffer which - on wasm WebGPU builds - avoids copying mapped data into wasm heap (#4042) (#4103)

Co-authored-by: Ryan Kaplan <ryan@Ryans-M2.local>
This commit is contained in:
Ryan Kaplan 2023-09-05 10:47:56 -07:00 committed by GitHub
parent 21098cdace
commit 8c891114ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 4 deletions

View File

@ -40,6 +40,12 @@ Bottom level categories:
## Unreleased
## v0.17.1
### Added/New Features
- Add `get_mapped_range_as_array_buffer` for faster buffer read-backs in wasm builds. By @ryankaplan in [#4042] (https://github.com/gfx-rs/wgpu/pull/4042).
## v0.17.0 (2023-07-20)
This is the first release that featured `wgpu-info` as a binary crate for getting information about what devices wgpu sees in your system. It can dump the information in both human readable format and json.

View File

@ -1890,10 +1890,8 @@ impl crate::context::Context for Context {
buffer_data: &Self::BufferData,
sub_range: Range<wgt::BufferAddress>,
) -> Box<dyn crate::context::BufferMappedRange> {
let array_buffer = buffer_data.0.get_mapped_range_with_f64_and_f64(
sub_range.start as f64,
(sub_range.end - sub_range.start) as f64,
);
let array_buffer =
self.buffer_get_mapped_range_as_array_buffer(_buffer, buffer_data, sub_range);
let actual_mapping = js_sys::Uint8Array::new(&array_buffer);
let temporary_mapping = actual_mapping.to_vec();
Box::new(BufferMappedRange {
@ -1902,6 +1900,18 @@ impl crate::context::Context for Context {
})
}
fn buffer_get_mapped_range_as_array_buffer(
&self,
_buffer: &Self::BufferId,
buffer_data: &Self::BufferData,
sub_range: Range<wgt::BufferAddress>,
) -> js_sys::ArrayBuffer {
buffer_data.0.get_mapped_range_with_f64_and_f64(
sub_range.start as f64,
(sub_range.end - sub_range.start) as f64,
)
}
fn buffer_unmap(&self, _buffer: &Self::BufferId, buffer_data: &Self::BufferData) {
buffer_data.0.unmap();
}

View File

@ -307,6 +307,16 @@ pub trait Context: Debug + WasmNotSend + WasmNotSync + Sized {
buffer_data: &Self::BufferData,
sub_range: Range<BufferAddress>,
) -> Box<dyn BufferMappedRange>;
#[cfg(all(
target_arch = "wasm32",
not(any(target_os = "emscripten", feature = "webgl"))
))]
fn buffer_get_mapped_range_as_array_buffer(
&self,
buffer: &Self::BufferId,
buffer_data: &Self::BufferData,
sub_range: Range<BufferAddress>,
) -> js_sys::ArrayBuffer;
fn buffer_unmap(&self, buffer: &Self::BufferId, buffer_data: &Self::BufferData);
fn texture_create_view(
&self,
@ -1375,6 +1385,16 @@ pub(crate) trait DynContext: Debug + WasmNotSend + WasmNotSync {
buffer_data: &crate::Data,
sub_range: Range<BufferAddress>,
) -> Box<dyn BufferMappedRange>;
#[cfg(all(
target_arch = "wasm32",
not(any(target_os = "emscripten", feature = "webgl"))
))]
fn buffer_get_mapped_range_as_array_buffer(
&self,
buffer: &ObjectId,
buffer_data: &crate::Data,
sub_range: Range<BufferAddress>,
) -> js_sys::ArrayBuffer;
fn buffer_unmap(&self, buffer: &ObjectId, buffer_data: &crate::Data);
fn texture_create_view(
&self,
@ -2453,6 +2473,21 @@ where
Context::buffer_get_mapped_range(self, &buffer, buffer_data, sub_range)
}
#[cfg(all(
target_arch = "wasm32",
not(any(target_os = "emscripten", feature = "webgl"))
))]
fn buffer_get_mapped_range_as_array_buffer(
&self,
buffer: &ObjectId,
buffer_data: &crate::Data,
sub_range: Range<BufferAddress>,
) -> js_sys::ArrayBuffer {
let buffer = <T::BufferId>::from(*buffer);
let buffer_data = downcast_ref(buffer_data);
Context::buffer_get_mapped_range_as_array_buffer(self, &buffer, buffer_data, sub_range)
}
fn buffer_unmap(&self, buffer: &ObjectId, buffer_data: &crate::Data) {
let buffer = <T::BufferId>::from(*buffer);
let buffer_data = downcast_ref(buffer_data);

View File

@ -2931,6 +2931,26 @@ impl<'a> BufferSlice<'a> {
BufferView { slice: *self, data }
}
/// Synchronously and immediately map a buffer for reading. If the buffer is not immediately mappable
/// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will panic.
///
/// This is useful in wasm builds when you want to pass mapped data directly to js. Unlike `get_mapped_range`
/// which unconditionally copies mapped data into the wasm heap, this function directly hands you the
/// ArrayBuffer that we mapped the data into in js.
#[cfg(all(
target_arch = "wasm32",
not(any(target_os = "emscripten", feature = "webgl"))
))]
pub fn get_mapped_range_as_array_buffer(&self) -> js_sys::ArrayBuffer {
let end = self.buffer.map_context.lock().add(self.offset, self.size);
DynContext::buffer_get_mapped_range_as_array_buffer(
&*self.buffer.context,
&self.buffer.id,
self.buffer.data.as_ref(),
self.offset..end,
)
}
/// Synchronously and immediately map a buffer for writing. If the buffer is not immediately mappable
/// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will panic.
pub fn get_mapped_range_mut(&self) -> BufferViewMut<'a> {