aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf4
-rw-r--r--components/script/dom/gpubuffer.rs174
-rw-r--r--components/script/dom/gpudevice.rs63
-rw-r--r--components/script/dom/webidls/GPUBuffer.webidl2
-rw-r--r--components/webgpu/lib.rs72
5 files changed, 253 insertions, 62 deletions
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index 5f978f5d3c4..7aa8ffa4c8b 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -150,6 +150,10 @@ DOMInterfaces = {
'GPUAdapter': {
'inRealms': ['RequestDevice'],
+},
+
+'GPUBuffer': {
+ 'inRealms': ['MapReadAsync'],
}
}
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),
+ }
+ }
+}
diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs
index 1d17961f87c..40e97d34d28 100644
--- a/components/script/dom/gpudevice.rs
+++ b/components/script/dom/gpudevice.rs
@@ -46,7 +46,7 @@ use webgpu::wgpu::binding_model::{
ShaderStage,
};
use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage};
-use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPUQueue, WebGPURequest};
+use webgpu::{WebGPU, WebGPUDevice, WebGPUQueue, WebGPURequest};
#[dom_struct]
pub struct GPUDevice {
@@ -106,38 +106,6 @@ impl GPUDevice {
}
impl GPUDevice {
- unsafe fn resolve_create_buffer_mapped(
- &self,
- cx: SafeJSContext,
- gpu_buffer: WebGPUBuffer,
- array_buffer: Vec<u8>,
- descriptor: BufferDescriptor,
- valid: bool,
- ) -> Vec<JSVal> {
- rooted!(in(*cx) let mut js_array_buffer = ptr::null_mut::<JSObject>());
- let mut out = Vec::new();
- assert!(ArrayBuffer::create(
- *cx,
- CreateWith::Slice(array_buffer.as_slice()),
- js_array_buffer.handle_mut(),
- )
- .is_ok());
-
- let buff = GPUBuffer::new(
- &self.global(),
- self.channel.clone(),
- gpu_buffer,
- self.device,
- GPUBufferState::Mapped,
- descriptor.size,
- descriptor.usage.bits(),
- valid,
- );
- out.push(ObjectValue(buff.reflector().get_jsobject().get()));
- out.push(ObjectValue(js_array_buffer.get()));
- out
- }
-
fn validate_buffer_descriptor(
&self,
descriptor: &GPUBufferDescriptor,
@@ -223,6 +191,7 @@ impl GPUDeviceMethods for GPUDevice {
descriptor.size,
descriptor.usage,
valid,
+ RootedTraceableBox::new(Heap::default()),
)
}
@@ -245,11 +214,33 @@ impl GPUDeviceMethods for GPUDevice {
))
.expect("Failed to create WebGPU buffer");
- let (buffer, array_buffer) = receiver.recv().unwrap();
-
+ rooted!(in(*cx) let mut js_array_buffer = ptr::null_mut::<JSObject>());
unsafe {
- self.resolve_create_buffer_mapped(cx, buffer, array_buffer, wgpu_descriptor, valid)
+ assert!(ArrayBuffer::create(
+ *cx,
+ CreateWith::Length(descriptor.size as u32),
+ js_array_buffer.handle_mut(),
+ )
+ .is_ok());
}
+
+ let buffer = receiver.recv().unwrap();
+ let buff = GPUBuffer::new(
+ &self.global(),
+ self.channel.clone(),
+ buffer,
+ self.device,
+ GPUBufferState::MappedForWriting,
+ wgpu_descriptor.size,
+ wgpu_descriptor.usage.bits(),
+ valid,
+ RootedTraceableBox::from_box(Heap::boxed(js_array_buffer.get())),
+ );
+
+ vec![
+ ObjectValue(buff.reflector().get_jsobject().get()),
+ ObjectValue(js_array_buffer.get()),
+ ]
}
/// https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout
diff --git a/components/script/dom/webidls/GPUBuffer.webidl b/components/script/dom/webidls/GPUBuffer.webidl
index 0b327bdcbd2..1688060b514 100644
--- a/components/script/dom/webidls/GPUBuffer.webidl
+++ b/components/script/dom/webidls/GPUBuffer.webidl
@@ -5,7 +5,7 @@
// https://gpuweb.github.io/gpuweb/#gpubuffer
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUBuffer {
- // Promise<ArrayBuffer> mapReadAsync();
+ Promise<ArrayBuffer> mapReadAsync();
// Promise<ArrayBuffer> mapWriteAsync();
void unmap();
diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs
index ae26972e073..1fe2738807b 100644
--- a/components/webgpu/lib.rs
+++ b/components/webgpu/lib.rs
@@ -7,7 +7,7 @@ extern crate log;
#[macro_use]
pub extern crate wgpu_core as wgpu;
-use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
+use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use servo_config::pref;
use smallvec::SmallVec;
@@ -16,6 +16,7 @@ use smallvec::SmallVec;
pub enum WebGPUResponse {
RequestAdapter(String, WebGPUAdapter, WebGPU),
RequestDevice(WebGPUDevice, WebGPUQueue, wgpu::instance::DeviceDescriptor),
+ MapReadAsync(IpcSharedMemory),
}
pub type WebGPUResponseResult = Result<WebGPUResponse, String>;
@@ -41,7 +42,7 @@ pub enum WebGPURequest {
wgpu::resource::BufferDescriptor,
),
CreateBufferMapped(
- IpcSender<(WebGPUBuffer, Vec<u8>)>,
+ IpcSender<WebGPUBuffer>,
WebGPUDevice,
wgpu::id::BufferId,
wgpu::resource::BufferDescriptor,
@@ -79,7 +80,14 @@ pub enum WebGPURequest {
wgpu::id::ShaderModuleId,
Vec<u32>,
),
- UnmapBuffer(WebGPUBuffer),
+ MapReadAsync(
+ IpcSender<WebGPUResponseResult>,
+ wgpu::id::BufferId,
+ wgpu::id::DeviceId,
+ u32,
+ u64,
+ ),
+ UnmapBuffer(wgpu::id::DeviceId, WebGPUBuffer, Vec<u8>),
DestroyBuffer(WebGPUBuffer),
CreateCommandEncoder(
IpcSender<WebGPUCommandEncoder>,
@@ -251,27 +259,26 @@ impl WGPU {
},
WebGPURequest::CreateBufferMapped(sender, device, id, descriptor) => {
let global = &self.global;
- let buffer_size = descriptor.size as usize;
-
- let (buffer_id, arr_buff_ptr) = gfx_select!(id =>
+ let (buffer_id, _arr_buff_ptr) = gfx_select!(id =>
global.device_create_buffer_mapped(device.0, &descriptor, id));
let buffer = WebGPUBuffer(buffer_id);
- let mut array_buffer = Vec::with_capacity(buffer_size);
- unsafe {
- array_buffer.set_len(buffer_size);
- std::ptr::copy(arr_buff_ptr, array_buffer.as_mut_ptr(), buffer_size);
- };
- if let Err(e) = sender.send((buffer, array_buffer)) {
+ if let Err(e) = sender.send(buffer) {
warn!(
"Failed to send response to WebGPURequest::CreateBufferMapped ({})",
e
)
}
},
- WebGPURequest::UnmapBuffer(buffer) => {
+ WebGPURequest::UnmapBuffer(device_id, buffer, array_buffer) => {
let global = &self.global;
- gfx_select!(buffer.0 => global.buffer_unmap(buffer.0));
+
+ gfx_select!(buffer.0 => global.device_set_buffer_sub_data(
+ device_id,
+ buffer.0,
+ 0,
+ array_buffer.as_slice()
+ ));
},
WebGPURequest::DestroyBuffer(buffer) => {
let global = &self.global;
@@ -412,6 +419,43 @@ impl WGPU {
)
}
},
+ WebGPURequest::MapReadAsync(sender, buffer_id, device_id, usage, size) => {
+ let global = &self.global;
+ let on_read = move |status: wgpu::resource::BufferMapAsyncStatus,
+ ptr: *const u8| {
+ match status {
+ wgpu::resource::BufferMapAsyncStatus::Success => {
+ let array_buffer =
+ unsafe { std::slice::from_raw_parts(ptr, size as usize) };
+ if let Err(e) = sender.send(Ok(WebGPUResponse::MapReadAsync(
+ IpcSharedMemory::from_bytes(array_buffer),
+ ))) {
+ warn!(
+ "Failed to send response to WebGPURequest::MapReadAsync ({})",
+ e
+ )
+ }
+ },
+ _ => {
+ if let Err(e) = sender
+ .send(Err("MapReadAsync: Failed to map buffer".to_owned()))
+ {
+ warn!(
+ "Failed to send response to WebGPURequest::MapReadAsync ({})",
+ e
+ )
+ }
+ },
+ }
+ };
+ gfx_select!(buffer_id => global.buffer_map_async(
+ buffer_id,
+ wgpu::resource::BufferUsage::from_bits(usage).unwrap(),
+ 0..size,
+ wgpu::resource::BufferMapOperation::Read(Box::new(on_read))
+ ));
+ gfx_select!(device_id => global.device_poll(device_id, true));
+ },
WebGPURequest::Submit(queue_id, command_buffer_ids) => {
let global = &self.global;
let _ = gfx_select!(queue_id => global.queue_submit(