From a3c6810b1850d5b8ce29af2f745058b94a39bb80 Mon Sep 17 00:00:00 2001 From: Zakor Date: Mon, 3 Feb 2020 17:25:01 +0100 Subject: [PATCH] Initial implementation of GPUQueue Added WebIDL bindings for `GPUQueue`. Implemented the `submit` function of `GPUQueue` and `defaultQueue` function of `GPUDevice`. --- components/script/dom/bindings/trace.rs | 3 +- components/script/dom/gpuadapter.rs | 3 +- components/script/dom/gpubuffer.rs | 7 +- components/script/dom/gpucommandbuffer.rs | 38 +++++++++- components/script/dom/gpucommandencoder.rs | 16 +++- components/script/dom/gpudevice.rs | 15 +++- components/script/dom/gpuqueue.rs | 73 +++++++++++++++++++ components/script/dom/mod.rs | 1 + .../script/dom/webidls/GPUDevice.webidl | 7 +- components/script/dom/webidls/GPUQueue.webidl | 18 +++++ components/webgpu/lib.rs | 16 +++- 11 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 components/script/dom/gpuqueue.rs create mode 100644 components/script/dom/webidls/GPUQueue.webidl diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index a1cc4843021..c481cf8b2bb 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -154,7 +154,7 @@ use uuid::Uuid; use webgpu::{ WebGPU, WebGPUAdapter, WebGPUBindGroup, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePipeline, WebGPUDevice, - WebGPUPipelineLayout, WebGPUShaderModule, + WebGPUPipelineLayout, WebGPUQueue, WebGPUShaderModule, }; use webrender_api::{DocumentId, ImageKey}; use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; @@ -537,6 +537,7 @@ unsafe_no_jsmanaged_fields!(WebGPUBindGroup); unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout); unsafe_no_jsmanaged_fields!(WebGPUComputePipeline); unsafe_no_jsmanaged_fields!(WebGPUPipelineLayout); +unsafe_no_jsmanaged_fields!(WebGPUQueue); unsafe_no_jsmanaged_fields!(WebGPUShaderModule); unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer); unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder); diff --git a/components/script/dom/gpuadapter.rs b/components/script/dom/gpuadapter.rs index f8ea0083153..abf8844522d 100644 --- a/components/script/dom/gpuadapter.rs +++ b/components/script/dom/gpuadapter.rs @@ -107,7 +107,7 @@ impl GPUAdapterMethods for GPUAdapter { impl AsyncWGPUListener for GPUAdapter { fn handle_response(&self, response: WebGPUResponse, promise: &Rc) { match response { - WebGPUResponse::RequestDevice(device_id, _descriptor) => { + WebGPUResponse::RequestDevice(device_id, queue_id, _descriptor) => { let device = GPUDevice::new( &self.global(), self.channel.clone(), @@ -115,6 +115,7 @@ impl AsyncWGPUListener for GPUAdapter { Heap::default(), Heap::default(), device_id, + queue_id, ); promise.resolve_native(&device); }, diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index 8cc9bc52249..e0694e4d726 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -2,7 +2,7 @@ * 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 crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::cell::{DomRefCell, Ref}; use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::{ self, GPUBufferMethods, GPUBufferSize, }; @@ -91,6 +91,10 @@ impl GPUBuffer { pub fn usage(&self) -> u32 { self.usage } + + pub fn state(&self) -> Ref { + self.state.borrow() + } } impl Drop for GPUBuffer { @@ -106,6 +110,7 @@ impl GPUBufferMethods for GPUBuffer { .0 .send(WebGPURequest::UnmapBuffer(self.buffer)) .unwrap(); + *self.state.borrow_mut() = GPUBufferState::Unmapped; } /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy diff --git a/components/script/dom/gpucommandbuffer.rs b/components/script/dom/gpucommandbuffer.rs index 5cc4c926547..24cd781c092 100644 --- a/components/script/dom/gpucommandbuffer.rs +++ b/components/script/dom/gpucommandbuffer.rs @@ -2,17 +2,28 @@ * 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 crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::cell::{DomRefCell, Ref}; use crate::dom::bindings::codegen::Bindings::GPUCommandBufferBinding::{ self, GPUCommandBufferMethods, }; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::Dom; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::gpubuffer::GPUBuffer; use dom_struct::dom_struct; +use std::collections::HashSet; +use std::hash::{Hash, Hasher}; use webgpu::{WebGPU, WebGPUCommandBuffer}; +impl Eq for DomRoot {} +impl Hash for DomRoot { + fn hash(&self, state: &mut H) { + self.id().hash(state); + } +} + #[dom_struct] pub struct GPUCommandBuffer { reflector_: Reflector, @@ -20,15 +31,21 @@ pub struct GPUCommandBuffer { channel: WebGPU, label: DomRefCell>, command_buffer: WebGPUCommandBuffer, + buffers: DomRefCell>>, } impl GPUCommandBuffer { - pub fn new_inherited(channel: WebGPU, command_buffer: WebGPUCommandBuffer) -> GPUCommandBuffer { + fn new_inherited( + channel: WebGPU, + command_buffer: WebGPUCommandBuffer, + buffers: HashSet>, + ) -> GPUCommandBuffer { GPUCommandBuffer { channel, reflector_: Reflector::new(), label: DomRefCell::new(None), command_buffer, + buffers: DomRefCell::new(buffers.into_iter().map(|b| Dom::from_ref(&*b)).collect()), } } @@ -36,15 +53,30 @@ impl GPUCommandBuffer { global: &GlobalScope, channel: WebGPU, command_buffer: WebGPUCommandBuffer, + buffers: HashSet>, ) -> DomRoot { reflect_dom_object( - Box::new(GPUCommandBuffer::new_inherited(channel, command_buffer)), + Box::new(GPUCommandBuffer::new_inherited( + channel, + command_buffer, + buffers, + )), global, GPUCommandBufferBinding::Wrap, ) } } +impl GPUCommandBuffer { + pub fn id(&self) -> WebGPUCommandBuffer { + self.command_buffer + } + + pub fn buffers(&self) -> Ref>> { + self.buffers.borrow() + } +} + impl GPUCommandBufferMethods for GPUCommandBuffer { /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label fn GetLabel(&self) -> Option { diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs index 5e86c0b8bfd..b0086ec0704 100644 --- a/components/script/dom/gpucommandencoder.rs +++ b/components/script/dom/gpucommandencoder.rs @@ -16,6 +16,7 @@ use crate::dom::gpucommandbuffer::GPUCommandBuffer; use crate::dom::gpucomputepassencoder::GPUComputePassEncoder; use dom_struct::dom_struct; use ipc_channel::ipc; +use std::collections::HashSet; use webgpu::{wgpu::command::RawPass, WebGPU, WebGPUCommandEncoder, WebGPURequest}; #[dom_struct] @@ -25,6 +26,7 @@ pub struct GPUCommandEncoder { channel: WebGPU, label: DomRefCell>, encoder: WebGPUCommandEncoder, + buffers: DomRefCell>>, } impl GPUCommandEncoder { @@ -34,6 +36,7 @@ impl GPUCommandEncoder { reflector_: Reflector::new(), label: DomRefCell::new(None), encoder, + buffers: DomRefCell::new(HashSet::new()), } } @@ -82,6 +85,10 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { destination_offset: u64, size: u64, ) { + self.buffers.borrow_mut().insert(DomRoot::from_ref(source)); + self.buffers + .borrow_mut() + .insert(DomRoot::from_ref(destination)); self.channel .0 .send(WebGPURequest::CopyBufferToBuffer( @@ -106,9 +113,14 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { // TODO(zakorgy): We should use `_descriptor` here after it's not empty // and the underlying wgpu-core struct is serializable )) - .expect("Failed to send CopyBufferToBuffer"); + .expect("Failed to send Finish"); let buffer = receiver.recv().unwrap(); - GPUCommandBuffer::new(&self.global(), self.channel.clone(), buffer) + GPUCommandBuffer::new( + &self.global(), + self.channel.clone(), + buffer, + self.buffers.borrow_mut().drain().collect(), + ) } } diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 514d6ad39d1..1d17961f87c 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -31,6 +31,7 @@ use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState}; use crate::dom::gpucommandencoder::GPUCommandEncoder; use crate::dom::gpucomputepipeline::GPUComputePipeline; use crate::dom::gpupipelinelayout::GPUPipelineLayout; +use crate::dom::gpuqueue::GPUQueue; use crate::dom::gpushadermodule::GPUShaderModule; use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; @@ -45,7 +46,7 @@ use webgpu::wgpu::binding_model::{ ShaderStage, }; use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage}; -use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; +use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPUQueue, WebGPURequest}; #[dom_struct] pub struct GPUDevice { @@ -59,6 +60,7 @@ pub struct GPUDevice { limits: Heap<*mut JSObject>, label: DomRefCell>, device: WebGPUDevice, + default_queue: Dom, } impl GPUDevice { @@ -68,6 +70,7 @@ impl GPUDevice { extensions: Heap<*mut JSObject>, limits: Heap<*mut JSObject>, device: WebGPUDevice, + queue: &GPUQueue, ) -> GPUDevice { Self { eventtarget: EventTarget::new_inherited(), @@ -77,6 +80,7 @@ impl GPUDevice { limits, label: DomRefCell::new(None), device, + default_queue: Dom::from_ref(queue), } } @@ -88,10 +92,12 @@ impl GPUDevice { extensions: Heap<*mut JSObject>, limits: Heap<*mut JSObject>, device: WebGPUDevice, + queue: WebGPUQueue, ) -> DomRoot { + let queue = GPUQueue::new(global, channel.clone(), queue); reflect_dom_object( Box::new(GPUDevice::new_inherited( - channel, adapter, extensions, limits, device, + channel, adapter, extensions, limits, device, &queue, )), global, GPUDeviceBinding::Wrap, @@ -176,6 +182,11 @@ impl GPUDeviceMethods for GPUDevice { NonNull::new(self.extensions.get()).unwrap() } + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-defaultqueue + fn DefaultQueue(&self) -> DomRoot { + DomRoot::from_ref(&self.default_queue) + } + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label fn GetLabel(&self) -> Option { self.label.borrow().clone() diff --git a/components/script/dom/gpuqueue.rs b/components/script/dom/gpuqueue.rs new file mode 100644 index 00000000000..dfa97402253 --- /dev/null +++ b/components/script/dom/gpuqueue.rs @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUQueueBinding::{self, GPUQueueMethods}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::dom::gpubuffer::GPUBufferState; +use crate::dom::gpucommandbuffer::GPUCommandBuffer; +use dom_struct::dom_struct; +use webgpu::{WebGPU, WebGPUQueue, WebGPURequest}; + +#[dom_struct] +pub struct GPUQueue { + reflector_: Reflector, + #[ignore_malloc_size_of = "defined in webgpu"] + channel: WebGPU, + label: DomRefCell>, + queue: WebGPUQueue, +} + +impl GPUQueue { + fn new_inherited(channel: WebGPU, queue: WebGPUQueue) -> GPUQueue { + GPUQueue { + channel, + reflector_: Reflector::new(), + label: DomRefCell::new(None), + queue, + } + } + + pub fn new(global: &GlobalScope, channel: WebGPU, queue: WebGPUQueue) -> DomRoot { + reflect_dom_object( + Box::new(GPUQueue::new_inherited(channel, queue)), + global, + GPUQueueBinding::Wrap, + ) + } +} + +impl GPUQueueMethods for GPUQueue { + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn GetLabel(&self) -> Option { + self.label.borrow().clone() + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn SetLabel(&self, value: Option) { + *self.label.borrow_mut() = value; + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuqueue-submit + fn Submit(&self, command_buffers: Vec>) { + let valid = command_buffers.iter().all(|cb| { + cb.buffers().iter().all(|b| match *b.state() { + GPUBufferState::Unmapped => true, + _ => false, + }) + }); + if !valid { + // TODO: Generate error to the ErrorScope + return; + } + let buffer_ids = command_buffers.iter().map(|cb| cb.id().0).collect(); + self.channel + .0 + .send(WebGPURequest::Submit(self.queue.0, buffer_ids)) + .unwrap(); + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 8636ede66ac..5c80db4a26a 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -328,6 +328,7 @@ pub mod gpucomputepassencoder; pub mod gpucomputepipeline; pub mod gpudevice; pub mod gpupipelinelayout; +pub mod gpuqueue; pub mod gpushadermodule; pub mod gpushaderstage; pub mod hashchangeevent; diff --git a/components/script/dom/webidls/GPUDevice.webidl b/components/script/dom/webidls/GPUDevice.webidl index 05fb449df8c..9c7195c9b18 100644 --- a/components/script/dom/webidls/GPUDevice.webidl +++ b/components/script/dom/webidls/GPUDevice.webidl @@ -5,13 +5,14 @@ // https://gpuweb.github.io/gpuweb/#gpudevice [Exposed=(Window, DedicatedWorker)/*, Serializable */, Pref="dom.webgpu.enabled"] interface GPUDevice : EventTarget { - readonly attribute GPUAdapter adapter; + /*[SameObject]*/ readonly attribute GPUAdapter adapter; readonly attribute object extensions; readonly attribute object limits; + [SameObject] readonly attribute GPUQueue defaultQueue; + GPUBuffer createBuffer(GPUBufferDescriptor descriptor); GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor); - // Promise createBufferMappedAsync(GPUBufferDescriptor descriptor); // GPUTexture createTexture(GPUTextureDescriptor descriptor); // GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {}); @@ -25,8 +26,6 @@ interface GPUDevice : EventTarget { GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {}); // GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor); - - // GPUQueue getQueue(); }; GPUDevice includes GPUObjectBase; diff --git a/components/script/dom/webidls/GPUQueue.webidl b/components/script/dom/webidls/GPUQueue.webidl new file mode 100644 index 00000000000..a1fdb9aa882 --- /dev/null +++ b/components/script/dom/webidls/GPUQueue.webidl @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// https://gpuweb.github.io/gpuweb/#gpuqueue +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUQueue { + void submit(sequence commandBuffers); + + // GPUFence createFence(optional GPUFenceDescriptor descriptor = {}); + // void signal(GPUFence fence, unsigned long long signalValue); + + // void copyImageBitmapToTexture( + // GPUImageBitmapCopyView source, + // GPUTextureCopyView destination, + // GPUExtent3D copySize); +}; +GPUQueue includes GPUObjectBase; diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index b807f922f16..d7259dba795 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -15,7 +15,7 @@ use smallvec::SmallVec; #[derive(Debug, Deserialize, Serialize)] pub enum WebGPUResponse { RequestAdapter(String, WebGPUAdapter, WebGPU), - RequestDevice(WebGPUDevice, wgpu::instance::DeviceDescriptor), + RequestDevice(WebGPUDevice, WebGPUQueue, wgpu::instance::DeviceDescriptor), } pub type WebGPUResponseResult = Result; @@ -102,6 +102,7 @@ pub enum WebGPURequest { // TODO(zakorgy): Serialize CommandBufferDescriptor in wgpu-core // wgpu::command::CommandBufferDescriptor, ), + Submit(wgpu::id::QueueId, Vec), } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -222,9 +223,12 @@ impl WGPU { id )); let device = WebGPUDevice(id); + // Note: (zakorgy) Note sure if sending the queue is needed at all, + // since wgpu-core uses the same id for the device and the queue + let queue = WebGPUQueue(id); self.devices.push(device); if let Err(e) = - sender.send(Ok(WebGPUResponse::RequestDevice(device, descriptor))) + sender.send(Ok(WebGPUResponse::RequestDevice(device, queue, descriptor))) { warn!( "Failed to send response to WebGPURequest::RequestDevice ({})", @@ -407,6 +411,13 @@ impl WGPU { ) } }, + WebGPURequest::Submit(queue_id, command_buffer_ids) => { + let global = &self.global; + let _ = gfx_select!(queue_id => global.queue_submit( + queue_id, + &command_buffer_ids + )); + }, WebGPURequest::Exit(sender) => { self.deinit(); if let Err(e) = sender.send(()) { @@ -444,3 +455,4 @@ webgpu_resource!(WebGPUPipelineLayout, wgpu::id::PipelineLayoutId); webgpu_resource!(WebGPUShaderModule, wgpu::id::ShaderModuleId); webgpu_resource!(WebGPUCommandEncoder, wgpu::id::CommandEncoderId); webgpu_resource!(WebGPUCommandBuffer, wgpu::id::CommandBufferId); +webgpu_resource!(WebGPUQueue, wgpu::id::QueueId);