diff options
Diffstat (limited to 'components/script/dom/gpubuffer.rs')
-rw-r--r-- | components/script/dom/gpubuffer.rs | 174 |
1 files changed, 163 insertions, 11 deletions
diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index e0694e4d726..a02c33cad64 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -6,17 +6,36 @@ use crate::dom::bindings::cell::{DomRefCell, Ref}; use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::{ self, GPUBufferMethods, GPUBufferSize, }; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::globalscope::GlobalScope; +use crate::dom::gpu::{response_async, AsyncWGPUListener}; +use crate::dom::promise::Promise; +use crate::realms::InRealm; use dom_struct::dom_struct; +use js::jsapi::{Heap, JSObject}; +use js::jsval::UndefinedValue; +use js::rust::jsapi_wrapped::{DetachArrayBuffer, IsPromiseObject, RejectPromise}; +use js::rust::MutableHandle; +use js::typedarray::{ArrayBuffer, CreateWith}; use std::cell::Cell; -use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; +use std::ptr; +use std::rc::Rc; +use webgpu::{ + wgpu::resource::BufferUsage, WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest, WebGPUResponse, +}; -#[derive(MallocSizeOf)] +// https://gpuweb.github.io/gpuweb/#buffer-state +#[derive(Clone, MallocSizeOf)] pub enum GPUBufferState { - Mapped, + MappedForReading, + MappedForWriting, + MappedPendingForReading, + MappedPendingForWriting, Unmapped, Destroyed, } @@ -24,7 +43,7 @@ pub enum GPUBufferState { #[dom_struct] pub struct GPUBuffer { reflector_: Reflector, - #[ignore_malloc_size_of = "channels are hard"] + #[ignore_malloc_size_of = "defined in webgpu"] channel: WebGPU, label: DomRefCell<Option<DOMString>>, size: GPUBufferSize, @@ -33,6 +52,8 @@ pub struct GPUBuffer { buffer: WebGPUBuffer, device: WebGPUDevice, valid: Cell<bool>, + #[ignore_malloc_size_of = "defined in mozjs"] + mapping: RootedTraceableBox<Heap<*mut JSObject>>, } impl GPUBuffer { @@ -44,6 +65,7 @@ impl GPUBuffer { size: GPUBufferSize, usage: u32, valid: bool, + mapping: RootedTraceableBox<Heap<*mut JSObject>>, ) -> GPUBuffer { Self { reflector_: Reflector::new(), @@ -55,6 +77,7 @@ impl GPUBuffer { valid: Cell::new(valid), device, buffer, + mapping, } } @@ -68,10 +91,11 @@ impl GPUBuffer { size: GPUBufferSize, usage: u32, valid: bool, + mapping: RootedTraceableBox<Heap<*mut JSObject>>, ) -> DomRoot<GPUBuffer> { reflect_dom_object( Box::new(GPUBuffer::new_inherited( - channel, buffer, device, state, size, usage, valid, + channel, buffer, device, state, size, usage, valid, mapping, )), global, GPUBufferBinding::Wrap, @@ -104,19 +128,59 @@ impl Drop for GPUBuffer { } impl GPUBufferMethods for GPUBuffer { + #[allow(unsafe_code)] /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap fn Unmap(&self) { - self.channel - .0 - .send(WebGPURequest::UnmapBuffer(self.buffer)) - .unwrap(); + let cx = self.global().get_cx(); + // Step 1 + match *self.state.borrow() { + GPUBufferState::Unmapped | GPUBufferState::Destroyed => { + // TODO: Record validation error on the current scope + return; + }, + GPUBufferState::MappedForWriting => { + // Step 3.1 + match ArrayBuffer::from(self.mapping.get()) { + Ok(array_buffer) => { + self.channel + .0 + .send(WebGPURequest::UnmapBuffer( + self.device.0, + self.id(), + array_buffer.to_vec(), + )) + .unwrap(); + // Step 3.2 + unsafe { + DetachArrayBuffer(*cx, self.mapping.handle()); + } + }, + _ => { + // Step 2 + unsafe { + if IsPromiseObject(self.mapping.handle()) { + let err = Error::Abort; + rooted!(in(*cx) let mut undef = UndefinedValue()); + err.to_jsval(*cx, &self.global(), undef.handle_mut()); + RejectPromise(*cx, self.mapping.handle(), undef.handle()); + }; + } + }, + }; + }, + _ => {}, + }; + // Step 3.3 + self.mapping.set(ptr::null_mut()); + // Step 4 *self.state.borrow_mut() = GPUBufferState::Unmapped; } /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy fn Destroy(&self) { - match *self.state.borrow() { - GPUBufferState::Mapped => { + let state = self.state.borrow().clone(); + match state { + GPUBufferState::MappedForReading | GPUBufferState::MappedForWriting => { self.Unmap(); }, _ => {}, @@ -128,6 +192,72 @@ impl GPUBufferMethods for GPUBuffer { *self.state.borrow_mut() = GPUBufferState::Destroyed; } + #[allow(unsafe_code)] + /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapreadasync + fn MapReadAsync(&self, comp: InRealm) -> Rc<Promise> { + // Step 1 & 2 + let promise = Promise::new_in_current_realm(&self.global(), comp); + match *self.state.borrow() { + GPUBufferState::Unmapped => { + match BufferUsage::from_bits(self.usage) { + Some(usage) => { + if !usage.contains(BufferUsage::MAP_READ) { + // TODO: Record validation error on the current scope + promise.reject_error(Error::Abort); + return promise; + }; + }, + None => { + promise.reject_error(Error::Abort); + return promise; + }, + } + }, + _ => { + promise.reject_error(Error::Abort); + return promise; + }, + } + // Step 3 + self.mapping.set(*promise.promise_obj()); + // Step 4 + *self.state.borrow_mut() = GPUBufferState::MappedPendingForReading; + + // Step 5.1 + if unsafe { + ArrayBuffer::create( + *self.global().get_cx(), + CreateWith::Length(self.size as u32), + MutableHandle::from_raw(self.mapping.handle_mut()), + ) + } + .is_err() + { + promise.reject_error(Error::Operation); + return promise; + } + + let sender = response_async(&promise, self); + if self + .channel + .0 + .send(WebGPURequest::MapReadAsync( + sender, + self.buffer.0, + self.device.0, + self.usage, + self.size, + )) + .is_err() + { + promise.reject_error(Error::Operation); + return promise; + } + + // Step 6 + promise + } + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label fn GetLabel(&self) -> Option<DOMString> { self.label.borrow().clone() @@ -138,3 +268,25 @@ impl GPUBufferMethods for GPUBuffer { *self.label.borrow_mut() = value; } } + +impl AsyncWGPUListener for GPUBuffer { + #[allow(unsafe_code)] + fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) { + match response { + WebGPUResponse::MapReadAsync(bytes) => unsafe { + match ArrayBuffer::from(self.mapping.get()) { + Ok(mut array_buffer) => { + // Step 5.2 + array_buffer.update(&bytes); + // Step 5.3 + *self.state.borrow_mut() = GPUBufferState::MappedForReading; + // Step 5.4 + promise.resolve_native(&array_buffer); + }, + _ => promise.reject_error(Error::Operation), + }; + }, + _ => promise.reject_error(Error::Operation), + } + } +} |