From d33a4d29a0d8467531a3beb722dfdb3e15df12ae Mon Sep 17 00:00:00 2001 From: Istvan Miklos Date: Wed, 15 Jan 2020 12:16:32 +0100 Subject: [PATCH] Initial implementation of GPUPipelineLayout for WebGPU Added WebIDL bindings for `GPUPipelineLayout`. Implemented the createPipelineLayout function of `GPUDevice`. --- components/script/dom/bindings/trace.rs | 5 +- components/script/dom/gpubindgrouplayout.rs | 15 ++++ components/script/dom/gpudevice.rs | 67 ++++++++++++++++++ components/script/dom/gpupipelinelayout.rs | 69 +++++++++++++++++++ components/script/dom/identityhub.rs | 22 +++++- components/script/dom/mod.rs | 1 + components/script/dom/navigator.rs | 8 ++- .../script/dom/webidls/GPUDevice.webidl | 4 +- .../dom/webidls/GPUPipelineLayout.webidl | 13 ++++ components/webgpu/lib.rs | 25 ++++++- 10 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 components/script/dom/gpupipelinelayout.rs create mode 100644 components/script/dom/webidls/GPUPipelineLayout.webidl diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index bdcf99197524..ffd4a4952d03 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -151,7 +151,9 @@ use tendril::stream::LossyDecoder; use tendril::{StrTendril, TendrilSink}; use time::{Duration, Timespec, Tm}; use uuid::Uuid; -use webgpu::{WebGPU, WebGPUAdapter, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUDevice}; +use webgpu::{ + WebGPU, WebGPUAdapter, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUDevice, WebGPUPipelineLayout, +}; use webrender_api::{DocumentId, ImageKey}; use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; use webxr_api::SwapChainId as WebXRSwapChainId; @@ -531,6 +533,7 @@ unsafe_no_jsmanaged_fields!(WebGPUAdapter); unsafe_no_jsmanaged_fields!(WebGPUDevice); unsafe_no_jsmanaged_fields!(WebGPUBuffer); unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout); +unsafe_no_jsmanaged_fields!(WebGPUPipelineLayout); unsafe_no_jsmanaged_fields!(GPUBufferState); unsafe_no_jsmanaged_fields!(WebXRSwapChainId); unsafe_no_jsmanaged_fields!(MediaList); diff --git a/components/script/dom/gpubindgrouplayout.rs b/components/script/dom/gpubindgrouplayout.rs index 7669f0e5f2d4..7651d16b52b0 100644 --- a/components/script/dom/gpubindgrouplayout.rs +++ b/components/script/dom/gpubindgrouplayout.rs @@ -42,6 +42,7 @@ impl GPUBindGroupLayout { valid: Cell::new(valid), } } + pub fn new( global: &GlobalScope, channel: WebGPU, @@ -62,6 +63,20 @@ impl GPUBindGroupLayout { } } +impl GPUBindGroupLayout { + pub fn is_valid(&self) -> bool { + self.valid.get() + } + + pub fn id(&self) -> WebGPUBindGroupLayout { + self.bind_group_layout + } + + pub fn bindings(&self) -> &[GPUBindGroupLayoutBindings] { + &self.bindings + } +} + impl GPUBindGroupLayoutMethods for GPUBindGroupLayout { /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label fn GetLabel(&self) -> Option { diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 1cc00110146e..0ebefda60a06 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -11,6 +11,7 @@ use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{ }; use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferDescriptor; use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{self, GPUDeviceMethods}; +use crate::dom::bindings::codegen::Bindings::GPUPipelineLayoutBinding::GPUPipelineLayoutDescriptor; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; @@ -21,6 +22,7 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::gpuadapter::GPUAdapter; use crate::dom::gpubindgrouplayout::GPUBindGroupLayout; use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState}; +use crate::dom::gpupipelinelayout::GPUPipelineLayout; use crate::dom::window::Window; use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; @@ -410,4 +412,69 @@ impl GPUDeviceMethods for GPUDevice { GPUBindGroupLayout::new(&self.global(), self.channel.clone(), bgl, binds, valid) } + + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout + fn CreatePipelineLayout( + &self, + descriptor: &GPUPipelineLayoutDescriptor, + ) -> DomRoot { + // TODO: We should have these limits on device creation + let limits = GPULimits::empty(); + let mut bind_group_layouts = Vec::new(); + let mut bgl_ids = Vec::new(); + let mut max_dynamic_uniform_buffers_per_pipeline_layout = + limits.maxDynamicUniformBuffersPerPipelineLayout as i32; + let mut max_dynamic_storage_buffers_per_pipeline_layout = + limits.maxDynamicStorageBuffersPerPipelineLayout as i32; + descriptor.bindGroupLayouts.iter().for_each(|each| { + if each.is_valid() { + let id = each.id(); + bind_group_layouts.push(id); + bgl_ids.push(id.0); + } + each.bindings().iter().for_each(|bind| { + match bind.type_ { + GPUBindingType::Uniform_buffer => { + if bind.hasDynamicOffset { + max_dynamic_uniform_buffers_per_pipeline_layout -= 1; + }; + }, + GPUBindingType::Storage_buffer => { + if bind.hasDynamicOffset { + max_dynamic_storage_buffers_per_pipeline_layout -= 1; + }; + }, + GPUBindingType::Readonly_storage_buffer => { + if bind.hasDynamicOffset { + max_dynamic_storage_buffers_per_pipeline_layout -= 1; + }; + }, + _ => {}, + }; + }); + }); + + let valid = descriptor.bindGroupLayouts.len() <= limits.maxBindGroups as usize && + descriptor.bindGroupLayouts.len() == bind_group_layouts.len() && + max_dynamic_uniform_buffers_per_pipeline_layout >= 0 && + max_dynamic_storage_buffers_per_pipeline_layout >= 0; + + let (sender, receiver) = ipc::channel().unwrap(); + if let Some(window) = self.global().downcast::() { + let id = window + .Navigator() + .create_pipeline_layout_id(self.device.0.backend()); + self.channel + .0 + .send(WebGPURequest::CreatePipelineLayout( + sender, + self.device, + id, + bgl_ids, + )) + .expect("Failed to create WebGPU PipelineLayout"); + } + let pipeline_layout = receiver.recv().unwrap(); + GPUPipelineLayout::new(&self.global(), bind_group_layouts, pipeline_layout, valid) + } } diff --git a/components/script/dom/gpupipelinelayout.rs b/components/script/dom/gpupipelinelayout.rs new file mode 100644 index 000000000000..a52bbedb7b52 --- /dev/null +++ b/components/script/dom/gpupipelinelayout.rs @@ -0,0 +1,69 @@ +/* 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::GPUPipelineLayoutBinding::{ + self, GPUPipelineLayoutMethods, +}; +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 dom_struct::dom_struct; +use std::cell::Cell; +use webgpu::{WebGPUBindGroupLayout, WebGPUPipelineLayout}; + +#[dom_struct] +pub struct GPUPipelineLayout { + reflector_: Reflector, + bind_group_layouts: Vec, + label: DomRefCell>, + pipeline_layout: WebGPUPipelineLayout, + valid: Cell, +} + +impl GPUPipelineLayout { + fn new_inherited( + bind_group_layouts: Vec, + pipeline_layout: WebGPUPipelineLayout, + valid: bool, + ) -> GPUPipelineLayout { + Self { + reflector_: Reflector::new(), + bind_group_layouts, + label: DomRefCell::new(None), + pipeline_layout, + valid: Cell::new(valid), + } + } + + pub fn new( + global: &GlobalScope, + bind_group_layouts: Vec, + pipeline_layout: WebGPUPipelineLayout, + valid: bool, + ) -> DomRoot { + reflect_dom_object( + Box::new(GPUPipelineLayout::new_inherited( + bind_group_layouts, + pipeline_layout, + valid, + )), + global, + GPUPipelineLayoutBinding::Wrap, + ) + } +} + +impl GPUPipelineLayoutMethods for GPUPipelineLayout { + /// 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; + } +} diff --git a/components/script/dom/identityhub.rs b/components/script/dom/identityhub.rs index 91543d2ea3fd..91ead1acf0a5 100644 --- a/components/script/dom/identityhub.rs +++ b/components/script/dom/identityhub.rs @@ -5,7 +5,7 @@ use smallvec::SmallVec; use webgpu::wgpu::{ hub::IdentityManager, - id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId}, + id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId, PipelineLayoutId}, Backend, }; @@ -15,6 +15,7 @@ pub struct IdentityHub { devices: IdentityManager, buffers: IdentityManager, bind_group_layouts: IdentityManager, + pipeline_layouts: IdentityManager, backend: Backend, } @@ -25,6 +26,7 @@ impl IdentityHub { devices: IdentityManager::default(), buffers: IdentityManager::default(), bind_group_layouts: IdentityManager::default(), + pipeline_layouts: IdentityManager::default(), backend, } } @@ -44,6 +46,10 @@ impl IdentityHub { fn create_bind_group_layout_id(&mut self) -> BindGroupLayoutId { self.bind_group_layouts.alloc(self.backend) } + + fn create_pipeline_layout_id(&mut self) -> PipelineLayoutId { + self.pipeline_layouts.alloc(self.backend) + } } #[derive(Debug)] @@ -139,4 +145,18 @@ impl Identities { _ => self.dummy_hub.create_bind_group_layout_id(), } } + + pub fn create_pipeline_layout_id(&mut self, backend: Backend) -> PipelineLayoutId { + match backend { + #[cfg(any(target_os = "linux", target_os = "windows"))] + Backend::Vulkan => self.vk_hub.create_pipeline_layout_id(), + #[cfg(target_os = "windows")] + Backend::Dx12 => self.dx12_hub.create_pipeline_layout_id(), + #[cfg(target_os = "windows")] + Backend::Dx11 => self.dx11_hub.create_pipeline_layout_id(), + #[cfg(any(target_os = "ios", target_os = "macos"))] + Backend::Metal => self.metal_hub.create_pipeline_layout_id(), + _ => self.dummy_hub.create_pipeline_layout_id(), + } + } } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 08f1f0812fb4..a39143c845b7 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -322,6 +322,7 @@ pub mod gpubindgrouplayout; pub mod gpubuffer; pub mod gpubufferusage; pub mod gpudevice; +pub mod gpupipelinelayout; pub mod gpushaderstage; pub mod hashchangeevent; pub mod headers; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 3d04433bfcda..1c96907cb317 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -28,7 +28,7 @@ use smallvec::SmallVec; use std::cell::RefCell; use std::rc::Rc; use webgpu::wgpu::{ - id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId}, + id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId, PipelineLayoutId}, Backend, }; @@ -94,6 +94,12 @@ impl Navigator { .borrow_mut() .create_bind_group_layout_id(backend) } + + pub fn create_pipeline_layout_id(&self, backend: Backend) -> PipelineLayoutId { + self.gpu_id_hub + .borrow_mut() + .create_pipeline_layout_id(backend) + } } impl NavigatorMethods for Navigator { diff --git a/components/script/dom/webidls/GPUDevice.webidl b/components/script/dom/webidls/GPUDevice.webidl index 512f9bf649f0..85e8a3ea6330 100644 --- a/components/script/dom/webidls/GPUDevice.webidl +++ b/components/script/dom/webidls/GPUDevice.webidl @@ -16,8 +16,8 @@ interface GPUDevice : EventTarget { //GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {}); GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor); - /*GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor); - GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor); + GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor); + /*GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor); GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor); GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor); diff --git a/components/script/dom/webidls/GPUPipelineLayout.webidl b/components/script/dom/webidls/GPUPipelineLayout.webidl new file mode 100644 index 000000000000..a50c2c60f403 --- /dev/null +++ b/components/script/dom/webidls/GPUPipelineLayout.webidl @@ -0,0 +1,13 @@ +/* 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/#pipeline-layout +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUPipelineLayout { +}; +GPUPipelineLayout includes GPUObjectBase; + +dictionary GPUPipelineLayoutDescriptor : GPUObjectDescriptorBase { + required sequence bindGroupLayouts; +}; diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index 92d99f8a90ef..8d223a123661 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -54,6 +54,12 @@ pub enum WebGPURequest { wgpu::id::BindGroupLayoutId, Vec, ), + CreatePipelineLayout( + IpcSender, + WebGPUDevice, + wgpu::id::PipelineLayoutId, + Vec, + ), UnmapBuffer(WebGPUBuffer), DestroyBuffer(WebGPUBuffer), } @@ -237,7 +243,23 @@ impl WGPU { if let Err(e) = sender.send(bgl) { warn!( - "Failed to send response to WebGPURequest::CreateBufferMapped ({})", + "Failed to send response to WebGPURequest::CreateBindGroupLayout ({})", + e + ) + } + }, + WebGPURequest::CreatePipelineLayout(sender, device, id, bind_group_layouts) => { + let global = &self.global; + let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { + bind_group_layouts: bind_group_layouts.as_ptr(), + bind_group_layouts_length: bind_group_layouts.len(), + }; + let pl_id = gfx_select!(id => global.device_create_pipeline_layout(device.0, &descriptor, id)); + let pipeline_layout = WebGPUPipelineLayout(pl_id); + + if let Err(e) = sender.send(pipeline_layout) { + warn!( + "Failed to send response to WebGPURequest::CreatePipelineLayout ({})", e ) } @@ -273,3 +295,4 @@ webgpu_resource!(WebGPUAdapter, wgpu::id::AdapterId); webgpu_resource!(WebGPUDevice, wgpu::id::DeviceId); webgpu_resource!(WebGPUBuffer, wgpu::id::BufferId); webgpu_resource!(WebGPUBindGroupLayout, wgpu::id::BindGroupLayoutId); +webgpu_resource!(WebGPUPipelineLayout, wgpu::id::PipelineLayoutId);