From 130de8b8e645867c5c834655d8b94defea599681 Mon Sep 17 00:00:00 2001 From: Kunal Mohan Date: Fri, 29 May 2020 11:15:31 +0530 Subject: [PATCH] Implement GPURenderPipeline Add webidl for GPURenderPipeline and implement GPUDevice.createRenderPipeline() --- Cargo.lock | 5 + components/script/Cargo.toml | 1 + components/script/dom/bindings/trace.rs | 3 +- components/script/dom/gpucolorwrite.rs | 11 + components/script/dom/gpudevice.rs | 276 ++++++++++++++++-- components/script/dom/gpupipelinelayout.rs | 4 + components/script/dom/gpurenderpipeline.rs | 67 +++++ components/script/dom/identityhub.rs | 12 +- components/script/dom/mod.rs | 2 + .../script/dom/webidls/GPUColorWrite.webidl | 15 + .../script/dom/webidls/GPUDevice.webidl | 2 +- .../dom/webidls/GPURenderPipeline.webidl | 188 ++++++++++++ components/script/script_thread.rs | 1 + components/webgpu/Cargo.toml | 1 + components/webgpu/lib.rs | 93 ++++++ 15 files changed, 659 insertions(+), 22 deletions(-) create mode 100644 components/script/dom/gpucolorwrite.rs create mode 100644 components/script/dom/gpurenderpipeline.rs create mode 100644 components/script/dom/webidls/GPUColorWrite.webidl create mode 100644 components/script/dom/webidls/GPURenderPipeline.webidl diff --git a/Cargo.lock b/Cargo.lock index ff98e6f4c91..1241d0c95a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,6 +152,9 @@ name = "arrayvec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +dependencies = [ + "serde", +] [[package]] name = "ascii" @@ -4589,6 +4592,7 @@ version = "0.0.1" dependencies = [ "accountable-refcell", "app_units", + "arrayvec 0.5.1", "backtrace", "base64 0.10.1", "bitflags", @@ -6457,6 +6461,7 @@ dependencies = [ name = "webgpu" version = "0.0.1" dependencies = [ + "arrayvec 0.5.1", "ipc-channel", "log", "malloc_size_of", diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 686143194be..8746273e9f1 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -32,6 +32,7 @@ serde_json = "1.0" [dependencies] accountable-refcell = { version = "0.2.0", optional = true } app_units = "0.7" +arrayvec = "0.5.1" backtrace = { version = "0.3", optional = true } base64 = "0.10.1" bitflags = "1.0" diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 29160e61186..907abb890ee 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -160,7 +160,7 @@ use uuid::Uuid; use webgpu::{ wgpu::command::RawPass, WebGPU, WebGPUAdapter, WebGPUBindGroup, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePipeline, WebGPUDevice, - WebGPUPipelineLayout, WebGPUQueue, WebGPUSampler, WebGPUShaderModule, + WebGPUPipelineLayout, WebGPUQueue, WebGPURenderPipeline, WebGPUSampler, WebGPUShaderModule, }; use webrender_api::{DocumentId, ImageKey}; use webxr_api::SwapChainId as WebXRSwapChainId; @@ -556,6 +556,7 @@ unsafe_no_jsmanaged_fields!(WebGPUBuffer); unsafe_no_jsmanaged_fields!(WebGPUBindGroup); unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout); unsafe_no_jsmanaged_fields!(WebGPUComputePipeline); +unsafe_no_jsmanaged_fields!(WebGPURenderPipeline); unsafe_no_jsmanaged_fields!(WebGPUPipelineLayout); unsafe_no_jsmanaged_fields!(WebGPUQueue); unsafe_no_jsmanaged_fields!(WebGPUShaderModule); diff --git a/components/script/dom/gpucolorwrite.rs b/components/script/dom/gpucolorwrite.rs new file mode 100644 index 00000000000..c34799d9982 --- /dev/null +++ b/components/script/dom/gpucolorwrite.rs @@ -0,0 +1,11 @@ +/* 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::reflector::Reflector; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct GPUColorWrite { + reflector_: Reflector, +} diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 9c5df68f7a2..9afbeb4483e 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -16,6 +16,11 @@ use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{ GPUCommandEncoderDescriptor, GPUDeviceMethods, }; use crate::dom::bindings::codegen::Bindings::GPUPipelineLayoutBinding::GPUPipelineLayoutDescriptor; +use crate::dom::bindings::codegen::Bindings::GPURenderPipelineBinding::{ + GPUBlendDescriptor, GPUBlendFactor, GPUBlendOperation, GPUCullMode, GPUFrontFace, + GPUIndexFormat, GPUInputStepMode, GPUPrimitiveTopology, GPURenderPipelineDescriptor, + GPUStencilOperation, GPUVertexFormat, +}; use crate::dom::bindings::codegen::Bindings::GPUSamplerBinding::{ GPUAddressMode, GPUCompareFunction, GPUFilterMode, GPUSamplerDescriptor, }; @@ -35,9 +40,11 @@ use crate::dom::gpucommandencoder::GPUCommandEncoder; use crate::dom::gpucomputepipeline::GPUComputePipeline; use crate::dom::gpupipelinelayout::GPUPipelineLayout; use crate::dom::gpuqueue::GPUQueue; +use crate::dom::gpurenderpipeline::GPURenderPipeline; use crate::dom::gpusampler::GPUSampler; use crate::dom::gpushadermodule::GPUShaderModule; use crate::script_runtime::JSContext as SafeJSContext; +use arrayvec::ArrayVec; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, ObjectValue}; @@ -47,7 +54,7 @@ use std::ptr::{self, NonNull}; use webgpu::wgpu::binding_model::{ BindGroupEntry, BindGroupLayoutEntry, BindingResource, BindingType, BufferBinding, }; -use webgpu::{self, wgt, WebGPU, WebGPURequest}; +use webgpu::{self, wgpu, wgt, WebGPU, WebGPURequest}; #[dom_struct] pub struct GPUDevice { @@ -605,6 +612,7 @@ impl GPUDeviceMethods for GPUDevice { let compute_pipeline = webgpu::WebGPUComputePipeline(compute_pipeline_id); GPUComputePipeline::new(&self.global(), compute_pipeline) } + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder fn CreateCommandEncoder( &self, @@ -627,6 +635,7 @@ impl GPUDeviceMethods for GPUDevice { GPUCommandEncoder::new(&self.global(), self.channel.clone(), encoder, true) } + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler fn CreateSampler(&self, descriptor: &GPUSamplerDescriptor) -> DomRoot { let sampler_id = self @@ -637,25 +646,16 @@ impl GPUDeviceMethods for GPUDevice { let compare_enable = descriptor.compare.is_some(); let desc = wgt::SamplerDescriptor { label: Default::default(), - address_mode_u: assign_address_mode(descriptor.addressModeU), - address_mode_v: assign_address_mode(descriptor.addressModeV), - address_mode_w: assign_address_mode(descriptor.addressModeW), - mag_filter: assign_filter_mode(descriptor.magFilter), - min_filter: assign_filter_mode(descriptor.minFilter), - mipmap_filter: assign_filter_mode(descriptor.mipmapFilter), + address_mode_u: convert_address_mode(descriptor.addressModeU), + address_mode_v: convert_address_mode(descriptor.addressModeV), + address_mode_w: convert_address_mode(descriptor.addressModeW), + mag_filter: convert_filter_mode(descriptor.magFilter), + min_filter: convert_filter_mode(descriptor.minFilter), + mipmap_filter: convert_filter_mode(descriptor.mipmapFilter), lod_min_clamp: *descriptor.lodMinClamp, lod_max_clamp: *descriptor.lodMaxClamp, compare: if let Some(c) = descriptor.compare { - match c { - GPUCompareFunction::Never => wgt::CompareFunction::Never, - GPUCompareFunction::Less => wgt::CompareFunction::Less, - GPUCompareFunction::Equal => wgt::CompareFunction::Equal, - GPUCompareFunction::Less_equal => wgt::CompareFunction::LessEqual, - GPUCompareFunction::Greater => wgt::CompareFunction::Greater, - GPUCompareFunction::Not_equal => wgt::CompareFunction::NotEqual, - GPUCompareFunction::Greater_equal => wgt::CompareFunction::GreaterEqual, - GPUCompareFunction::Always => wgt::CompareFunction::Always, - } + convert_compare_function(c) } else { wgt::CompareFunction::Undefined }, @@ -680,9 +680,154 @@ impl GPUDeviceMethods for GPUDevice { true, ) } + + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline + fn CreateRenderPipeline( + &self, + descriptor: &GPURenderPipelineDescriptor, + ) -> DomRoot { + let mut valid = descriptor.parent.layout.is_valid(); + valid &= descriptor.colorStates.len() <= wgpu::device::MAX_COLOR_TARGETS; + if descriptor.alphaToCoverageEnabled { + valid &= descriptor.sampleCount > 1; + } + + let vertex_module = descriptor.vertexStage.module.id().0; + let vertex_entry_point = descriptor.vertexStage.entryPoint.to_string(); + let (fragment_module, fragment_entry_point) = match descriptor.fragmentStage { + Some(ref frag) => (Some(frag.module.id().0), Some(frag.entryPoint.to_string())), + None => (None, None), + }; + + let primitive_topology = match descriptor.primitiveTopology { + GPUPrimitiveTopology::Point_list => wgt::PrimitiveTopology::PointList, + GPUPrimitiveTopology::Line_list => wgt::PrimitiveTopology::LineList, + GPUPrimitiveTopology::Line_strip => wgt::PrimitiveTopology::LineStrip, + GPUPrimitiveTopology::Triangle_list => wgt::PrimitiveTopology::TriangleList, + GPUPrimitiveTopology::Triangle_strip => wgt::PrimitiveTopology::TriangleStrip, + }; + + let ref rs_desc = descriptor.rasterizationState; + let rasterization_state = wgt::RasterizationStateDescriptor { + front_face: match rs_desc.frontFace { + GPUFrontFace::Ccw => wgt::FrontFace::Ccw, + GPUFrontFace::Cw => wgt::FrontFace::Cw, + }, + cull_mode: match rs_desc.cullMode { + GPUCullMode::None => wgt::CullMode::None, + GPUCullMode::Front => wgt::CullMode::Front, + GPUCullMode::Back => wgt::CullMode::Back, + }, + depth_bias: rs_desc.depthBias, + depth_bias_slope_scale: *rs_desc.depthBiasSlopeScale, + depth_bias_clamp: *rs_desc.depthBiasClamp, + }; + + let color_states = descriptor + .colorStates + .iter() + .map(|state| wgt::ColorStateDescriptor { + format: wgt::TextureFormat::Rgba8UnormSrgb, //TODO: Update this after implementing Texture + alpha_blend: convert_blend_descriptor(&state.alphaBlend), + color_blend: convert_blend_descriptor(&state.colorBlend), + write_mask: match wgt::ColorWrite::from_bits(state.writeMask) { + Some(mask) => mask, + None => { + valid = false; + wgt::ColorWrite::empty() + }, + }, + }) + .collect::>(); + + let depth_stencil_state = if let Some(ref dss_desc) = descriptor.depthStencilState { + Some(wgt::DepthStencilStateDescriptor { + format: wgt::TextureFormat::Rgba8UnormSrgb, //TODO: Update this + depth_write_enabled: dss_desc.depthWriteEnabled, + depth_compare: convert_compare_function(dss_desc.depthCompare), + stencil_front: wgt::StencilStateFaceDescriptor { + compare: convert_compare_function(dss_desc.stencilFront.compare), + fail_op: convert_stencil_op(dss_desc.stencilFront.failOp), + depth_fail_op: convert_stencil_op(dss_desc.stencilFront.depthFailOp), + pass_op: convert_stencil_op(dss_desc.stencilFront.passOp), + }, + stencil_back: wgt::StencilStateFaceDescriptor { + compare: convert_compare_function(dss_desc.stencilBack.compare), + fail_op: convert_stencil_op(dss_desc.stencilBack.failOp), + depth_fail_op: convert_stencil_op(dss_desc.stencilBack.depthFailOp), + pass_op: convert_stencil_op(dss_desc.stencilBack.passOp), + }, + stencil_read_mask: dss_desc.stencilReadMask, + stencil_write_mask: dss_desc.stencilWriteMask, + }) + } else { + None + }; + + let ref vs_desc = descriptor.vertexState; + let vertex_state = ( + match vs_desc.indexFormat { + GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16, + GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32, + }, + vs_desc + .vertexBuffers + .iter() + .map(|buffer| { + ( + buffer.arrayStride, + match buffer.stepMode { + GPUInputStepMode::Vertex => wgt::InputStepMode::Vertex, + GPUInputStepMode::Instance => wgt::InputStepMode::Instance, + }, + buffer + .attributes + .iter() + .map(|att| wgt::VertexAttributeDescriptor { + format: convert_vertex_format(att.format), + offset: att.offset, + shader_location: att.shaderLocation, + }) + .collect::>(), + ) + }) + .collect::>(), + ); + + let render_pipeline_id = self + .global() + .wgpu_id_hub() + .lock() + .create_render_pipeline_id(self.device.0.backend()); + + self.channel + .0 + .send(WebGPURequest::CreateRenderPipeline { + device_id: self.device.0, + render_pipeline_id, + pipeline_layout_id: descriptor.parent.layout.id().0, + vertex_module, + vertex_entry_point, + fragment_module, + fragment_entry_point, + primitive_topology, + rasterization_state, + color_states, + depth_stencil_state, + vertex_state, + sample_count: descriptor.sampleCount, + sample_mask: descriptor.sampleMask, + alpha_to_coverage_enabled: descriptor.alphaToCoverageEnabled, + }) + .expect("Failed to create WebGPU render pipeline"); + + let render_pipeline = webgpu::WebGPURenderPipeline(render_pipeline_id); + + GPURenderPipeline::new(&self.global(), render_pipeline, self.device, valid) + } } -fn assign_address_mode(address_mode: GPUAddressMode) -> wgt::AddressMode { +fn convert_address_mode(address_mode: GPUAddressMode) -> wgt::AddressMode { match address_mode { GPUAddressMode::Clamp_to_edge => wgt::AddressMode::ClampToEdge, GPUAddressMode::Repeat => wgt::AddressMode::Repeat, @@ -690,9 +835,102 @@ fn assign_address_mode(address_mode: GPUAddressMode) -> wgt::AddressMode { } } -fn assign_filter_mode(filter_mode: GPUFilterMode) -> wgt::FilterMode { +fn convert_filter_mode(filter_mode: GPUFilterMode) -> wgt::FilterMode { match filter_mode { GPUFilterMode::Nearest => wgt::FilterMode::Nearest, GPUFilterMode::Linear => wgt::FilterMode::Linear, } } + +fn convert_compare_function(compare: GPUCompareFunction) -> wgt::CompareFunction { + match compare { + GPUCompareFunction::Never => wgt::CompareFunction::Never, + GPUCompareFunction::Less => wgt::CompareFunction::Less, + GPUCompareFunction::Equal => wgt::CompareFunction::Equal, + GPUCompareFunction::Less_equal => wgt::CompareFunction::LessEqual, + GPUCompareFunction::Greater => wgt::CompareFunction::Greater, + GPUCompareFunction::Not_equal => wgt::CompareFunction::NotEqual, + GPUCompareFunction::Greater_equal => wgt::CompareFunction::GreaterEqual, + GPUCompareFunction::Always => wgt::CompareFunction::Always, + } +} + +fn convert_blend_descriptor(desc: &GPUBlendDescriptor) -> wgt::BlendDescriptor { + wgt::BlendDescriptor { + src_factor: convert_blend_factor(desc.srcFactor), + dst_factor: convert_blend_factor(desc.dstFactor), + operation: match desc.operation { + GPUBlendOperation::Add => wgt::BlendOperation::Add, + GPUBlendOperation::Subtract => wgt::BlendOperation::Subtract, + GPUBlendOperation::Reverse_subtract => wgt::BlendOperation::ReverseSubtract, + GPUBlendOperation::Min => wgt::BlendOperation::Min, + GPUBlendOperation::Max => wgt::BlendOperation::Max, + }, + } +} + +fn convert_blend_factor(factor: GPUBlendFactor) -> wgt::BlendFactor { + match factor { + GPUBlendFactor::Zero => wgt::BlendFactor::Zero, + GPUBlendFactor::One => wgt::BlendFactor::One, + GPUBlendFactor::Src_color => wgt::BlendFactor::SrcColor, + GPUBlendFactor::One_minus_src_color => wgt::BlendFactor::OneMinusSrcColor, + GPUBlendFactor::Src_alpha => wgt::BlendFactor::SrcAlpha, + GPUBlendFactor::One_minus_src_alpha => wgt::BlendFactor::OneMinusSrcAlpha, + GPUBlendFactor::Dst_color => wgt::BlendFactor::DstColor, + GPUBlendFactor::One_minus_dst_color => wgt::BlendFactor::OneMinusDstColor, + GPUBlendFactor::Dst_alpha => wgt::BlendFactor::DstAlpha, + GPUBlendFactor::One_minus_dst_alpha => wgt::BlendFactor::OneMinusDstAlpha, + GPUBlendFactor::Src_alpha_saturated => wgt::BlendFactor::SrcAlphaSaturated, + GPUBlendFactor::Blend_color => wgt::BlendFactor::BlendColor, + GPUBlendFactor::One_minus_blend_color => wgt::BlendFactor::OneMinusBlendColor, + } +} + +fn convert_stencil_op(operation: GPUStencilOperation) -> wgt::StencilOperation { + match operation { + GPUStencilOperation::Keep => wgt::StencilOperation::Keep, + GPUStencilOperation::Zero => wgt::StencilOperation::Zero, + GPUStencilOperation::Replace => wgt::StencilOperation::Replace, + GPUStencilOperation::Invert => wgt::StencilOperation::Invert, + GPUStencilOperation::Increment_clamp => wgt::StencilOperation::IncrementClamp, + GPUStencilOperation::Decrement_clamp => wgt::StencilOperation::DecrementClamp, + GPUStencilOperation::Increment_wrap => wgt::StencilOperation::IncrementWrap, + GPUStencilOperation::Decrement_wrap => wgt::StencilOperation::DecrementWrap, + } +} + +fn convert_vertex_format(format: GPUVertexFormat) -> wgt::VertexFormat { + match format { + GPUVertexFormat::Uchar2 => wgt::VertexFormat::Uchar2, + GPUVertexFormat::Uchar4 => wgt::VertexFormat::Uchar4, + GPUVertexFormat::Char2 => wgt::VertexFormat::Char2, + GPUVertexFormat::Char4 => wgt::VertexFormat::Char4, + GPUVertexFormat::Uchar2norm => wgt::VertexFormat::Uchar2Norm, + GPUVertexFormat::Uchar4norm => wgt::VertexFormat::Uchar4Norm, + GPUVertexFormat::Char2norm => wgt::VertexFormat::Char2Norm, + GPUVertexFormat::Char4norm => wgt::VertexFormat::Char4Norm, + GPUVertexFormat::Ushort2 => wgt::VertexFormat::Ushort2, + GPUVertexFormat::Ushort4 => wgt::VertexFormat::Ushort4, + GPUVertexFormat::Short2 => wgt::VertexFormat::Short2, + GPUVertexFormat::Short4 => wgt::VertexFormat::Short4, + GPUVertexFormat::Ushort2norm => wgt::VertexFormat::Ushort2Norm, + GPUVertexFormat::Ushort4norm => wgt::VertexFormat::Ushort4Norm, + GPUVertexFormat::Short2norm => wgt::VertexFormat::Short2Norm, + GPUVertexFormat::Short4norm => wgt::VertexFormat::Short4Norm, + GPUVertexFormat::Half2 => wgt::VertexFormat::Half2, + GPUVertexFormat::Half4 => wgt::VertexFormat::Half4, + GPUVertexFormat::Float => wgt::VertexFormat::Float, + GPUVertexFormat::Float2 => wgt::VertexFormat::Float2, + GPUVertexFormat::Float3 => wgt::VertexFormat::Float3, + GPUVertexFormat::Float4 => wgt::VertexFormat::Float4, + GPUVertexFormat::Uint => wgt::VertexFormat::Uint, + GPUVertexFormat::Uint2 => wgt::VertexFormat::Uint2, + GPUVertexFormat::Uint3 => wgt::VertexFormat::Uint3, + GPUVertexFormat::Uint4 => wgt::VertexFormat::Uint4, + GPUVertexFormat::Int => wgt::VertexFormat::Int, + GPUVertexFormat::Int2 => wgt::VertexFormat::Int2, + GPUVertexFormat::Int3 => wgt::VertexFormat::Int3, + GPUVertexFormat::Int4 => wgt::VertexFormat::Int4, + } +} diff --git a/components/script/dom/gpupipelinelayout.rs b/components/script/dom/gpupipelinelayout.rs index a3f816deb0d..d1c3d0d2bef 100644 --- a/components/script/dom/gpupipelinelayout.rs +++ b/components/script/dom/gpupipelinelayout.rs @@ -57,6 +57,10 @@ impl GPUPipelineLayout { pub fn id(&self) -> WebGPUPipelineLayout { self.pipeline_layout } + + pub fn is_valid(&self) -> bool { + self.valid.get() + } } impl GPUPipelineLayoutMethods for GPUPipelineLayout { diff --git a/components/script/dom/gpurenderpipeline.rs b/components/script/dom/gpurenderpipeline.rs new file mode 100644 index 00000000000..c7e56fb253a --- /dev/null +++ b/components/script/dom/gpurenderpipeline.rs @@ -0,0 +1,67 @@ +/* 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::GPURenderPipelineBinding::GPURenderPipelineMethods; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::reflector::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::{WebGPUDevice, WebGPURenderPipeline}; + +#[dom_struct] +pub struct GPURenderPipeline { + reflector_: Reflector, + label: DomRefCell>, + render_pipeline: WebGPURenderPipeline, + device: WebGPUDevice, + valid: Cell, +} + +impl GPURenderPipeline { + fn new_inherited( + render_pipeline: WebGPURenderPipeline, + device: WebGPUDevice, + valid: bool, + ) -> GPURenderPipeline { + Self { + reflector_: Reflector::new(), + label: DomRefCell::new(None), + render_pipeline, + valid: Cell::new(valid), + device, + } + } + + pub fn new( + global: &GlobalScope, + render_pipeline: WebGPURenderPipeline, + device: WebGPUDevice, + valid: bool, + ) -> DomRoot { + reflect_dom_object( + Box::new(GPURenderPipeline::new_inherited( + render_pipeline, + device, + valid, + )), + global, + ) + } +} + +impl GPURenderPipelineMethods for GPURenderPipeline { + /// 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 8ba21e36969..2e7a8cc8a96 100644 --- a/components/script/dom/identityhub.rs +++ b/components/script/dom/identityhub.rs @@ -7,7 +7,7 @@ use webgpu::wgpu::{ hub::IdentityManager, id::{ AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId, - DeviceId, PipelineLayoutId, SamplerId, ShaderModuleId, + DeviceId, PipelineLayoutId, RenderPipelineId, SamplerId, ShaderModuleId, }, }; use webgpu::wgt::Backend; @@ -24,6 +24,7 @@ pub struct IdentityHub { shader_modules: IdentityManager, command_encoders: IdentityManager, samplers: IdentityManager, + render_pipelines: IdentityManager, } impl IdentityHub { @@ -39,6 +40,7 @@ impl IdentityHub { shader_modules: IdentityManager::default(), command_encoders: IdentityManager::default(), samplers: IdentityManager::default(), + render_pipelines: IdentityManager::default(), } } } @@ -184,4 +186,12 @@ impl Identities { pub fn kill_sampler_id(&mut self, id: SamplerId) { self.select(id.backend()).samplers.free(id); } + + pub fn create_render_pipeline_id(&mut self, backend: Backend) -> RenderPipelineId { + self.select(backend).render_pipelines.alloc(backend) + } + + pub fn kill_render_pipeline_id(&mut self, id: RenderPipelineId) { + self.select(id.backend()).render_pipelines.free(id); + } } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index f76bdc498ad..9c259a3dea5 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -325,6 +325,7 @@ pub mod gpubindgroup; pub mod gpubindgrouplayout; pub mod gpubuffer; pub mod gpubufferusage; +pub mod gpucolorwrite; pub mod gpucommandbuffer; pub mod gpucommandencoder; pub mod gpucomputepassencoder; @@ -332,6 +333,7 @@ pub mod gpucomputepipeline; pub mod gpudevice; pub mod gpupipelinelayout; pub mod gpuqueue; +pub mod gpurenderpipeline; pub mod gpusampler; pub mod gpushadermodule; pub mod gpushaderstage; diff --git a/components/script/dom/webidls/GPUColorWrite.webidl b/components/script/dom/webidls/GPUColorWrite.webidl new file mode 100644 index 00000000000..e4a74fe5f6a --- /dev/null +++ b/components/script/dom/webidls/GPUColorWrite.webidl @@ -0,0 +1,15 @@ +/* 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/#gpucolorwrite +[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"] +interface GPUColorWrite { + const GPUColorWriteFlags RED = 0x1; + const GPUColorWriteFlags GREEN = 0x2; + const GPUColorWriteFlags BLUE = 0x4; + const GPUColorWriteFlags ALPHA = 0x8; + const GPUColorWriteFlags ALL = 0xF; +}; + +typedef [EnforceRange] unsigned long GPUColorWriteFlags; diff --git a/components/script/dom/webidls/GPUDevice.webidl b/components/script/dom/webidls/GPUDevice.webidl index 13f7ab37caa..3db27fdae7f 100644 --- a/components/script/dom/webidls/GPUDevice.webidl +++ b/components/script/dom/webidls/GPUDevice.webidl @@ -22,7 +22,7 @@ interface GPUDevice : EventTarget { GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor); GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor); - // GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor); + GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor); GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {}); // GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor); diff --git a/components/script/dom/webidls/GPURenderPipeline.webidl b/components/script/dom/webidls/GPURenderPipeline.webidl new file mode 100644 index 00000000000..768a4c70c4d --- /dev/null +++ b/components/script/dom/webidls/GPURenderPipeline.webidl @@ -0,0 +1,188 @@ +/* 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/#gpurenderpipeline +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPURenderPipeline { +}; +GPURenderPipeline includes GPUObjectBase; + +dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase { + required GPUProgrammableStageDescriptor vertexStage; + GPUProgrammableStageDescriptor fragmentStage; + + required GPUPrimitiveTopology primitiveTopology; + GPURasterizationStateDescriptor rasterizationState = {}; + required sequence colorStates; + GPUDepthStencilStateDescriptor depthStencilState; + GPUVertexStateDescriptor vertexState = {}; + + GPUSize32 sampleCount = 1; + GPUSampleMask sampleMask = 0xFFFFFFFF; + boolean alphaToCoverageEnabled = false; +}; + +typedef [EnforceRange] unsigned long GPUSampleMask; + +enum GPUPrimitiveTopology { + "point-list", + "line-list", + "line-strip", + "triangle-list", + "triangle-strip" +}; + +typedef [EnforceRange] long GPUDepthBias; + +dictionary GPURasterizationStateDescriptor { + GPUFrontFace frontFace = "ccw"; + GPUCullMode cullMode = "none"; + + GPUDepthBias depthBias = 0; + float depthBiasSlopeScale = 0; + float depthBiasClamp = 0; +}; + +enum GPUFrontFace { + "ccw", + "cw" +}; + +enum GPUCullMode { + "none", + "front", + "back" +}; + +dictionary GPUColorStateDescriptor { + //required GPUTextureFormat format; + + GPUBlendDescriptor alphaBlend = {}; + GPUBlendDescriptor colorBlend = {}; + GPUColorWriteFlags writeMask = 0xF; // GPUColorWrite.ALL +}; + +dictionary GPUBlendDescriptor { + GPUBlendFactor srcFactor = "one"; + GPUBlendFactor dstFactor = "zero"; + GPUBlendOperation operation = "add"; +}; + +enum GPUBlendFactor { + "zero", + "one", + "src-color", + "one-minus-src-color", + "src-alpha", + "one-minus-src-alpha", + "dst-color", + "one-minus-dst-color", + "dst-alpha", + "one-minus-dst-alpha", + "src-alpha-saturated", + "blend-color", + "one-minus-blend-color" +}; + +enum GPUBlendOperation { + "add", + "subtract", + "reverse-subtract", + "min", + "max" +}; + +enum GPUStencilOperation { + "keep", + "zero", + "replace", + "invert", + "increment-clamp", + "decrement-clamp", + "increment-wrap", + "decrement-wrap" +}; + +typedef [EnforceRange] unsigned long GPUStencilValue; + +dictionary GPUDepthStencilStateDescriptor { + //required GPUTextureFormat format; + + boolean depthWriteEnabled = false; + GPUCompareFunction depthCompare = "always"; + + GPUStencilStateFaceDescriptor stencilFront = {}; + GPUStencilStateFaceDescriptor stencilBack = {}; + + GPUStencilValue stencilReadMask = 0xFFFFFFFF; + GPUStencilValue stencilWriteMask = 0xFFFFFFFF; +}; + +dictionary GPUStencilStateFaceDescriptor { + GPUCompareFunction compare = "always"; + GPUStencilOperation failOp = "keep"; + GPUStencilOperation depthFailOp = "keep"; + GPUStencilOperation passOp = "keep"; +}; + +enum GPUIndexFormat { + "uint16", + "uint32" +}; + +enum GPUVertexFormat { + "uchar2", + "uchar4", + "char2", + "char4", + "uchar2norm", + "uchar4norm", + "char2norm", + "char4norm", + "ushort2", + "ushort4", + "short2", + "short4", + "ushort2norm", + "ushort4norm", + "short2norm", + "short4norm", + "half2", + "half4", + "float", + "float2", + "float3", + "float4", + "uint", + "uint2", + "uint3", + "uint4", + "int", + "int2", + "int3", + "int4" +}; + +enum GPUInputStepMode { + "vertex", + "instance" +}; + +dictionary GPUVertexStateDescriptor { + GPUIndexFormat indexFormat = "uint32"; + sequence vertexBuffers = []; +}; + +dictionary GPUVertexBufferLayoutDescriptor { + required GPUSize64 arrayStride; + GPUInputStepMode stepMode = "vertex"; + required sequence attributes; +}; + +dictionary GPUVertexAttributeDescriptor { + required GPUVertexFormat format; + required GPUSize64 offset; + + required GPUIndex32 shaderLocation; +}; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 639cca400eb..967b23e5de4 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1955,6 +1955,7 @@ impl ScriptThread { WebGPUMsg::FreeCommandBuffer(id) => self.gpu_id_hub.lock().kill_command_buffer_id(id), WebGPUMsg::FreeSampler(id) => self.gpu_id_hub.lock().kill_sampler_id(id), WebGPUMsg::FreeShaderModule(id) => self.gpu_id_hub.lock().kill_shader_module_id(id), + WebGPUMsg::FreeRenderPipeline(id) => self.gpu_id_hub.lock().kill_render_pipeline_id(id), WebGPUMsg::Exit => *self.webgpu_port.borrow_mut() = None, _ => {}, } diff --git a/components/webgpu/Cargo.toml b/components/webgpu/Cargo.toml index d241ce0f6c3..555dffbfabb 100644 --- a/components/webgpu/Cargo.toml +++ b/components/webgpu/Cargo.toml @@ -11,6 +11,7 @@ name = "webgpu" path = "lib.rs" [dependencies] +arrayvec = { version = "0.5.1", features = ["serde"] } ipc-channel = "0.14" log = "0.4" malloc_size_of = { path = "../malloc_size_of" } diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index f616fc8b87f..c385a20c2de 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -10,6 +10,7 @@ pub extern crate wgpu_types as wgt; pub mod identity; +use arrayvec::ArrayVec; use identity::{IdentityRecyclerFactory, WebGPUMsg}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; @@ -96,6 +97,26 @@ pub enum WebGPURequest { pipeline_layout_id: id::PipelineLayoutId, bind_group_layouts: Vec, }, + CreateRenderPipeline { + device_id: id::DeviceId, + render_pipeline_id: id::RenderPipelineId, + pipeline_layout_id: id::PipelineLayoutId, + vertex_module: id::ShaderModuleId, + vertex_entry_point: String, + fragment_module: Option, + fragment_entry_point: Option, + primitive_topology: wgt::PrimitiveTopology, + rasterization_state: wgt::RasterizationStateDescriptor, + color_states: ArrayVec<[wgt::ColorStateDescriptor; wgpu::device::MAX_COLOR_TARGETS]>, + depth_stencil_state: Option, + vertex_state: ( + wgt::IndexFormat, + Vec<(u64, wgt::InputStepMode, Vec)>, + ), + sample_count: u32, + sample_mask: u32, + alpha_to_coverage_enabled: bool, + }, CreateSampler { device_id: id::DeviceId, sampler_id: id::SamplerId, @@ -340,6 +361,77 @@ impl WGPU { let _ = gfx_select!(pipeline_layout_id => global.device_create_pipeline_layout(device_id, &descriptor, pipeline_layout_id)); }, + //TODO: consider https://github.com/gfx-rs/wgpu/issues/684 + WebGPURequest::CreateRenderPipeline { + device_id, + render_pipeline_id, + pipeline_layout_id, + vertex_module, + vertex_entry_point, + fragment_module, + fragment_entry_point, + primitive_topology, + rasterization_state, + color_states, + depth_stencil_state, + vertex_state, + sample_count, + sample_mask, + alpha_to_coverage_enabled, + } => { + let global = &self.global; + let vertex_ep = std::ffi::CString::new(vertex_entry_point).unwrap(); + let frag_stage = match fragment_module { + Some(frag) => { + let frag_ep = + std::ffi::CString::new(fragment_entry_point.unwrap()).unwrap(); + let frag_module = wgpu_core::pipeline::ProgrammableStageDescriptor { + module: frag, + entry_point: frag_ep.as_ptr(), + }; + Some(frag_module) + }, + None => None, + }; + let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { + layout: pipeline_layout_id, + vertex_stage: wgpu_core::pipeline::ProgrammableStageDescriptor { + module: vertex_module, + entry_point: vertex_ep.as_ptr(), + }, + fragment_stage: frag_stage + .as_ref() + .map_or(ptr::null(), |fs| fs as *const _), + primitive_topology, + rasterization_state: &rasterization_state as *const _, + color_states: color_states.as_ptr(), + color_states_length: color_states.len(), + depth_stencil_state: depth_stencil_state + .as_ref() + .map_or(ptr::null(), |dss| dss as *const _), + vertex_state: wgpu_core::pipeline::VertexStateDescriptor { + index_format: vertex_state.0, + vertex_buffers_length: vertex_state.1.len(), + vertex_buffers: vertex_state + .1 + .iter() + .map(|buffer| wgpu_core::pipeline::VertexBufferLayoutDescriptor { + array_stride: buffer.0, + step_mode: buffer.1, + attributes_length: buffer.2.len(), + attributes: buffer.2.as_ptr(), + }) + .collect::>() + .as_ptr(), + }, + sample_count, + sample_mask, + alpha_to_coverage_enabled, + }; + + let _ = gfx_select!(render_pipeline_id => + global.device_create_render_pipeline(device_id, &descriptor, render_pipeline_id)); + }, WebGPURequest::CreateSampler { device_id, sampler_id, @@ -510,5 +602,6 @@ webgpu_resource!(WebGPUComputePipeline, id::ComputePipelineId); webgpu_resource!(WebGPUDevice, id::DeviceId); webgpu_resource!(WebGPUPipelineLayout, id::PipelineLayoutId); webgpu_resource!(WebGPUQueue, id::QueueId); +webgpu_resource!(WebGPURenderPipeline, id::RenderPipelineId); webgpu_resource!(WebGPUSampler, id::SamplerId); webgpu_resource!(WebGPUShaderModule, id::ShaderModuleId);