Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved

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