aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/buffer_source.rs142
-rw-r--r--components/script/dom/bindings/trace.rs2
-rw-r--r--components/script/dom/gpubuffer.rs389
-rw-r--r--components/script/dom/gpucommandbuffer.rs6
-rw-r--r--components/script/dom/gpudevice.rs37
-rw-r--r--components/script/dom/gpuqueue.rs17
-rw-r--r--components/script/dom/promise.rs18
-rw-r--r--components/script/dom/webidls/WebGPU.webidl43
8 files changed, 362 insertions, 292 deletions
diff --git a/components/script/dom/bindings/buffer_source.rs b/components/script/dom/bindings/buffer_source.rs
index 71c5051accd..5af00da3a45 100644
--- a/components/script/dom/bindings/buffer_source.rs
+++ b/components/script/dom/bindings/buffer_source.rs
@@ -4,18 +4,23 @@
#![allow(unsafe_code)]
-use std::borrow::BorrowMut;
use std::ffi::c_void;
use std::marker::PhantomData;
+use std::ops::Range;
use std::ptr;
-use std::sync::{Arc, Mutex};
+use std::sync::Arc;
-use js::jsapi::glue::NewExternalArrayBuffer;
-use js::jsapi::{Heap, JSObject, JS_GetArrayBufferViewBuffer, JS_IsArrayBufferViewObject};
+use js::jsapi::{
+ Heap, JSObject, JS_GetArrayBufferViewBuffer, JS_IsArrayBufferViewObject, NewExternalArrayBuffer,
+};
use js::rust::wrappers::DetachArrayBuffer;
use js::rust::{CustomAutoRooterGuard, Handle, MutableHandleObject};
-use js::typedarray::{CreateWith, TypedArray, TypedArrayElement, TypedArrayElementCreator};
+use js::typedarray::{
+ ArrayBuffer, CreateWith, HeapArrayBuffer, TypedArray, TypedArrayElement,
+ TypedArrayElementCreator,
+};
+use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::JSContext;
/// <https://webidl.spec.whatwg.org/#BufferSource>
@@ -402,44 +407,103 @@ where
}
}
-pub fn create_new_external_array_buffer<T>(
- cx: JSContext,
- mapping: Arc<Mutex<Vec<T::Element>>>,
- offset: usize,
- range_size: usize,
- m_end: usize,
-) -> HeapBufferSource<T>
-where
- T: TypedArrayElement + TypedArrayElementCreator,
- T::Element: Clone + Copy,
-{
- /// `freeFunc()` must be threadsafe, should be safely callable from any thread
- /// without causing conflicts , unexpected behavior.
- /// <https://github.com/servo/mozjs/blob/main/mozjs-sys/mozjs/js/public/ArrayBuffer.h#L89>
- unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
- // Clippy warns about "creating a `Arc` from a void raw pointer" here, but suggests
- // the exact same line to fix it. Doing the cast is tricky because of the use of
- // a generic type in this parameter.
- #[allow(clippy::from_raw_with_void_ptr)]
- let _ = Arc::from_raw(free_user_data as *const _);
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct DataBlock {
+ #[ignore_malloc_size_of = "Arc"]
+ data: Arc<Box<[u8]>>,
+ /// Data views (mutable subslices of data)
+ data_views: Vec<DataView>,
+}
+
+/// Returns true if two non-inclusive ranges overlap
+// https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-if-two-ranges-overlap
+fn range_overlap<T: std::cmp::PartialOrd>(range1: &Range<T>, range2: &Range<T>) -> bool {
+ range1.start < range2.end && range2.start < range1.end
+}
+
+impl DataBlock {
+ pub fn new_zeroed(size: usize) -> Self {
+ let data = vec![0; size];
+ Self {
+ data: Arc::new(data.into_boxed_slice()),
+ data_views: Vec::new(),
+ }
}
- unsafe {
- let mapping_slice_ptr = mapping.lock().unwrap().borrow_mut()[offset..m_end].as_mut_ptr();
+ /// Panics if there is any active view or src data is not same length
+ pub fn load(&mut self, src: &[u8]) {
+ // `Arc::get_mut` ensures there are no views
+ Arc::get_mut(&mut self.data).unwrap().clone_from_slice(src)
+ }
- // rooted! is needed to ensure memory safety and prevent potential garbage collection issues.
- // https://github.com/mozilla-spidermonkey/spidermonkey-embedding-examples/blob/esr78/docs/GC%20Rooting%20Guide.md#performance-tweaking
- rooted!(in(*cx) let array_buffer = NewExternalArrayBuffer(
- *cx,
- range_size,
- mapping_slice_ptr as _,
- Some(free_func),
- Arc::into_raw(mapping) as _,
- ));
+ /// Panics if there is any active view
+ pub fn data(&mut self) -> &mut [u8] {
+ // `Arc::get_mut` ensures there are no views
+ Arc::get_mut(&mut self.data).unwrap()
+ }
- HeapBufferSource {
- buffer_source: BufferSource::ArrayBuffer(Heap::boxed(*array_buffer)),
- phantom: PhantomData,
+ pub fn clear_views(&mut self) {
+ self.data_views.clear()
+ }
+
+ /// Returns error if requested range is already mapped
+ pub fn view(&mut self, range: Range<usize>) -> Result<&DataView, ()> {
+ if self
+ .data_views
+ .iter()
+ .any(|view| range_overlap(&view.range, &range))
+ {
+ return Err(());
+ }
+ let cx = GlobalScope::get_cx();
+ /// `freeFunc()` must be threadsafe, should be safely callable from any thread
+ /// without causing conflicts, unexpected behavior.
+ unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
+ // Clippy warns about "creating a `Arc` from a void raw pointer" here, but suggests
+ // the exact same line to fix it. Doing the cast is tricky because of the use of
+ // a generic type in this parameter.
+ #[allow(clippy::from_raw_with_void_ptr)]
+ drop(Arc::from_raw(free_user_data as *const _));
}
+ let raw: *mut Box<[u8]> = Arc::into_raw(Arc::clone(&self.data)) as _;
+ rooted!(in(*cx) let object = unsafe {
+ NewExternalArrayBuffer(
+ *cx,
+ range.end - range.start,
+ // SAFETY: This is safe because we have checked there is no overlapping view
+ (*raw)[range.clone()].as_mut_ptr() as _,
+ Some(free_func),
+ raw as _,
+ )
+ });
+ self.data_views.push(DataView {
+ range,
+ buffer: HeapArrayBuffer::from(*object).unwrap(),
+ });
+ Ok(self.data_views.last().unwrap())
+ }
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct DataView {
+ #[no_trace]
+ range: Range<usize>,
+ #[ignore_malloc_size_of = "defined in mozjs"]
+ buffer: HeapArrayBuffer,
+}
+
+impl DataView {
+ pub fn array_buffer(&self) -> ArrayBuffer {
+ unsafe { ArrayBuffer::from(self.buffer.underlying_object().get()).unwrap() }
+ }
+}
+
+impl Drop for DataView {
+ #[allow(unsafe_code)]
+ fn drop(&mut self) {
+ let cx = GlobalScope::get_cx();
+ assert!(unsafe {
+ js::jsapi::DetachArrayBuffer(*cx, self.buffer.underlying_object().handle())
+ })
}
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index cc5462f9dc1..ae05cf36ec4 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -61,7 +61,6 @@ use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString};
-use crate::dom::gpubuffer::GPUBufferState;
use crate::dom::gpucanvascontext::WebGPUContextId;
use crate::dom::htmlimageelement::SourceSet;
use crate::dom::htmlmediaelement::HTMLMediaElementFetchContext;
@@ -366,7 +365,6 @@ unsafe_no_jsmanaged_fields!(WindowProxyHandler);
unsafe_no_jsmanaged_fields!(DOMString);
unsafe_no_jsmanaged_fields!(USVString);
unsafe_no_jsmanaged_fields!(WebGPUContextId);
-unsafe_no_jsmanaged_fields!(GPUBufferState);
unsafe_no_jsmanaged_fields!(SourceSet);
unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext);
unsafe_no_jsmanaged_fields!(StreamConsumer);
diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs
index 1182e247a4a..a8d8d204163 100644
--- a/components/script/dom/gpubuffer.rs
+++ b/components/script/dom/gpubuffer.rs
@@ -2,19 +2,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::Cell;
use std::ops::Range;
use std::rc::Rc;
use std::string::String;
-use std::sync::{Arc, Mutex};
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSharedMemory;
-use js::typedarray::{ArrayBuffer, ArrayBufferU8};
+use js::typedarray::ArrayBuffer;
use webgpu::wgc::device::HostMap;
-use webgpu::{WebGPU, WebGPUBuffer, WebGPURequest, WebGPUResponse};
+use webgpu::{wgt, Mapping, WebGPU, WebGPUBuffer, WebGPURequest, WebGPUResponse};
-use super::bindings::buffer_source::{create_new_external_array_buffer, HeapBufferSource};
+use super::bindings::buffer_source::DataBlock;
+use super::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUBufferMapState, GPUFlagsConstant, GPUMapModeFlags,
+};
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
GPUBufferMethods, GPUMapModeConstants, GPUSize64,
@@ -30,31 +31,36 @@ use crate::dom::promise::Promise;
use crate::realms::InRealm;
use crate::script_runtime::JSContext;
-const RANGE_OFFSET_ALIGN_MASK: u64 = 8;
-const RANGE_SIZE_ALIGN_MASK: u64 = 4;
-
-// https://gpuweb.github.io/gpuweb/#buffer-state
-#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
-pub enum GPUBufferState {
- Mapped,
- MappedAtCreation,
- MappingPending,
- Unmapped,
- Destroyed,
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct ActiveBufferMapping {
+ // TODO(sagudev): Use IpcSharedMemory when https://github.com/servo/ipc-channel/pull/356 lands
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-data>
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-views>
+ pub data: DataBlock,
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-mode>
+ mode: GPUMapModeFlags,
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-range>
+ range: Range<u64>,
}
-#[derive(JSTraceable, MallocSizeOf)]
-pub struct GPUBufferMapInfo {
- #[ignore_malloc_size_of = "Arc"]
- #[no_trace]
- /// The `mapping` is wrapped in an `Arc` to ensure thread safety.
- /// This is necessary for integration with the SpiderMonkey engine,
- pub mapping: Arc<Mutex<Vec<u8>>>,
- pub mapping_range: Range<u64>,
- pub mapped_ranges: Vec<Range<u64>>,
- #[ignore_malloc_size_of = "defined in mozjs"]
- pub js_buffers: Vec<HeapBufferSource<ArrayBufferU8>>,
- pub map_mode: Option<u32>,
+impl ActiveBufferMapping {
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-initialize-an-active-buffer-mapping>
+ pub fn new(mode: GPUMapModeFlags, range: Range<u64>) -> Fallible<Self> {
+ // Step 1
+ let size = range.end - range.start;
+ // Step 2
+ if size > (1 << 53) - 1 {
+ return Err(Error::Range("Over MAX_SAFE_INTEGER".to_string()));
+ }
+ let size: usize = size
+ .try_into()
+ .map_err(|_| Error::Range("Over usize".to_string()))?;
+ Ok(Self {
+ data: DataBlock::new_zeroed(size),
+ mode,
+ range,
+ })
+ }
}
#[dom_struct]
@@ -64,14 +70,18 @@ pub struct GPUBuffer {
#[no_trace]
channel: WebGPU,
label: DomRefCell<USVString>,
- state: Cell<GPUBufferState>,
#[no_trace]
buffer: WebGPUBuffer,
device: Dom<GPUDevice>,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-size>
size: GPUSize64,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-usage>
+ usage: GPUFlagsConstant,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-pending_map-slot>
#[ignore_malloc_size_of = "promises are hard"]
- map_promise: DomRefCell<Option<Rc<Promise>>>,
- map_info: DomRefCell<Option<GPUBufferMapInfo>>,
+ pending_map: DomRefCell<Option<Rc<Promise>>>,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapping-slot>
+ mapping: DomRefCell<Option<ActiveBufferMapping>>,
}
impl GPUBuffer {
@@ -79,38 +89,37 @@ impl GPUBuffer {
channel: WebGPU,
buffer: WebGPUBuffer,
device: &GPUDevice,
- state: GPUBufferState,
size: GPUSize64,
- map_info: DomRefCell<Option<GPUBufferMapInfo>>,
+ usage: GPUFlagsConstant,
+ mapping: Option<ActiveBufferMapping>,
label: USVString,
) -> Self {
Self {
reflector_: Reflector::new(),
channel,
label: DomRefCell::new(label),
- state: Cell::new(state),
device: Dom::from_ref(device),
buffer,
- map_promise: DomRefCell::new(None),
+ pending_map: DomRefCell::new(None),
size,
- map_info,
+ usage,
+ mapping: DomRefCell::new(mapping),
}
}
- #[allow(clippy::too_many_arguments)]
pub fn new(
global: &GlobalScope,
channel: WebGPU,
buffer: WebGPUBuffer,
device: &GPUDevice,
- state: GPUBufferState,
size: GPUSize64,
- map_info: DomRefCell<Option<GPUBufferMapInfo>>,
+ usage: GPUFlagsConstant,
+ mapping: Option<ActiveBufferMapping>,
label: USVString,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(GPUBuffer::new_inherited(
- channel, buffer, device, state, size, map_info, label,
+ channel, buffer, device, size, usage, mapping, label,
)),
global,
)
@@ -121,86 +130,49 @@ impl GPUBuffer {
pub fn id(&self) -> WebGPUBuffer {
self.buffer
}
-
- pub fn state(&self) -> GPUBufferState {
- self.state.get()
- }
}
impl Drop for GPUBuffer {
fn drop(&mut self) {
- if matches!(self.state(), GPUBufferState::Destroyed) {
- return;
- }
- if let Err(e) = self
- .channel
- .0
- .send(WebGPURequest::DropBuffer(self.buffer.0))
- {
- warn!(
- "Failed to send WebGPURequest::DropBuffer({:?}) ({})",
- self.buffer.0, e
- );
- };
+ self.Destroy()
}
}
impl GPUBufferMethods for GPUBuffer {
+ #[allow(unsafe_code)]
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap>
- fn Unmap(&self) -> Fallible<()> {
- let cx = GlobalScope::get_cx();
+ fn Unmap(&self) {
// Step 1
- match self.state.get() {
- GPUBufferState::Unmapped | GPUBufferState::Destroyed => {
- // TODO: Record validation error on the current scope
- return Ok(());
- },
- // Step 3
- GPUBufferState::Mapped | GPUBufferState::MappedAtCreation => {
- let mut info = self.map_info.borrow_mut();
- let m_info = if let Some(m_info) = info.as_mut() {
- m_info
- } else {
- return Err(Error::Operation);
- };
- let m_range = m_info.mapping_range.clone();
- if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
- buffer_id: self.id().0,
- device_id: self.device.id().0,
- array_buffer: IpcSharedMemory::from_bytes(&m_info.mapping.lock().unwrap()),
- is_map_read: m_info.map_mode == Some(GPUMapModeConstants::READ),
- offset: m_range.start,
- size: m_range.end - m_range.start,
- }) {
- warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
- }
- // Step 3.3
- m_info.js_buffers.drain(..).for_each(|obj| {
- obj.detach_buffer(cx);
- });
- },
- // Step 2
- GPUBufferState::MappingPending => {
- let promise = self.map_promise.borrow_mut().take().unwrap();
- promise.reject_error(Error::Operation);
- },
+ if let Some(promise) = self.pending_map.borrow_mut().take() {
+ promise.reject_error(Error::Abort);
+ }
+ // Step 2
+ let mut mapping = self.mapping.borrow_mut().take();
+ let mapping = if let Some(mapping) = mapping.as_mut() {
+ mapping
+ } else {
+ return;
};
- // Step 4
- self.state.set(GPUBufferState::Unmapped);
- *self.map_info.borrow_mut() = None;
- Ok(())
+
+ // Step 3
+ mapping.data.clear_views();
+ // Step 5&7
+ if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
+ buffer_id: self.id().0,
+ array_buffer: IpcSharedMemory::from_bytes(mapping.data.data()),
+ write_back: mapping.mode >= GPUMapModeConstants::WRITE,
+ offset: mapping.range.start,
+ size: mapping.range.end - mapping.range.start,
+ }) {
+ warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
+ }
}
- /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy>
- fn Destroy(&self) -> Fallible<()> {
- let state = self.state.get();
- match state {
- GPUBufferState::Mapped | GPUBufferState::MappedAtCreation => {
- self.Unmap()?;
- },
- GPUBufferState::Destroyed => return Ok(()),
- _ => {},
- };
+ /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy
+ fn Destroy(&self) {
+ // Step 1
+ self.Unmap();
+ // Step 2
if let Err(e) = self
.channel
.0
@@ -211,11 +183,9 @@ impl GPUBufferMethods for GPUBuffer {
self.buffer.0, e
);
};
- self.state.set(GPUBufferState::Destroyed);
- Ok(())
}
- /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapasync-offset-size>
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapasync>
fn MapAsync(
&self,
mode: u32,
@@ -224,22 +194,16 @@ impl GPUBufferMethods for GPUBuffer {
comp: InRealm,
) -> Rc<Promise> {
let promise = Promise::new_in_current_realm(comp);
- let range_size = if let Some(s) = size {
- s
- } else if offset >= self.size {
+ // Step 2
+ if self.pending_map.borrow().is_some() {
promise.reject_error(Error::Operation);
return promise;
- } else {
- self.size - offset
- };
- if self.state.get() != GPUBufferState::Unmapped {
- self.device
- .dispatch_error(webgpu::Error::Validation(String::from(
- "Buffer is not Unmapped",
- )));
- promise.reject_error(Error::Abort);
- return promise;
}
+ // Step 4
+ *self.pending_map.borrow_mut() = Some(promise.clone());
+ // Step 5
+
+ // This should be bitflags in wgpu-core
let host_map = match mode {
GPUMapModeConstants::READ => HostMap::Read,
GPUMapModeConstants::WRITE => HostMap::Write,
@@ -248,13 +212,11 @@ impl GPUBufferMethods for GPUBuffer {
.dispatch_error(webgpu::Error::Validation(String::from(
"Invalid MapModeFlags",
)));
- promise.reject_error(Error::Abort);
+ self.map_failure(&promise);
return promise;
},
};
- let map_range = offset..offset + range_size;
-
let sender = response_async(&promise, self);
if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync {
sender,
@@ -262,80 +224,53 @@ impl GPUBufferMethods for GPUBuffer {
device_id: self.device.id().0,
host_map,
offset,
- size: Some(range_size),
+ size,
}) {
warn!(
"Failed to send BufferMapAsync ({:?}) ({})",
self.buffer.0, e
);
- promise.reject_error(Error::Operation);
+ self.map_failure(&promise);
return promise;
}
-
- self.state.set(GPUBufferState::MappingPending);
- *self.map_info.borrow_mut() = Some(GPUBufferMapInfo {
- mapping: Arc::new(Mutex::new(Vec::with_capacity(0))),
- mapping_range: map_range,
- mapped_ranges: Vec::new(),
- js_buffers: Vec::new(),
- map_mode: Some(mode),
- });
- *self.map_promise.borrow_mut() = Some(promise.clone());
+ // Step 6
promise
}
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-getmappedrange>
+ #[allow(unsafe_code)]
fn GetMappedRange(
&self,
- cx: JSContext,
+ _cx: JSContext,
offset: GPUSize64,
size: Option<GPUSize64>,
) -> Fallible<ArrayBuffer> {
let range_size = if let Some(s) = size {
s
- } else if offset >= self.size {
- return Err(Error::Operation);
- } else {
- self.size - offset
- };
- let m_end = offset + range_size;
- let mut info = self.map_info.borrow_mut();
- let m_info = if let Some(m_info) = info.as_mut() {
- m_info
} else {
- return Err(Error::Operation);
+ self.size.checked_sub(offset).unwrap_or(0)
};
- let mut valid = matches!(
- self.state.get(),
- GPUBufferState::Mapped | GPUBufferState::MappedAtCreation
- );
+ // Step 2: validation
+ let mut mapping = self.mapping.borrow_mut();
+ let mapping = mapping.as_mut().ok_or(Error::Operation)?;
- valid &= offset % RANGE_OFFSET_ALIGN_MASK == 0 &&
- range_size % RANGE_SIZE_ALIGN_MASK == 0 &&
- offset >= m_info.mapping_range.start &&
- m_end <= m_info.mapping_range.end;
- valid &= m_info
- .mapped_ranges
- .iter()
- .all(|range| range.start >= m_end || range.end <= offset);
+ let valid = offset % wgt::MAP_ALIGNMENT == 0 &&
+ range_size % wgt::COPY_BUFFER_ALIGNMENT == 0 &&
+ offset >= mapping.range.start &&
+ offset + range_size <= mapping.range.end;
if !valid {
return Err(Error::Operation);
}
- let heap_typed_array = create_new_external_array_buffer::<ArrayBufferU8>(
- cx,
- Arc::clone(&m_info.mapping),
- offset as usize,
- range_size as usize,
- m_end as usize,
- );
-
- let result = heap_typed_array.get_buffer().map_err(|_| Error::JSFailed);
-
- m_info.mapped_ranges.push(offset..m_end);
- m_info.js_buffers.push(heap_typed_array);
-
- result
+ // Step 4
+ // only mapping.range is mapped with mapping.range.start at 0
+ // so we need to rebase range to mapped.range
+ let rebased_offset = (offset - mapping.range.start) as usize;
+ mapping
+ .data
+ .view(rebased_offset..rebased_offset + range_size as usize)
+ .map(|view| view.array_buffer())
+ .map_err(|()| Error::Operation)
}
/// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
@@ -347,30 +282,96 @@ impl GPUBufferMethods for GPUBuffer {
fn SetLabel(&self, value: USVString) {
*self.label.borrow_mut() = value;
}
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-size>
+ fn Size(&self) -> GPUSize64 {
+ self.size
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-usage>
+ fn Usage(&self) -> GPUFlagsConstant {
+ self.usage
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapstate>
+ fn MapState(&self) -> GPUBufferMapState {
+ // Step 1&2&3
+ if self.mapping.borrow().is_some() {
+ GPUBufferMapState::Mapped
+ } else if self.pending_map.borrow().is_some() {
+ GPUBufferMapState::Pending
+ } else {
+ GPUBufferMapState::Unmapped
+ }
+ }
+}
+
+impl GPUBuffer {
+ fn map_failure(&self, p: &Rc<Promise>) {
+ let mut pending_map = self.pending_map.borrow_mut();
+ // Step 1
+ if pending_map.as_ref() != Some(p) {
+ assert!(p.is_rejected());
+ return;
+ }
+ // Step 2
+ assert!(p.is_pending());
+ // Step 3
+ pending_map.take();
+ // Step 4
+ if self.device.is_lost() {
+ p.reject_error(Error::Abort);
+ } else {
+ p.reject_error(Error::Operation);
+ }
+ }
+
+ fn map_success(&self, p: &Rc<Promise>, wgpu_mapping: Mapping) {
+ let mut pending_map = self.pending_map.borrow_mut();
+
+ // Step 1
+ if pending_map.as_ref() != Some(p) {
+ assert!(p.is_rejected());
+ return;
+ }
+
+ // Step 2
+ assert!(p.is_pending());
+
+ // Step 4
+ let mapping = ActiveBufferMapping::new(
+ match wgpu_mapping.mode {
+ HostMap::Read => GPUMapModeConstants::READ,
+ HostMap::Write => GPUMapModeConstants::WRITE,
+ },
+ wgpu_mapping.range,
+ );
+
+ match mapping {
+ Err(error) => {
+ *pending_map = None;
+ p.reject_error(error.clone());
+ },
+ Ok(mut mapping) => {
+ // Step 5
+ mapping.data.load(&wgpu_mapping.data);
+ // Step 6
+ self.mapping.borrow_mut().replace(mapping);
+ // Step 7
+ pending_map.take();
+ p.resolve_native(&());
+ },
+ }
+ }
}
impl AsyncWGPUListener for GPUBuffer {
+ #[allow(unsafe_code)]
fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) {
match response {
- WebGPUResponse::BufferMapAsync(Ok(bytes)) => {
- *self
- .map_info
- .borrow_mut()
- .as_mut()
- .unwrap()
- .mapping
- .lock()
- .unwrap()
- .as_mut() = bytes.to_vec();
- promise.resolve_native(&());
- self.state.set(GPUBufferState::Mapped);
- },
- WebGPUResponse::BufferMapAsync(Err(e)) => {
- warn!("Could not map buffer({:?})", e);
- promise.reject_error(Error::Abort);
- },
- _ => unreachable!("GPUBuffer received wrong WebGPUResponse"),
+ WebGPUResponse::BufferMapAsync(Ok(mapping)) => self.map_success(promise, mapping),
+ WebGPUResponse::BufferMapAsync(Err(_)) => self.map_failure(promise),
+ _ => unreachable!("Wrong response received on AsyncWGPUListener for GPUBuffer"),
}
- *self.map_promise.borrow_mut() = None;
}
}
diff --git a/components/script/dom/gpucommandbuffer.rs b/components/script/dom/gpucommandbuffer.rs
index e50c350f18c..be0edf8f2c8 100644
--- a/components/script/dom/gpucommandbuffer.rs
+++ b/components/script/dom/gpucommandbuffer.rs
@@ -8,7 +8,7 @@ use std::hash::{Hash, Hasher};
use dom_struct::dom_struct;
use webgpu::{WebGPU, WebGPUCommandBuffer, WebGPURequest};
-use crate::dom::bindings::cell::{DomRefCell, Ref};
+use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUCommandBufferMethods;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
@@ -89,10 +89,6 @@ impl GPUCommandBuffer {
pub fn id(&self) -> WebGPUCommandBuffer {
self.command_buffer
}
-
- pub fn buffers(&self) -> Ref<HashSet<Dom<GPUBuffer>>> {
- self.buffers.borrow()
- }
}
impl GPUCommandBufferMethods for GPUCommandBuffer {
diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs
index e3f19813890..47ab1e29fac 100644
--- a/components/script/dom/gpudevice.rs
+++ b/components/script/dom/gpudevice.rs
@@ -9,7 +9,6 @@ use std::cell::Cell;
use std::collections::HashMap;
use std::num::NonZeroU64;
use std::rc::Rc;
-use std::sync::{Arc, Mutex};
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject};
@@ -24,7 +23,9 @@ use webgpu::{
WebGPUResponse,
};
-use super::bindings::codegen::Bindings::WebGPUBinding::{GPUPipelineErrorReason, GPUTextureFormat};
+use super::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUMapModeConstants, GPUPipelineErrorReason, GPUTextureFormat,
+};
use super::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
use super::bindings::error::Fallible;
use super::gpu::AsyncWGPUListener;
@@ -55,7 +56,7 @@ use crate::dom::gpu::response_async;
use crate::dom::gpuadapter::GPUAdapter;
use crate::dom::gpubindgroup::GPUBindGroup;
use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
-use crate::dom::gpubuffer::{GPUBuffer, GPUBufferMapInfo, GPUBufferState};
+use crate::dom::gpubuffer::{ActiveBufferMapping, GPUBuffer};
use crate::dom::gpucommandencoder::GPUCommandEncoder;
use crate::dom::gpucomputepipeline::GPUComputePipeline;
use crate::dom::gpuconvert::{
@@ -213,6 +214,10 @@ impl GPUDevice {
}
}
+ pub fn is_lost(&self) -> bool {
+ self.lost_promise.borrow().is_fulfilled()
+ }
+
fn get_pipeline_layout_data(
&self,
layout: &GPUPipelineLayoutOrGPUAutoLayoutMode,
@@ -452,31 +457,23 @@ impl GPUDeviceMethods for GPUDevice {
.expect("Failed to create WebGPU buffer");
let buffer = webgpu::WebGPUBuffer(id);
- let map_info;
- let state;
- if descriptor.mappedAtCreation {
- let buf_data = vec![0u8; descriptor.size as usize];
- map_info = DomRefCell::new(Some(GPUBufferMapInfo {
- mapping: Arc::new(Mutex::new(buf_data)),
- mapping_range: 0..descriptor.size,
- mapped_ranges: Vec::new(),
- js_buffers: Vec::new(),
- map_mode: None,
- }));
- state = GPUBufferState::MappedAtCreation;
+ let mapping = if descriptor.mappedAtCreation {
+ Some(ActiveBufferMapping::new(
+ GPUMapModeConstants::WRITE,
+ 0..descriptor.size,
+ )?)
} else {
- map_info = DomRefCell::new(None);
- state = GPUBufferState::Unmapped;
- }
+ None
+ };
Ok(GPUBuffer::new(
&self.global(),
self.channel.clone(),
buffer,
self,
- state,
descriptor.size,
- map_info,
+ descriptor.usage,
+ mapping,
descriptor.parent.label.clone(),
))
}
diff --git a/components/script/dom/gpuqueue.rs b/components/script/dom/gpuqueue.rs
index aa0daa5ae0f..e4a4102141d 100644
--- a/components/script/dom/gpuqueue.rs
+++ b/components/script/dom/gpuqueue.rs
@@ -20,7 +20,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::USVString;
use crate::dom::globalscope::GlobalScope;
-use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState};
+use crate::dom::gpubuffer::GPUBuffer;
use crate::dom::gpucommandbuffer::GPUCommandBuffer;
use crate::dom::gpuconvert::{
convert_ic_texture, convert_image_data_layout, convert_texture_size_to_dict,
@@ -80,21 +80,6 @@ impl GPUQueueMethods for GPUQueue {
/// <https://gpuweb.github.io/gpuweb/#dom-gpuqueue-submit>
fn Submit(&self, command_buffers: Vec<DomRoot<GPUCommandBuffer>>) {
- let valid = command_buffers.iter().all(|cb| {
- cb.buffers()
- .iter()
- .all(|b| matches!(b.state(), GPUBufferState::Unmapped))
- });
- if !valid {
- self.device
- .borrow()
- .as_ref()
- .unwrap()
- .dispatch_error(webgpu::Error::Validation(String::from(
- "Referenced GPUBuffer(s) are not Unmapped",
- )));
- return;
- }
let command_buffers = command_buffers.iter().map(|cb| cb.id().0).collect();
self.channel
.0
diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs
index 7c98ddbd2ad..38e5a9112ac 100644
--- a/components/script/dom/promise.rs
+++ b/components/script/dom/promise.rs
@@ -52,6 +52,12 @@ pub struct Promise {
permanent_js_root: Heap<JSVal>,
}
+impl PartialEq for Promise {
+ fn eq(&self, other: &Self) -> bool {
+ self.reflector == other.reflector
+ }
+}
+
/// Private helper to enable adding new methods to `Rc<Promise>`.
trait PromiseHelper {
fn initialize(&self, cx: SafeJSContext);
@@ -232,6 +238,18 @@ impl Promise {
}
#[allow(unsafe_code)]
+ pub fn is_rejected(&self) -> bool {
+ let state = unsafe { GetPromiseState(self.promise_obj()) };
+ matches!(state, PromiseState::Rejected)
+ }
+
+ #[allow(unsafe_code)]
+ pub fn is_pending(&self) -> bool {
+ let state = unsafe { GetPromiseState(self.promise_obj()) };
+ matches!(state, PromiseState::Pending)
+ }
+
+ #[allow(unsafe_code)]
pub fn promise_obj(&self) -> HandleObject {
let obj = self.reflector().get_jsobject();
unsafe {
diff --git a/components/script/dom/webidls/WebGPU.webidl b/components/script/dom/webidls/WebGPU.webidl
index db7889aff73..65694fb3b29 100644
--- a/components/script/dom/webidls/WebGPU.webidl
+++ b/components/script/dom/webidls/WebGPU.webidl
@@ -165,17 +165,28 @@ GPUDevice includes GPUObjectBase;
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUBuffer {
+ readonly attribute GPUSize64Out size;
+ readonly attribute GPUFlagsConstant usage;
+
+ readonly attribute GPUBufferMapState mapState;
+
[NewObject]
Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
[NewObject, Throws]
ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
- [Throws]
undefined unmap();
- [Throws]
+
undefined destroy();
};
GPUBuffer includes GPUObjectBase;
+
+enum GPUBufferMapState {
+ "unmapped",
+ "pending",
+ "mapped",
+};
+
dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
required GPUSize64 size;
required GPUBufferUsageFlags usage;
@@ -184,24 +195,24 @@ dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
-interface GPUBufferUsage {
- const GPUBufferUsageFlags MAP_READ = 0x0001;
- const GPUBufferUsageFlags MAP_WRITE = 0x0002;
- const GPUBufferUsageFlags COPY_SRC = 0x0004;
- const GPUBufferUsageFlags COPY_DST = 0x0008;
- const GPUBufferUsageFlags INDEX = 0x0010;
- const GPUBufferUsageFlags VERTEX = 0x0020;
- const GPUBufferUsageFlags UNIFORM = 0x0040;
- const GPUBufferUsageFlags STORAGE = 0x0080;
- const GPUBufferUsageFlags INDIRECT = 0x0100;
- const GPUBufferUsageFlags QUERY_RESOLVE = 0x0200;
+namespace GPUBufferUsage {
+ const GPUFlagsConstant MAP_READ = 0x0001;
+ const GPUFlagsConstant MAP_WRITE = 0x0002;
+ const GPUFlagsConstant COPY_SRC = 0x0004;
+ const GPUFlagsConstant COPY_DST = 0x0008;
+ const GPUFlagsConstant INDEX = 0x0010;
+ const GPUFlagsConstant VERTEX = 0x0020;
+ const GPUFlagsConstant UNIFORM = 0x0040;
+ const GPUFlagsConstant STORAGE = 0x0080;
+ const GPUFlagsConstant INDIRECT = 0x0100;
+ const GPUFlagsConstant QUERY_RESOLVE = 0x0200;
};
typedef [EnforceRange] unsigned long GPUMapModeFlags;
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
-interface GPUMapMode {
- const GPUMapModeFlags READ = 0x0001;
- const GPUMapModeFlags WRITE = 0x0002;
+namespace GPUMapMode {
+ const GPUFlagsConstant READ = 0x0001;
+ const GPUFlagsConstant WRITE = 0x0002;
};
[Exposed=(Window, DedicatedWorker), Serializable , Pref="dom.webgpu.enabled"]