Skip to content

Commit

Permalink
Add get_mapped_range_as_array_buffer which - on wasm WebGPU builds - …
Browse files Browse the repository at this point in the history
…avoids copying mapped data into wasm heap (#4042) (#4103)

Co-authored-by: Ryan Kaplan <ryan@Ryans-M2.local>
  • Loading branch information
ryankaplan and Ryan Kaplan authored Sep 5, 2023
1 parent 21098cd commit 8c89111
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 4 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 14 additions & 4 deletions wgpu/src/backend/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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();
}
Expand Down
35 changes: 35 additions & 0 deletions wgpu/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down

0 comments on commit 8c89111

Please sign in to comment.