aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webgpu
diff options
context:
space:
mode:
authoratbrakhi <atbrakhi@igalia.com>2024-11-28 15:24:15 +0100
committerGitHub <noreply@github.com>2024-11-28 14:24:15 +0000
commitd2d3407501b83d03db522b5dde5e159073fd9e4b (patch)
tree299be7d04ca2470f1c6c8741e46784a2c7e85dd7 /components/script/dom/webgpu
parenta37ccc3e64c92e8ba10a3cdc48ebd7f031bb7298 (diff)
downloadservo-d2d3407501b83d03db522b5dde5e159073fd9e4b.tar.gz
servo-d2d3407501b83d03db522b5dde5e159073fd9e4b.zip
Move script gpu files into webgpu folder (#34415)
Signed-off-by: atbrakhi <atbrakhi@igalia.com>
Diffstat (limited to 'components/script/dom/webgpu')
-rw-r--r--components/script/dom/webgpu/gpu.rs180
-rw-r--r--components/script/dom/webgpu/gpuadapter.rs254
-rw-r--r--components/script/dom/webgpu/gpuadapterinfo.rs56
-rw-r--r--components/script/dom/webgpu/gpubindgroup.rs142
-rw-r--r--components/script/dom/webgpu/gpubindgrouplayout.rs139
-rw-r--r--components/script/dom/webgpu/gpubuffer.rs425
-rw-r--r--components/script/dom/webgpu/gpubufferusage.rs12
-rw-r--r--components/script/dom/webgpu/gpucanvascontext.rs386
-rw-r--r--components/script/dom/webgpu/gpucolorwrite.rs12
-rw-r--r--components/script/dom/webgpu/gpucommandbuffer.rs88
-rw-r--r--components/script/dom/webgpu/gpucommandencoder.rs319
-rw-r--r--components/script/dom/webgpu/gpucompilationinfo.rs63
-rw-r--r--components/script/dom/webgpu/gpucompilationmessage.rs110
-rw-r--r--components/script/dom/webgpu/gpucomputepassencoder.rs156
-rw-r--r--components/script/dom/webgpu/gpucomputepipeline.rs154
-rw-r--r--components/script/dom/webgpu/gpuconvert.rs680
-rw-r--r--components/script/dom/webgpu/gpudevice.rs652
-rw-r--r--components/script/dom/webgpu/gpudevicelostinfo.rs55
-rw-r--r--components/script/dom/webgpu/gpuerror.rs100
-rw-r--r--components/script/dom/webgpu/gpuinternalerror.rs53
-rw-r--r--components/script/dom/webgpu/gpumapmode.rs12
-rw-r--r--components/script/dom/webgpu/gpuoutofmemoryerror.rs53
-rw-r--r--components/script/dom/webgpu/gpupipelineerror.rs74
-rw-r--r--components/script/dom/webgpu/gpupipelinelayout.rs142
-rw-r--r--components/script/dom/webgpu/gpuqueryset.rs35
-rw-r--r--components/script/dom/webgpu/gpuqueue.rs225
-rw-r--r--components/script/dom/webgpu/gpurenderbundle.rs94
-rw-r--r--components/script/dom/webgpu/gpurenderbundleencoder.rs279
-rw-r--r--components/script/dom/webgpu/gpurenderpassencoder.rs249
-rw-r--r--components/script/dom/webgpu/gpurenderpipeline.rs144
-rw-r--r--components/script/dom/webgpu/gpusampler.rs143
-rw-r--r--components/script/dom/webgpu/gpushadermodule.rs154
-rw-r--r--components/script/dom/webgpu/gpushaderstage.rs12
-rw-r--r--components/script/dom/webgpu/gpusupportedfeatures.rs177
-rw-r--r--components/script/dom/webgpu/gpusupportedlimits.rs316
-rw-r--r--components/script/dom/webgpu/gputexture.rs285
-rw-r--r--components/script/dom/webgpu/gputextureusage.rs12
-rw-r--r--components/script/dom/webgpu/gputextureview.rs94
-rw-r--r--components/script/dom/webgpu/gpuuncapturederrorevent.rs92
-rw-r--r--components/script/dom/webgpu/gpuvalidationerror.rs53
-rw-r--r--components/script/dom/webgpu/mod.rs44
41 files changed, 6725 insertions, 0 deletions
diff --git a/components/script/dom/webgpu/gpu.rs b/components/script/dom/webgpu/gpu.rs
new file mode 100644
index 00000000000..f955db7c350
--- /dev/null
+++ b/components/script/dom/webgpu/gpu.rs
@@ -0,0 +1,180 @@
+/* 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 std::rc::Rc;
+
+use dom_struct::dom_struct;
+use ipc_channel::ipc::{self, IpcSender};
+use ipc_channel::router::ROUTER;
+use js::jsapi::Heap;
+use script_traits::ScriptMsg;
+use webgpu::wgt::PowerPreference;
+use webgpu::{wgc, WebGPUResponse};
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUMethods, GPUPowerPreference, GPURequestAdapterOptions, GPUTextureFormat,
+};
+use crate::dom::bindings::error::Error;
+use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpuadapter::GPUAdapter;
+use crate::dom::promise::Promise;
+use crate::realms::InRealm;
+use crate::script_runtime::CanGc;
+use crate::task_source::{TaskSource, TaskSourceName};
+
+#[dom_struct]
+#[allow(clippy::upper_case_acronyms)]
+pub struct GPU {
+ reflector_: Reflector,
+}
+
+impl GPU {
+ pub fn new_inherited() -> GPU {
+ GPU {
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(global: &GlobalScope) -> DomRoot<GPU> {
+ reflect_dom_object(Box::new(GPU::new_inherited()), global)
+ }
+}
+
+pub trait AsyncWGPUListener {
+ fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>, can_gc: CanGc);
+}
+
+struct WGPUResponse<T: AsyncWGPUListener + DomObject> {
+ trusted: TrustedPromise,
+ receiver: Trusted<T>,
+}
+
+impl<T: AsyncWGPUListener + DomObject> WGPUResponse<T> {
+ #[allow(crown::unrooted_must_root)]
+ fn response(self, response: WebGPUResponse, can_gc: CanGc) {
+ let promise = self.trusted.root();
+ self.receiver
+ .root()
+ .handle_response(response, &promise, can_gc);
+ }
+}
+
+pub fn response_async<T: AsyncWGPUListener + DomObject + 'static>(
+ promise: &Rc<Promise>,
+ receiver: &T,
+) -> IpcSender<WebGPUResponse> {
+ let (action_sender, action_receiver) = ipc::channel().unwrap();
+ let task_source = receiver.global().dom_manipulation_task_source();
+ let canceller = receiver
+ .global()
+ .task_canceller(TaskSourceName::DOMManipulation);
+ let mut trusted: Option<TrustedPromise> = Some(TrustedPromise::new(promise.clone()));
+ let trusted_receiver = Trusted::new(receiver);
+ ROUTER.add_typed_route(
+ action_receiver,
+ Box::new(move |message| {
+ let trusted = if let Some(trusted) = trusted.take() {
+ trusted
+ } else {
+ error!("WebGPU callback called twice!");
+ return;
+ };
+
+ let context = WGPUResponse {
+ trusted,
+ receiver: trusted_receiver.clone(),
+ };
+ let result = task_source.queue_with_canceller(
+ task!(process_webgpu_task: move|| {
+ context.response(message.unwrap(), CanGc::note());
+ }),
+ &canceller,
+ );
+ if let Err(err) = result {
+ error!("Failed to queue GPU listener-task: {:?}", err);
+ }
+ }),
+ );
+ action_sender
+}
+
+impl GPUMethods<crate::DomTypeHolder> for GPU {
+ // https://gpuweb.github.io/gpuweb/#dom-gpu-requestadapter
+ fn RequestAdapter(
+ &self,
+ options: &GPURequestAdapterOptions,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> Rc<Promise> {
+ let global = &self.global();
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ let sender = response_async(&promise, self);
+ let power_preference = match options.powerPreference {
+ Some(GPUPowerPreference::Low_power) => PowerPreference::LowPower,
+ Some(GPUPowerPreference::High_performance) => PowerPreference::HighPerformance,
+ None => PowerPreference::default(),
+ };
+ let ids = global.wgpu_id_hub().create_adapter_id();
+
+ let script_to_constellation_chan = global.script_to_constellation_chan();
+ if script_to_constellation_chan
+ .send(ScriptMsg::RequestAdapter(
+ sender,
+ wgc::instance::RequestAdapterOptions {
+ power_preference,
+ compatible_surface: None,
+ force_fallback_adapter: options.forceFallbackAdapter,
+ },
+ ids,
+ ))
+ .is_err()
+ {
+ promise.reject_error(Error::Operation);
+ }
+ promise
+ }
+
+ // https://gpuweb.github.io/gpuweb/#dom-gpu-getpreferredcanvasformat
+ fn GetPreferredCanvasFormat(&self) -> GPUTextureFormat {
+ // TODO: real implementation
+ GPUTextureFormat::Rgba8unorm
+ }
+}
+
+impl AsyncWGPUListener for GPU {
+ fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>, can_gc: CanGc) {
+ match response {
+ WebGPUResponse::Adapter(Ok(adapter)) => {
+ let adapter = GPUAdapter::new(
+ &self.global(),
+ adapter.channel,
+ DOMString::from(format!(
+ "{} ({:?})",
+ adapter.adapter_info.name, adapter.adapter_id.0
+ )),
+ Heap::default(),
+ adapter.features,
+ adapter.limits,
+ adapter.adapter_info,
+ adapter.adapter_id,
+ can_gc,
+ );
+ promise.resolve_native(&adapter);
+ },
+ WebGPUResponse::Adapter(Err(e)) => {
+ warn!("Could not get GPUAdapter ({:?})", e);
+ promise.resolve_native(&None::<GPUAdapter>);
+ },
+ WebGPUResponse::None => {
+ warn!("Couldn't get a response, because WebGPU is disabled");
+ promise.resolve_native(&None::<GPUAdapter>);
+ },
+ _ => unreachable!("GPU received wrong WebGPUResponse"),
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpuadapter.rs b/components/script/dom/webgpu/gpuadapter.rs
new file mode 100644
index 00000000000..da81c8d7d4c
--- /dev/null
+++ b/components/script/dom/webgpu/gpuadapter.rs
@@ -0,0 +1,254 @@
+/* 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 std::rc::Rc;
+
+use dom_struct::dom_struct;
+use js::jsapi::{Heap, JSObject};
+use webgpu::wgc::instance::RequestDeviceError;
+use webgpu::wgt::MemoryHints;
+use webgpu::{wgt, WebGPU, WebGPUAdapter, WebGPURequest, WebGPUResponse};
+
+use super::gpusupportedfeatures::GPUSupportedFeatures;
+use super::gpusupportedlimits::set_limit;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUAdapterMethods, GPUDeviceDescriptor, GPUDeviceLostReason,
+};
+use crate::dom::bindings::error::Error;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpudevice::GPUDevice;
+use crate::dom::gpusupportedfeatures::gpu_to_wgt_feature;
+use crate::dom::promise::Promise;
+use crate::dom::types::{GPUAdapterInfo, GPUSupportedLimits};
+use crate::dom::webgpu::gpu::{response_async, AsyncWGPUListener};
+use crate::realms::InRealm;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUAdapter {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ name: DOMString,
+ #[ignore_malloc_size_of = "mozjs"]
+ extensions: Heap<*mut JSObject>,
+ features: Dom<GPUSupportedFeatures>,
+ limits: Dom<GPUSupportedLimits>,
+ info: Dom<GPUAdapterInfo>,
+ #[no_trace]
+ adapter: WebGPUAdapter,
+}
+
+impl GPUAdapter {
+ fn new_inherited(
+ channel: WebGPU,
+ name: DOMString,
+ extensions: Heap<*mut JSObject>,
+ features: &GPUSupportedFeatures,
+ limits: &GPUSupportedLimits,
+ info: &GPUAdapterInfo,
+ adapter: WebGPUAdapter,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ name,
+ extensions,
+ features: Dom::from_ref(features),
+ limits: Dom::from_ref(limits),
+ info: Dom::from_ref(info),
+ adapter,
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ name: DOMString,
+ extensions: Heap<*mut JSObject>,
+ features: wgt::Features,
+ limits: wgt::Limits,
+ info: wgt::AdapterInfo,
+ adapter: WebGPUAdapter,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
+ let limits = GPUSupportedLimits::new(global, limits);
+ let info = GPUAdapterInfo::new(global, info);
+ reflect_dom_object(
+ Box::new(GPUAdapter::new_inherited(
+ channel, name, extensions, &features, &limits, &info, adapter,
+ )),
+ global,
+ )
+ }
+}
+
+impl Drop for GPUAdapter {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropAdapter(self.adapter.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DropAdapter({:?}) ({})",
+ self.adapter.0, e
+ );
+ };
+ }
+}
+
+impl GPUAdapterMethods<crate::DomTypeHolder> for GPUAdapter {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-requestdevice>
+ fn RequestDevice(
+ &self,
+ descriptor: &GPUDeviceDescriptor,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> Rc<Promise> {
+ // Step 2
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ let sender = response_async(&promise, self);
+ let mut required_features = wgt::Features::empty();
+ for &ext in descriptor.requiredFeatures.iter() {
+ if let Some(feature) = gpu_to_wgt_feature(ext) {
+ required_features.insert(feature);
+ } else {
+ promise.reject_error(Error::Type(format!(
+ "{} is not supported feature",
+ ext.as_str()
+ )));
+ return promise;
+ }
+ }
+
+ let mut required_limits = wgt::Limits::default();
+ if let Some(limits) = &descriptor.requiredLimits {
+ for (limit, value) in (*limits).iter() {
+ if !set_limit(&mut required_limits, limit.as_ref(), *value) {
+ warn!("Unknown GPUDevice limit: {limit}");
+ promise.reject_error(Error::Operation);
+ return promise;
+ }
+ }
+ }
+
+ let desc = wgt::DeviceDescriptor {
+ required_features,
+ required_limits,
+ label: Some(descriptor.parent.label.to_string()),
+ memory_hints: MemoryHints::MemoryUsage,
+ };
+ let device_id = self.global().wgpu_id_hub().create_device_id();
+ let queue_id = self.global().wgpu_id_hub().create_queue_id();
+ let pipeline_id = self.global().pipeline_id();
+ if self
+ .channel
+ .0
+ .send(WebGPURequest::RequestDevice {
+ sender,
+ adapter_id: self.adapter,
+ descriptor: desc,
+ device_id,
+ queue_id,
+ pipeline_id,
+ })
+ .is_err()
+ {
+ promise.reject_error(Error::Operation);
+ }
+ // Step 5
+ promise
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-isfallbackadapter>
+ fn IsFallbackAdapter(&self) -> bool {
+ //TODO
+ false
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-requestadapterinfo>
+ fn RequestAdapterInfo(
+ &self,
+ unmask_hints: Vec<DOMString>,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> Rc<Promise> {
+ // XXX: Adapter info should be generated here ...
+ // Step 1
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ // Step 4
+ if !unmask_hints.is_empty() {
+ todo!("unmaskHints on RequestAdapterInfo");
+ }
+ promise.resolve_native(&*self.info);
+ // Step 5
+ promise
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-features>
+ fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
+ DomRoot::from_ref(&self.features)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-limits>
+ fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
+ DomRoot::from_ref(&self.limits)
+ }
+}
+
+impl AsyncWGPUListener for GPUAdapter {
+ fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>, can_gc: CanGc) {
+ match response {
+ WebGPUResponse::Device((device_id, queue_id, Ok(descriptor))) => {
+ let device = GPUDevice::new(
+ &self.global(),
+ self.channel.clone(),
+ self,
+ Heap::default(),
+ descriptor.required_features,
+ descriptor.required_limits,
+ device_id,
+ queue_id,
+ descriptor.label.unwrap_or_default(),
+ can_gc,
+ );
+ self.global().add_gpu_device(&device);
+ promise.resolve_native(&device);
+ },
+ WebGPUResponse::Device((_, _, Err(RequestDeviceError::UnsupportedFeature(f)))) => {
+ promise.reject_error(Error::Type(
+ RequestDeviceError::UnsupportedFeature(f).to_string(),
+ ))
+ },
+ WebGPUResponse::Device((_, _, Err(RequestDeviceError::LimitsExceeded(_)))) => {
+ promise.reject_error(Error::Operation)
+ },
+ WebGPUResponse::Device((device_id, queue_id, Err(e))) => {
+ let device = GPUDevice::new(
+ &self.global(),
+ self.channel.clone(),
+ self,
+ Heap::default(),
+ wgt::Features::default(),
+ wgt::Limits::default(),
+ device_id,
+ queue_id,
+ String::new(),
+ can_gc,
+ );
+ device.lose(GPUDeviceLostReason::Unknown, e.to_string());
+ promise.resolve_native(&device);
+ },
+ WebGPUResponse::None => unreachable!("Failed to get a response for RequestDevice"),
+ _ => unreachable!("GPUAdapter received wrong WebGPUResponse"),
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpuadapterinfo.rs b/components/script/dom/webgpu/gpuadapterinfo.rs
new file mode 100644
index 00000000000..0cda8bed2d7
--- /dev/null
+++ b/components/script/dom/webgpu/gpuadapterinfo.rs
@@ -0,0 +1,56 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::wgt::AdapterInfo;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUAdapterInfoMethods;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::globalscope::GlobalScope;
+use crate::test::DOMString;
+
+#[dom_struct]
+pub struct GPUAdapterInfo {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in wgpu-types"]
+ #[no_trace]
+ info: AdapterInfo,
+}
+
+impl GPUAdapterInfo {
+ fn new_inherited(info: AdapterInfo) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ info,
+ }
+ }
+
+ pub fn new(global: &GlobalScope, info: AdapterInfo) -> DomRoot<Self> {
+ reflect_dom_object(Box::new(Self::new_inherited(info)), global)
+ }
+}
+
+// TODO: wgpu does not expose right fields right now
+impl GPUAdapterInfoMethods<crate::DomTypeHolder> for GPUAdapterInfo {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapterinfo-vendor>
+ fn Vendor(&self) -> DOMString {
+ DOMString::new()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapterinfo-architecture>
+ fn Architecture(&self) -> DOMString {
+ DOMString::new()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapterinfo-device>
+ fn Device(&self) -> DOMString {
+ DOMString::new()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuadapterinfo-description>
+ fn Description(&self) -> DOMString {
+ DOMString::from_string(self.info.driver_info.clone())
+ }
+}
diff --git a/components/script/dom/webgpu/gpubindgroup.rs b/components/script/dom/webgpu/gpubindgroup.rs
new file mode 100644
index 00000000000..7992e3825d0
--- /dev/null
+++ b/components/script/dom/webgpu/gpubindgroup.rs
@@ -0,0 +1,142 @@
+/* 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 std::borrow::Cow;
+
+use dom_struct::dom_struct;
+use webgpu::wgc::binding_model::BindGroupDescriptor;
+use webgpu::{WebGPU, WebGPUBindGroup, WebGPUDevice, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUBindGroupDescriptor, GPUBindGroupMethods,
+};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
+use crate::dom::gpudevice::GPUDevice;
+
+#[dom_struct]
+pub struct GPUBindGroup {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ bind_group: WebGPUBindGroup,
+ #[no_trace]
+ device: WebGPUDevice,
+ layout: Dom<GPUBindGroupLayout>,
+}
+
+impl GPUBindGroup {
+ fn new_inherited(
+ channel: WebGPU,
+ bind_group: WebGPUBindGroup,
+ device: WebGPUDevice,
+ layout: &GPUBindGroupLayout,
+ label: USVString,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ label: DomRefCell::new(label),
+ bind_group,
+ device,
+ layout: Dom::from_ref(layout),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ bind_group: WebGPUBindGroup,
+ device: WebGPUDevice,
+ layout: &GPUBindGroupLayout,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUBindGroup::new_inherited(
+ channel, bind_group, device, layout, label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUBindGroup {
+ pub fn id(&self) -> &WebGPUBindGroup {
+ &self.bind_group
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPUBindGroupDescriptor,
+ ) -> DomRoot<GPUBindGroup> {
+ let entries = descriptor
+ .entries
+ .iter()
+ .map(|bind| bind.into())
+ .collect::<Vec<_>>();
+
+ let desc = BindGroupDescriptor {
+ label: (&descriptor.parent).into(),
+ layout: descriptor.layout.id().0,
+ entries: Cow::Owned(entries),
+ };
+
+ let bind_group_id = device.global().wgpu_id_hub().create_bind_group_id();
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateBindGroup {
+ device_id: device.id().0,
+ bind_group_id,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU BindGroup");
+
+ let bind_group = WebGPUBindGroup(bind_group_id);
+
+ GPUBindGroup::new(
+ &device.global(),
+ device.channel().clone(),
+ bind_group,
+ device.id(),
+ &descriptor.layout,
+ descriptor.parent.label.clone(),
+ )
+ }
+}
+
+impl Drop for GPUBindGroup {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropBindGroup(self.bind_group.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DropBindGroup({:?}) ({})",
+ self.bind_group.0, e
+ );
+ };
+ }
+}
+
+impl GPUBindGroupMethods<crate::DomTypeHolder> for GPUBindGroup {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+}
diff --git a/components/script/dom/webgpu/gpubindgrouplayout.rs b/components/script/dom/webgpu/gpubindgrouplayout.rs
new file mode 100644
index 00000000000..c09c36ade58
--- /dev/null
+++ b/components/script/dom/webgpu/gpubindgrouplayout.rs
@@ -0,0 +1,139 @@
+/* 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 std::borrow::Cow;
+
+use dom_struct::dom_struct;
+use webgpu::wgc::binding_model::BindGroupLayoutDescriptor;
+use webgpu::{WebGPU, WebGPUBindGroupLayout, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUBindGroupLayoutDescriptor, GPUBindGroupLayoutMethods,
+};
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpuconvert::convert_bind_group_layout_entry;
+use crate::dom::gpudevice::GPUDevice;
+
+#[dom_struct]
+pub struct GPUBindGroupLayout {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ bind_group_layout: WebGPUBindGroupLayout,
+}
+
+impl GPUBindGroupLayout {
+ fn new_inherited(
+ channel: WebGPU,
+ bind_group_layout: WebGPUBindGroupLayout,
+ label: USVString,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ label: DomRefCell::new(label),
+ bind_group_layout,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ bind_group_layout: WebGPUBindGroupLayout,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUBindGroupLayout::new_inherited(
+ channel,
+ bind_group_layout,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUBindGroupLayout {
+ pub fn id(&self) -> WebGPUBindGroupLayout {
+ self.bind_group_layout
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPUBindGroupLayoutDescriptor,
+ ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
+ let entries = descriptor
+ .entries
+ .iter()
+ .map(|bgle| convert_bind_group_layout_entry(bgle, device))
+ .collect::<Fallible<Result<Vec<_>, _>>>()?;
+
+ let desc = match entries {
+ Ok(entries) => Some(BindGroupLayoutDescriptor {
+ label: (&descriptor.parent).into(),
+ entries: Cow::Owned(entries),
+ }),
+ Err(error) => {
+ device.dispatch_error(error);
+ None
+ },
+ };
+
+ let bind_group_layout_id = device.global().wgpu_id_hub().create_bind_group_layout_id();
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateBindGroupLayout {
+ device_id: device.id().0,
+ bind_group_layout_id,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU BindGroupLayout");
+
+ let bgl = WebGPUBindGroupLayout(bind_group_layout_id);
+
+ Ok(GPUBindGroupLayout::new(
+ &device.global(),
+ device.channel().clone(),
+ bgl,
+ descriptor.parent.label.clone(),
+ ))
+ }
+}
+
+impl Drop for GPUBindGroupLayout {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropBindGroupLayout(self.bind_group_layout.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DropBindGroupLayout({:?}) ({})",
+ self.bind_group_layout.0, e
+ );
+ };
+ }
+}
+
+impl GPUBindGroupLayoutMethods<crate::DomTypeHolder> for GPUBindGroupLayout {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+}
diff --git a/components/script/dom/webgpu/gpubuffer.rs b/components/script/dom/webgpu/gpubuffer.rs
new file mode 100644
index 00000000000..cab38d067c2
--- /dev/null
+++ b/components/script/dom/webgpu/gpubuffer.rs
@@ -0,0 +1,425 @@
+/* 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 std::ops::Range;
+use std::rc::Rc;
+use std::string::String;
+
+use dom_struct::dom_struct;
+use ipc_channel::ipc::IpcSharedMemory;
+use js::typedarray::ArrayBuffer;
+use webgpu::wgc::device::HostMap;
+use webgpu::{wgt, Mapping, WebGPU, WebGPUBuffer, WebGPURequest, WebGPUResponse};
+
+use crate::dom::bindings::buffer_source::DataBlock;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUBufferDescriptor, GPUBufferMapState, GPUBufferMethods, GPUFlagsConstant,
+ GPUMapModeConstants, GPUMapModeFlags, GPUSize64,
+};
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpudevice::GPUDevice;
+use crate::dom::promise::Promise;
+use crate::dom::webgpu::gpu::{response_async, AsyncWGPUListener};
+use crate::realms::InRealm;
+use crate::script_runtime::{CanGc, JSContext};
+
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct ActiveBufferMapping {
+ // TODO(sagudev): Use IpcSharedMemory when https://github.com/servo/ipc-channel/pull/356 lands
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-data>
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-views>
+ pub data: DataBlock,
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-mode>
+ mode: GPUMapModeFlags,
+ /// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-range>
+ range: Range<u64>,
+}
+
+impl ActiveBufferMapping {
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-initialize-an-active-buffer-mapping>
+ pub fn new(mode: GPUMapModeFlags, range: Range<u64>) -> Fallible<Self> {
+ // Step 1
+ let size = range.end - range.start;
+ // Step 2
+ if size > (1 << 53) - 1 {
+ return Err(Error::Range("Over MAX_SAFE_INTEGER".to_string()));
+ }
+ let size: usize = size
+ .try_into()
+ .map_err(|_| Error::Range("Over usize".to_string()))?;
+ Ok(Self {
+ data: DataBlock::new_zeroed(size),
+ mode,
+ range,
+ })
+ }
+}
+
+#[dom_struct]
+pub struct GPUBuffer {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ buffer: WebGPUBuffer,
+ device: Dom<GPUDevice>,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-size>
+ size: GPUSize64,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-usage>
+ usage: GPUFlagsConstant,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-pending_map-slot>
+ #[ignore_malloc_size_of = "promises are hard"]
+ pending_map: DomRefCell<Option<Rc<Promise>>>,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapping-slot>
+ mapping: DomRefCell<Option<ActiveBufferMapping>>,
+}
+
+impl GPUBuffer {
+ fn new_inherited(
+ channel: WebGPU,
+ buffer: WebGPUBuffer,
+ device: &GPUDevice,
+ size: GPUSize64,
+ usage: GPUFlagsConstant,
+ mapping: Option<ActiveBufferMapping>,
+ label: USVString,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ label: DomRefCell::new(label),
+ device: Dom::from_ref(device),
+ buffer,
+ pending_map: DomRefCell::new(None),
+ size,
+ usage,
+ mapping: DomRefCell::new(mapping),
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ buffer: WebGPUBuffer,
+ device: &GPUDevice,
+ size: GPUSize64,
+ usage: GPUFlagsConstant,
+ mapping: Option<ActiveBufferMapping>,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUBuffer::new_inherited(
+ channel, buffer, device, size, usage, mapping, label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUBuffer {
+ pub fn id(&self) -> WebGPUBuffer {
+ self.buffer
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPUBufferDescriptor,
+ ) -> Fallible<DomRoot<GPUBuffer>> {
+ let desc = wgt::BufferDescriptor {
+ label: (&descriptor.parent).into(),
+ size: descriptor.size as wgt::BufferAddress,
+ usage: wgt::BufferUsages::from_bits_retain(descriptor.usage),
+ mapped_at_creation: descriptor.mappedAtCreation,
+ };
+ let id = device.global().wgpu_id_hub().create_buffer_id();
+
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateBuffer {
+ device_id: device.id().0,
+ buffer_id: id,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU buffer");
+
+ let buffer = WebGPUBuffer(id);
+ let mapping = if descriptor.mappedAtCreation {
+ Some(ActiveBufferMapping::new(
+ GPUMapModeConstants::WRITE,
+ 0..descriptor.size,
+ )?)
+ } else {
+ None
+ };
+
+ Ok(GPUBuffer::new(
+ &device.global(),
+ device.channel().clone(),
+ buffer,
+ device,
+ descriptor.size,
+ descriptor.usage,
+ mapping,
+ descriptor.parent.label.clone(),
+ ))
+ }
+}
+
+impl Drop for GPUBuffer {
+ fn drop(&mut self) {
+ self.Destroy()
+ }
+}
+
+impl GPUBufferMethods<crate::DomTypeHolder> for GPUBuffer {
+ #[allow(unsafe_code)]
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap>
+ fn Unmap(&self) {
+ // Step 1
+ if let Some(promise) = self.pending_map.borrow_mut().take() {
+ promise.reject_error(Error::Abort);
+ }
+ // Step 2
+ let mut mapping = self.mapping.borrow_mut().take();
+ let mapping = if let Some(mapping) = mapping.as_mut() {
+ mapping
+ } else {
+ return;
+ };
+
+ // Step 3
+ mapping.data.clear_views();
+ // Step 5&7
+ if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
+ buffer_id: self.id().0,
+ mapping: if mapping.mode >= GPUMapModeConstants::WRITE {
+ Some(Mapping {
+ data: IpcSharedMemory::from_bytes(mapping.data.data()),
+ range: mapping.range.clone(),
+ mode: HostMap::Write,
+ })
+ } else {
+ None
+ },
+ }) {
+ warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy>
+ fn Destroy(&self) {
+ // Step 1
+ self.Unmap();
+ // Step 2
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DestroyBuffer(self.buffer.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DestroyBuffer({:?}) ({})",
+ self.buffer.0, e
+ );
+ };
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapasync>
+ fn MapAsync(
+ &self,
+ mode: u32,
+ offset: GPUSize64,
+ size: Option<GPUSize64>,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> Rc<Promise> {
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ // Step 2
+ if self.pending_map.borrow().is_some() {
+ promise.reject_error(Error::Operation);
+ return promise;
+ }
+ // Step 4
+ *self.pending_map.borrow_mut() = Some(promise.clone());
+ // Step 5
+ let host_map = match mode {
+ GPUMapModeConstants::READ => HostMap::Read,
+ GPUMapModeConstants::WRITE => HostMap::Write,
+ _ => {
+ self.device
+ .dispatch_error(webgpu::Error::Validation(String::from(
+ "Invalid MapModeFlags",
+ )));
+ self.map_failure(&promise);
+ return promise;
+ },
+ };
+
+ let sender = response_async(&promise, self);
+ if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync {
+ sender,
+ buffer_id: self.buffer.0,
+ device_id: self.device.id().0,
+ host_map,
+ offset,
+ size,
+ }) {
+ warn!(
+ "Failed to send BufferMapAsync ({:?}) ({})",
+ self.buffer.0, e
+ );
+ self.map_failure(&promise);
+ return promise;
+ }
+ // Step 6
+ promise
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-getmappedrange>
+ #[allow(unsafe_code)]
+ fn GetMappedRange(
+ &self,
+ _cx: JSContext,
+ offset: GPUSize64,
+ size: Option<GPUSize64>,
+ ) -> Fallible<ArrayBuffer> {
+ let range_size = if let Some(s) = size {
+ s
+ } else {
+ self.size.saturating_sub(offset)
+ };
+ // Step 2: validation
+ let mut mapping = self.mapping.borrow_mut();
+ let mapping = mapping.as_mut().ok_or(Error::Operation)?;
+
+ let valid = offset % wgt::MAP_ALIGNMENT == 0 &&
+ range_size % wgt::COPY_BUFFER_ALIGNMENT == 0 &&
+ offset >= mapping.range.start &&
+ offset + range_size <= mapping.range.end;
+ if !valid {
+ return Err(Error::Operation);
+ }
+
+ // Step 4
+ // only mapping.range is mapped with mapping.range.start at 0
+ // so we need to rebase range to mapped.range
+ let rebased_offset = (offset - mapping.range.start) as usize;
+ mapping
+ .data
+ .view(rebased_offset..rebased_offset + range_size as usize)
+ .map(|view| view.array_buffer())
+ .map_err(|()| Error::Operation)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-size>
+ fn Size(&self) -> GPUSize64 {
+ self.size
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-usage>
+ fn Usage(&self) -> GPUFlagsConstant {
+ self.usage
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapstate>
+ fn MapState(&self) -> GPUBufferMapState {
+ // Step 1&2&3
+ if self.mapping.borrow().is_some() {
+ GPUBufferMapState::Mapped
+ } else if self.pending_map.borrow().is_some() {
+ GPUBufferMapState::Pending
+ } else {
+ GPUBufferMapState::Unmapped
+ }
+ }
+}
+
+impl GPUBuffer {
+ fn map_failure(&self, p: &Rc<Promise>) {
+ let mut pending_map = self.pending_map.borrow_mut();
+ // Step 1
+ if pending_map.as_ref() != Some(p) {
+ assert!(p.is_rejected());
+ return;
+ }
+ // Step 2
+ assert!(p.is_pending());
+ // Step 3
+ pending_map.take();
+ // Step 4
+ if self.device.is_lost() {
+ p.reject_error(Error::Abort);
+ } else {
+ p.reject_error(Error::Operation);
+ }
+ }
+
+ fn map_success(&self, p: &Rc<Promise>, wgpu_mapping: Mapping) {
+ let mut pending_map = self.pending_map.borrow_mut();
+
+ // Step 1
+ if pending_map.as_ref() != Some(p) {
+ assert!(p.is_rejected());
+ return;
+ }
+
+ // Step 2
+ assert!(p.is_pending());
+
+ // Step 4
+ let mapping = ActiveBufferMapping::new(
+ match wgpu_mapping.mode {
+ HostMap::Read => GPUMapModeConstants::READ,
+ HostMap::Write => GPUMapModeConstants::WRITE,
+ },
+ wgpu_mapping.range,
+ );
+
+ match mapping {
+ Err(error) => {
+ *pending_map = None;
+ p.reject_error(error.clone());
+ },
+ Ok(mut mapping) => {
+ // Step 5
+ mapping.data.load(&wgpu_mapping.data);
+ // Step 6
+ self.mapping.borrow_mut().replace(mapping);
+ // Step 7
+ pending_map.take();
+ p.resolve_native(&());
+ },
+ }
+ }
+}
+
+impl AsyncWGPUListener for GPUBuffer {
+ #[allow(unsafe_code)]
+ fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>, _can_gc: CanGc) {
+ match response {
+ WebGPUResponse::BufferMapAsync(Ok(mapping)) => self.map_success(promise, mapping),
+ WebGPUResponse::BufferMapAsync(Err(_)) => self.map_failure(promise),
+ _ => unreachable!("Wrong response received on AsyncWGPUListener for GPUBuffer"),
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpubufferusage.rs b/components/script/dom/webgpu/gpubufferusage.rs
new file mode 100644
index 00000000000..b35768d25d5
--- /dev/null
+++ b/components/script/dom/webgpu/gpubufferusage.rs
@@ -0,0 +1,12 @@
+/* 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 dom_struct::dom_struct;
+
+use crate::dom::bindings::reflector::Reflector;
+
+#[dom_struct]
+pub struct GPUBufferUsage {
+ reflector_: Reflector,
+}
diff --git a/components/script/dom/webgpu/gpucanvascontext.rs b/components/script/dom/webgpu/gpucanvascontext.rs
new file mode 100644
index 00000000000..2766f318c8a
--- /dev/null
+++ b/components/script/dom/webgpu/gpucanvascontext.rs
@@ -0,0 +1,386 @@
+/* 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 std::borrow::Cow;
+use std::cell::RefCell;
+
+use arrayvec::ArrayVec;
+use dom_struct::dom_struct;
+use euclid::default::Size2D;
+use ipc_channel::ipc;
+use script_layout_interface::HTMLCanvasDataSource;
+use webgpu::swapchain::WebGPUContextId;
+use webgpu::wgc::id;
+use webgpu::{
+ ContextConfiguration, WebGPU, WebGPURequest, WebGPUTexture, PRESENTATION_BUFFER_COUNT,
+};
+use webrender_api::units::DeviceIntSize;
+use webrender_api::ImageKey;
+
+use super::gpuconvert::convert_texture_descriptor;
+use super::gputexture::GPUTexture;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUTexture_Binding::GPUTextureMethods;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUCanvasAlphaMode, GPUCanvasConfiguration, GPUCanvasContextMethods, GPUDeviceMethods,
+ GPUExtent3D, GPUExtent3DDict, GPUObjectDescriptorBase, GPUTextureDescriptor,
+ GPUTextureDimension, GPUTextureFormat, GPUTextureUsageConstants,
+};
+use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas;
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::htmlcanvaselement::{HTMLCanvasElement, LayoutCanvasRenderingContextHelpers};
+use crate::dom::node::{document_from_node, Node, NodeDamage};
+
+impl HTMLCanvasElementOrOffscreenCanvas {
+ fn size(&self) -> Size2D<u64> {
+ match self {
+ HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
+ canvas.get_size().cast()
+ },
+ HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
+ }
+ }
+}
+
+/// <https://gpuweb.github.io/gpuweb/#supported-context-formats>
+fn supported_context_format(format: GPUTextureFormat) -> bool {
+ // TODO: GPUTextureFormat::Rgba16float
+ matches!(
+ format,
+ GPUTextureFormat::Bgra8unorm | GPUTextureFormat::Rgba8unorm
+ )
+}
+
+#[derive(Clone, Debug, Default, JSTraceable, MallocSizeOf)]
+/// Helps observe changes on swapchain
+struct DrawingBuffer {
+ #[no_trace]
+ size: DeviceIntSize,
+ /// image is transparent black
+ cleared: bool,
+ #[ignore_malloc_size_of = "Defined in wgpu"]
+ #[no_trace]
+ config: Option<ContextConfiguration>,
+}
+
+#[dom_struct]
+pub struct GPUCanvasContext {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-canvas>
+ canvas: HTMLCanvasElementOrOffscreenCanvas,
+ // TODO: can we have wgpu surface that is hw accelerated inside wr ...
+ #[ignore_malloc_size_of = "Defined in webrender"]
+ #[no_trace]
+ webrender_image: ImageKey,
+ #[no_trace]
+ context_id: WebGPUContextId,
+ #[ignore_malloc_size_of = "manual writing is hard"]
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configuration-slot>
+ configuration: RefCell<Option<GPUCanvasConfiguration>>,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-texturedescriptor-slot>
+ texture_descriptor: RefCell<Option<GPUTextureDescriptor>>,
+ /// Conceptually <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-drawingbuffer-slot>
+ drawing_buffer: RefCell<DrawingBuffer>,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-currenttexture-slot>
+ current_texture: MutNullableDom<GPUTexture>,
+}
+
+impl GPUCanvasContext {
+ fn new_inherited(
+ global: &GlobalScope,
+ canvas: HTMLCanvasElementOrOffscreenCanvas,
+ channel: WebGPU,
+ ) -> Self {
+ let (sender, receiver) = ipc::channel().unwrap();
+ let size = canvas.size().cast().cast_unit();
+ let mut buffer_ids = ArrayVec::<id::BufferId, PRESENTATION_BUFFER_COUNT>::new();
+ for _ in 0..PRESENTATION_BUFFER_COUNT {
+ buffer_ids.push(global.wgpu_id_hub().create_buffer_id());
+ }
+ if let Err(e) = channel.0.send(WebGPURequest::CreateContext {
+ buffer_ids,
+ size,
+ sender,
+ }) {
+ warn!("Failed to send CreateContext ({:?})", e);
+ }
+ let (external_id, webrender_image) = receiver.recv().unwrap();
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ canvas,
+ webrender_image,
+ context_id: WebGPUContextId(external_id.0),
+ drawing_buffer: RefCell::new(DrawingBuffer {
+ size,
+ cleared: true,
+ ..Default::default()
+ }),
+ configuration: RefCell::new(None),
+ texture_descriptor: RefCell::new(None),
+ current_texture: MutNullableDom::default(),
+ }
+ }
+
+ pub fn new(global: &GlobalScope, canvas: &HTMLCanvasElement, channel: WebGPU) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUCanvasContext::new_inherited(
+ global,
+ HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)),
+ channel,
+ )),
+ global,
+ )
+ }
+}
+
+// Abstract ops from spec
+impl GPUCanvasContext {
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-gputexturedescriptor-for-the-canvas-and-configuration>
+ fn texture_descriptor_for_canvas(
+ &self,
+ configuration: &GPUCanvasConfiguration,
+ ) -> GPUTextureDescriptor {
+ let size = self.size();
+ GPUTextureDescriptor {
+ format: configuration.format,
+ // We need to add `COPY_SRC` so we can copy texture to presentation buffer
+ // causes FAIL on webgpu:web_platform,canvas,configure:usage:*
+ usage: configuration.usage | GPUTextureUsageConstants::COPY_SRC,
+ size: GPUExtent3D::GPUExtent3DDict(GPUExtent3DDict {
+ width: size.width as u32,
+ height: size.height as u32,
+ depthOrArrayLayers: 1,
+ }),
+ viewFormats: configuration.viewFormats.clone(),
+ // other members to default
+ mipLevelCount: 1,
+ sampleCount: 1,
+ parent: GPUObjectDescriptorBase {
+ label: USVString::default(),
+ },
+ dimension: GPUTextureDimension::_2d,
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-expire-the-current-texture>
+ fn expire_current_texture(&self) {
+ if let Some(current_texture) = self.current_texture.take() {
+ // Make copy of texture content
+ self.send_swap_chain_present(current_texture.id());
+ // Step 1
+ current_texture.Destroy()
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-replace-the-drawing-buffer>
+ fn replace_drawing_buffer(&self) {
+ // Step 1
+ self.expire_current_texture();
+ // Step 2
+ let configuration = self.configuration.borrow();
+ // Step 3
+ let mut drawing_buffer = self.drawing_buffer.borrow_mut();
+ drawing_buffer.size = self.size().cast().cast_unit();
+ drawing_buffer.cleared = true;
+ if let Some(configuration) = configuration.as_ref() {
+ drawing_buffer.config = Some(ContextConfiguration {
+ device_id: configuration.device.id().0,
+ queue_id: configuration.device.queue_id().0,
+ format: configuration.format.into(),
+ is_opaque: matches!(configuration.alphaMode, GPUCanvasAlphaMode::Opaque),
+ });
+ } else {
+ drawing_buffer.config.take();
+ };
+ // TODO: send less
+ self.channel
+ .0
+ .send(WebGPURequest::UpdateContext {
+ context_id: self.context_id,
+ size: drawing_buffer.size,
+ configuration: drawing_buffer.config,
+ })
+ .expect("Failed to update webgpu context");
+ }
+}
+
+// Internal helper methods
+impl GPUCanvasContext {
+ fn layout_handle(&self) -> HTMLCanvasDataSource {
+ if self.drawing_buffer.borrow().cleared {
+ HTMLCanvasDataSource::Empty
+ } else {
+ HTMLCanvasDataSource::WebGPU(self.webrender_image)
+ }
+ }
+
+ fn send_swap_chain_present(&self, texture_id: WebGPUTexture) {
+ self.drawing_buffer.borrow_mut().cleared = false;
+ let encoder_id = self.global().wgpu_id_hub().create_command_encoder_id();
+ if let Err(e) = self.channel.0.send(WebGPURequest::SwapChainPresent {
+ context_id: self.context_id,
+ texture_id: texture_id.0,
+ encoder_id,
+ }) {
+ warn!(
+ "Failed to send UpdateWebrenderData({:?}) ({})",
+ self.context_id, e
+ );
+ }
+ }
+
+ fn size(&self) -> Size2D<u64> {
+ self.canvas.size()
+ }
+}
+
+// public methods for canvas handling
+// these methods should probably be behind trait for all canvases
+impl GPUCanvasContext {
+ pub(crate) fn context_id(&self) -> WebGPUContextId {
+ self.context_id
+ }
+
+ pub(crate) fn mark_as_dirty(&self) {
+ if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) = &self.canvas {
+ canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ let document = document_from_node(&**canvas);
+ document.add_dirty_webgpu_canvas(self);
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-updating-the-rendering-of-a-webgpu-canvas>
+ pub(crate) fn update_rendering_of_webgpu_canvas(&self) {
+ // Step 1
+ self.expire_current_texture();
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-update-the-canvas-size>
+ pub(crate) fn resize(&self) {
+ // Step 1
+ self.replace_drawing_buffer();
+ // Step 2
+ let configuration = self.configuration.borrow();
+ // Step 3
+ if let Some(configuration) = configuration.as_ref() {
+ self.texture_descriptor
+ .replace(Some(self.texture_descriptor_for_canvas(configuration)));
+ }
+ }
+}
+
+impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, GPUCanvasContext> {
+ fn canvas_data_source(self) -> HTMLCanvasDataSource {
+ (*self.unsafe_get()).layout_handle()
+ }
+}
+
+impl GPUCanvasContextMethods<crate::DomTypeHolder> for GPUCanvasContext {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-canvas>
+ fn Canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
+ self.canvas.clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configure>
+ fn Configure(&self, configuration: &GPUCanvasConfiguration) -> Fallible<()> {
+ // Step 1: Let device be configuration.device
+ let device = &configuration.device;
+
+ // Step 5: Let descriptor be the GPUTextureDescriptor for the canvas and configuration.
+ let descriptor = self.texture_descriptor_for_canvas(configuration);
+
+ // Step 2&3: Validate texture format required features
+ let (mut desc, _) = convert_texture_descriptor(&descriptor, device)?;
+ desc.label = Some(Cow::Borrowed(
+ "dummy texture for texture descriptor validation",
+ ));
+
+ // Step 4: If Supported context formats does not contain configuration.format, throw a TypeError
+ if !supported_context_format(configuration.format) {
+ return Err(Error::Type(format!(
+ "Unsupported context format: {:?}",
+ configuration.format
+ )));
+ }
+
+ // Step 5
+ self.configuration.replace(Some(configuration.clone()));
+
+ // Step 6
+ self.texture_descriptor.replace(Some(descriptor));
+
+ // Step 7
+ self.replace_drawing_buffer();
+
+ // Step 8: Validate texture descriptor
+ let texture_id = self.global().wgpu_id_hub().create_texture_id();
+ self.channel
+ .0
+ .send(WebGPURequest::ValidateTextureDescriptor {
+ device_id: device.id().0,
+ texture_id,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU SwapChain");
+
+ Ok(())
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-unconfigure>
+ fn Unconfigure(&self) {
+ // Step 1
+ self.configuration.take();
+ // Step 2
+ self.current_texture.take();
+ // Step 3
+ self.replace_drawing_buffer();
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-getcurrenttexture>
+ fn GetCurrentTexture(&self) -> Fallible<DomRoot<GPUTexture>> {
+ // Step 1
+ let configuration = self.configuration.borrow();
+ let Some(configuration) = configuration.as_ref() else {
+ return Err(Error::InvalidState);
+ };
+ // Step 2
+ let texture_descriptor = self.texture_descriptor.borrow();
+ let texture_descriptor = texture_descriptor.as_ref().unwrap();
+ // Step 6
+ let current_texture = if let Some(current_texture) = self.current_texture.get() {
+ current_texture
+ } else {
+ // Step 3&4
+ self.replace_drawing_buffer();
+ let current_texture = configuration.device.CreateTexture(texture_descriptor)?;
+ self.current_texture.set(Some(&current_texture));
+ current_texture
+ };
+ // Step 5
+ self.mark_as_dirty();
+ // Step 6
+ Ok(current_texture)
+ }
+}
+
+impl Drop for GPUCanvasContext {
+ fn drop(&mut self) {
+ if let Err(e) = self.channel.0.send(WebGPURequest::DestroyContext {
+ context_id: self.context_id,
+ }) {
+ warn!(
+ "Failed to send DestroySwapChain-ImageKey({:?}) ({})",
+ self.webrender_image, e
+ );
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpucolorwrite.rs b/components/script/dom/webgpu/gpucolorwrite.rs
new file mode 100644
index 00000000000..29e19826342
--- /dev/null
+++ b/components/script/dom/webgpu/gpucolorwrite.rs
@@ -0,0 +1,12 @@
+/* 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 dom_struct::dom_struct;
+
+use crate::dom::bindings::reflector::Reflector;
+
+#[dom_struct]
+pub struct GPUColorWrite {
+ reflector_: Reflector,
+}
diff --git a/components/script/dom/webgpu/gpucommandbuffer.rs b/components/script/dom/webgpu/gpucommandbuffer.rs
new file mode 100644
index 00000000000..120af69a480
--- /dev/null
+++ b/components/script/dom/webgpu/gpucommandbuffer.rs
@@ -0,0 +1,88 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::{WebGPU, WebGPUCommandBuffer, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUCommandBufferMethods;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+
+#[dom_struct]
+pub struct GPUCommandBuffer {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ command_buffer: WebGPUCommandBuffer,
+}
+
+impl GPUCommandBuffer {
+ fn new_inherited(
+ channel: WebGPU,
+ command_buffer: WebGPUCommandBuffer,
+ label: USVString,
+ ) -> Self {
+ Self {
+ channel,
+ reflector_: Reflector::new(),
+ label: DomRefCell::new(label),
+ command_buffer,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ command_buffer: WebGPUCommandBuffer,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUCommandBuffer::new_inherited(
+ channel,
+ command_buffer,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl Drop for GPUCommandBuffer {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropCommandBuffer(self.command_buffer.0))
+ {
+ warn!(
+ "Failed to send DropCommandBuffer({:?}) ({})",
+ self.command_buffer.0, e
+ );
+ }
+ }
+}
+
+impl GPUCommandBuffer {
+ pub fn id(&self) -> WebGPUCommandBuffer {
+ self.command_buffer
+ }
+}
+
+impl GPUCommandBufferMethods<crate::DomTypeHolder> for GPUCommandBuffer {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+}
diff --git a/components/script/dom/webgpu/gpucommandencoder.rs b/components/script/dom/webgpu/gpucommandencoder.rs
new file mode 100644
index 00000000000..67d78c0fcc9
--- /dev/null
+++ b/components/script/dom/webgpu/gpucommandencoder.rs
@@ -0,0 +1,319 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::wgc::command as wgpu_com;
+use webgpu::{
+ wgt, WebGPU, WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePass, WebGPUDevice,
+ WebGPURenderPass, WebGPURequest,
+};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUCommandBufferDescriptor, GPUCommandEncoderDescriptor, GPUCommandEncoderMethods,
+ GPUComputePassDescriptor, GPUExtent3D, GPUImageCopyBuffer, GPUImageCopyTexture,
+ GPURenderPassDescriptor, GPUSize64,
+};
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubuffer::GPUBuffer;
+use crate::dom::gpucommandbuffer::GPUCommandBuffer;
+use crate::dom::gpucomputepassencoder::GPUComputePassEncoder;
+use crate::dom::gpuconvert::{convert_load_op, convert_store_op};
+use crate::dom::gpudevice::GPUDevice;
+use crate::dom::gpurenderpassencoder::GPURenderPassEncoder;
+
+#[dom_struct]
+pub struct GPUCommandEncoder {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ encoder: WebGPUCommandEncoder,
+ device: Dom<GPUDevice>,
+}
+
+impl GPUCommandEncoder {
+ pub fn new_inherited(
+ channel: WebGPU,
+ device: &GPUDevice,
+ encoder: WebGPUCommandEncoder,
+ label: USVString,
+ ) -> Self {
+ Self {
+ channel,
+ reflector_: Reflector::new(),
+ label: DomRefCell::new(label),
+ device: Dom::from_ref(device),
+ encoder,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ device: &GPUDevice,
+ encoder: WebGPUCommandEncoder,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUCommandEncoder::new_inherited(
+ channel, device, encoder, label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUCommandEncoder {
+ pub fn id(&self) -> WebGPUCommandEncoder {
+ self.encoder
+ }
+
+ pub fn device_id(&self) -> WebGPUDevice {
+ self.device.id()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPUCommandEncoderDescriptor,
+ ) -> DomRoot<GPUCommandEncoder> {
+ let command_encoder_id = device.global().wgpu_id_hub().create_command_encoder_id();
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateCommandEncoder {
+ device_id: device.id().0,
+ command_encoder_id,
+ desc: wgt::CommandEncoderDescriptor {
+ label: (&descriptor.parent).into(),
+ },
+ })
+ .expect("Failed to create WebGPU command encoder");
+
+ let encoder = WebGPUCommandEncoder(command_encoder_id);
+
+ GPUCommandEncoder::new(
+ &device.global(),
+ device.channel().clone(),
+ device,
+ encoder,
+ descriptor.parent.label.clone(),
+ )
+ }
+}
+
+impl GPUCommandEncoderMethods<crate::DomTypeHolder> for GPUCommandEncoder {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-begincomputepass>
+ fn BeginComputePass(
+ &self,
+ descriptor: &GPUComputePassDescriptor,
+ ) -> DomRoot<GPUComputePassEncoder> {
+ let compute_pass_id = self.global().wgpu_id_hub().create_compute_pass_id();
+
+ if let Err(e) = self.channel.0.send(WebGPURequest::BeginComputePass {
+ command_encoder_id: self.id().0,
+ compute_pass_id,
+ label: (&descriptor.parent).into(),
+ device_id: self.device.id().0,
+ }) {
+ warn!("Failed to send WebGPURequest::BeginComputePass {e:?}");
+ }
+
+ GPUComputePassEncoder::new(
+ &self.global(),
+ self.channel.clone(),
+ self,
+ WebGPUComputePass(compute_pass_id),
+ descriptor.parent.label.clone(),
+ )
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-beginrenderpass>
+ fn BeginRenderPass(
+ &self,
+ descriptor: &GPURenderPassDescriptor,
+ ) -> Fallible<DomRoot<GPURenderPassEncoder>> {
+ let depth_stencil_attachment = descriptor.depthStencilAttachment.as_ref().map(|depth| {
+ wgpu_com::RenderPassDepthStencilAttachment {
+ depth: wgpu_com::PassChannel {
+ load_op: convert_load_op(depth.depthLoadOp),
+ store_op: convert_store_op(depth.depthStoreOp),
+ clear_value: *depth.depthClearValue.unwrap_or_default(),
+ read_only: depth.depthReadOnly,
+ },
+ stencil: wgpu_com::PassChannel {
+ load_op: convert_load_op(depth.stencilLoadOp),
+ store_op: convert_store_op(depth.stencilStoreOp),
+ clear_value: depth.stencilClearValue,
+ read_only: depth.stencilReadOnly,
+ },
+ view: depth.view.id().0,
+ }
+ });
+
+ let color_attachments = descriptor
+ .colorAttachments
+ .iter()
+ .map(|color| -> Fallible<_> {
+ let channel = wgpu_com::PassChannel {
+ load_op: convert_load_op(Some(color.loadOp)),
+ store_op: convert_store_op(Some(color.storeOp)),
+ clear_value: color
+ .clearValue
+ .as_ref()
+ .map(|color| (color).try_into())
+ .transpose()?
+ .unwrap_or_default(),
+ read_only: false,
+ };
+ Ok(Some(wgpu_com::RenderPassColorAttachment {
+ resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0),
+ channel,
+ view: color.view.id().0,
+ }))
+ })
+ .collect::<Fallible<Vec<_>>>()?;
+ let render_pass_id = self.global().wgpu_id_hub().create_render_pass_id();
+
+ if let Err(e) = self.channel.0.send(WebGPURequest::BeginRenderPass {
+ command_encoder_id: self.id().0,
+ render_pass_id,
+ label: (&descriptor.parent).into(),
+ depth_stencil_attachment,
+ color_attachments,
+ device_id: self.device.id().0,
+ }) {
+ warn!("Failed to send WebGPURequest::BeginRenderPass {e:?}");
+ }
+
+ Ok(GPURenderPassEncoder::new(
+ &self.global(),
+ self.channel.clone(),
+ WebGPURenderPass(render_pass_id),
+ self,
+ descriptor.parent.label.clone(),
+ ))
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertobuffer>
+ fn CopyBufferToBuffer(
+ &self,
+ source: &GPUBuffer,
+ source_offset: GPUSize64,
+ destination: &GPUBuffer,
+ destination_offset: GPUSize64,
+ size: GPUSize64,
+ ) {
+ self.channel
+ .0
+ .send(WebGPURequest::CopyBufferToBuffer {
+ command_encoder_id: self.encoder.0,
+ source_id: source.id().0,
+ source_offset,
+ destination_id: destination.id().0,
+ destination_offset,
+ size,
+ })
+ .expect("Failed to send CopyBufferToBuffer");
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertotexture>
+ fn CopyBufferToTexture(
+ &self,
+ source: &GPUImageCopyBuffer,
+ destination: &GPUImageCopyTexture,
+ copy_size: GPUExtent3D,
+ ) -> Fallible<()> {
+ self.channel
+ .0
+ .send(WebGPURequest::CopyBufferToTexture {
+ command_encoder_id: self.encoder.0,
+ source: source.into(),
+ destination: destination.try_into()?,
+ copy_size: (&copy_size).try_into()?,
+ })
+ .expect("Failed to send CopyBufferToTexture");
+
+ Ok(())
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertotexture>
+ fn CopyTextureToBuffer(
+ &self,
+ source: &GPUImageCopyTexture,
+ destination: &GPUImageCopyBuffer,
+ copy_size: GPUExtent3D,
+ ) -> Fallible<()> {
+ self.channel
+ .0
+ .send(WebGPURequest::CopyTextureToBuffer {
+ command_encoder_id: self.encoder.0,
+ source: source.try_into()?,
+ destination: destination.into(),
+ copy_size: (&copy_size).try_into()?,
+ })
+ .expect("Failed to send CopyTextureToBuffer");
+
+ Ok(())
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#GPUCommandEncoder-copyTextureToTexture>
+ fn CopyTextureToTexture(
+ &self,
+ source: &GPUImageCopyTexture,
+ destination: &GPUImageCopyTexture,
+ copy_size: GPUExtent3D,
+ ) -> Fallible<()> {
+ self.channel
+ .0
+ .send(WebGPURequest::CopyTextureToTexture {
+ command_encoder_id: self.encoder.0,
+ source: source.try_into()?,
+ destination: destination.try_into()?,
+ copy_size: (&copy_size).try_into()?,
+ })
+ .expect("Failed to send CopyTextureToTexture");
+
+ Ok(())
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-finish>
+ fn Finish(&self, descriptor: &GPUCommandBufferDescriptor) -> DomRoot<GPUCommandBuffer> {
+ self.channel
+ .0
+ .send(WebGPURequest::CommandEncoderFinish {
+ command_encoder_id: self.encoder.0,
+ device_id: self.device.id().0,
+ desc: wgt::CommandBufferDescriptor {
+ label: (&descriptor.parent).into(),
+ },
+ })
+ .expect("Failed to send Finish");
+
+ let buffer = WebGPUCommandBuffer(self.encoder.0.into_command_buffer_id());
+ GPUCommandBuffer::new(
+ &self.global(),
+ self.channel.clone(),
+ buffer,
+ descriptor.parent.label.clone(),
+ )
+ }
+}
diff --git a/components/script/dom/webgpu/gpucompilationinfo.rs b/components/script/dom/webgpu/gpucompilationinfo.rs
new file mode 100644
index 00000000000..08751c03d57
--- /dev/null
+++ b/components/script/dom/webgpu/gpucompilationinfo.rs
@@ -0,0 +1,63 @@
+/* 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 dom_struct::dom_struct;
+use js::rust::MutableHandleValue;
+use webgpu::ShaderCompilationInfo;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUCompilationInfoMethods;
+use crate::dom::bindings::import::module::DomRoot;
+use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
+use crate::dom::bindings::utils::to_frozen_array;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::types::GPUCompilationMessage;
+use crate::script_runtime::{CanGc, JSContext};
+
+#[dom_struct]
+pub struct GPUCompilationInfo {
+ reflector_: Reflector,
+ // currently we only get one message from wgpu
+ msg: Vec<DomRoot<GPUCompilationMessage>>,
+}
+
+impl GPUCompilationInfo {
+ pub fn new_inherited(msg: Vec<DomRoot<GPUCompilationMessage>>) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ msg,
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn new(
+ global: &GlobalScope,
+ msg: Vec<DomRoot<GPUCompilationMessage>>,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ reflect_dom_object_with_proto(Box::new(Self::new_inherited(msg)), global, None, can_gc)
+ }
+
+ pub fn from(
+ global: &GlobalScope,
+ error: Option<ShaderCompilationInfo>,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ Self::new(
+ global,
+ if let Some(error) = error {
+ vec![GPUCompilationMessage::from(global, error)]
+ } else {
+ Vec::new()
+ },
+ can_gc,
+ )
+ }
+}
+
+impl GPUCompilationInfoMethods<crate::DomTypeHolder> for GPUCompilationInfo {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucompilationinfo-messages>
+ fn Messages(&self, cx: JSContext, retval: MutableHandleValue) {
+ to_frozen_array(self.msg.as_slice(), cx, retval)
+ }
+}
diff --git a/components/script/dom/webgpu/gpucompilationmessage.rs b/components/script/dom/webgpu/gpucompilationmessage.rs
new file mode 100644
index 00000000000..d811e8baf6e
--- /dev/null
+++ b/components/script/dom/webgpu/gpucompilationmessage.rs
@@ -0,0 +1,110 @@
+/* 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/. */
+
+#![allow(dead_code)] // this file is stub as wgpu does not provide info
+
+use dom_struct::dom_struct;
+use webgpu::ShaderCompilationInfo;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUCompilationMessageMethods, GPUCompilationMessageType,
+};
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::types::GlobalScope;
+use crate::test::DOMString;
+
+#[dom_struct]
+pub struct GPUCompilationMessage {
+ reflector_: Reflector,
+ // #[ignore_malloc_size_of = "defined in wgpu-types"]
+ message: DOMString,
+ mtype: GPUCompilationMessageType,
+ line_num: u64,
+ line_pos: u64,
+ offset: u64,
+ length: u64,
+}
+
+impl GPUCompilationMessage {
+ fn new_inherited(
+ message: DOMString,
+ mtype: GPUCompilationMessageType,
+ line_num: u64,
+ line_pos: u64,
+ offset: u64,
+ length: u64,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ message,
+ mtype,
+ line_num,
+ line_pos,
+ offset,
+ length,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ message: DOMString,
+ mtype: GPUCompilationMessageType,
+ line_num: u64,
+ line_pos: u64,
+ offset: u64,
+ length: u64,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(Self::new_inherited(
+ message, mtype, line_num, line_pos, offset, length,
+ )),
+ global,
+ )
+ }
+
+ pub fn from(global: &GlobalScope, info: ShaderCompilationInfo) -> DomRoot<Self> {
+ GPUCompilationMessage::new(
+ global,
+ info.message.into(),
+ GPUCompilationMessageType::Error,
+ info.line_number,
+ info.line_pos,
+ info.offset,
+ info.length,
+ )
+ }
+}
+
+impl GPUCompilationMessageMethods<crate::DomTypeHolder> for GPUCompilationMessage {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucompilationmessage-message>
+ fn Message(&self) -> DOMString {
+ self.message.to_owned()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucompilationmessage-type>
+ fn Type(&self) -> GPUCompilationMessageType {
+ self.mtype
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucompilationmessage-linenum>
+ fn LineNum(&self) -> u64 {
+ self.line_num
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucompilationmessage-linepos>
+ fn LinePos(&self) -> u64 {
+ self.line_pos
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucompilationmessage-offset>
+ fn Offset(&self) -> u64 {
+ self.offset
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucompilationmessage-length>
+ fn Length(&self) -> u64 {
+ self.length
+ }
+}
diff --git a/components/script/dom/webgpu/gpucomputepassencoder.rs b/components/script/dom/webgpu/gpucomputepassencoder.rs
new file mode 100644
index 00000000000..e75157b32fc
--- /dev/null
+++ b/components/script/dom/webgpu/gpucomputepassencoder.rs
@@ -0,0 +1,156 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::{WebGPU, WebGPUComputePass, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUComputePassEncoderMethods;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubindgroup::GPUBindGroup;
+use crate::dom::gpubuffer::GPUBuffer;
+use crate::dom::gpucommandencoder::GPUCommandEncoder;
+use crate::dom::gpucomputepipeline::GPUComputePipeline;
+
+#[dom_struct]
+pub struct GPUComputePassEncoder {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ compute_pass: WebGPUComputePass,
+ command_encoder: Dom<GPUCommandEncoder>,
+}
+
+impl GPUComputePassEncoder {
+ fn new_inherited(
+ channel: WebGPU,
+ parent: &GPUCommandEncoder,
+ compute_pass: WebGPUComputePass,
+ label: USVString,
+ ) -> Self {
+ Self {
+ channel,
+ reflector_: Reflector::new(),
+ label: DomRefCell::new(label),
+ compute_pass,
+ command_encoder: Dom::from_ref(parent),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ parent: &GPUCommandEncoder,
+ compute_pass: WebGPUComputePass,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUComputePassEncoder::new_inherited(
+ channel,
+ parent,
+ compute_pass,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUComputePassEncoderMethods<crate::DomTypeHolder> for GPUComputePassEncoder {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucomputepassencoder-dispatchworkgroups>
+ fn DispatchWorkgroups(&self, x: u32, y: u32, z: u32) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::ComputePassDispatchWorkgroups {
+ compute_pass_id: self.compute_pass.0,
+ x,
+ y,
+ z,
+ device_id: self.command_encoder.device_id().0,
+ })
+ {
+ warn!("Error sending WebGPURequest::ComputePassDispatchWorkgroups: {e:?}")
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucomputepassencoder-dispatchworkgroupsindirect>
+ fn DispatchWorkgroupsIndirect(&self, buffer: &GPUBuffer, offset: u64) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::ComputePassDispatchWorkgroupsIndirect {
+ compute_pass_id: self.compute_pass.0,
+ buffer_id: buffer.id().0,
+ offset,
+ device_id: self.command_encoder.device_id().0,
+ })
+ {
+ warn!("Error sending WebGPURequest::ComputePassDispatchWorkgroupsIndirect: {e:?}")
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-endpass>
+ fn End(&self) {
+ if let Err(e) = self.channel.0.send(WebGPURequest::EndComputePass {
+ compute_pass_id: self.compute_pass.0,
+ device_id: self.command_encoder.device_id().0,
+ command_encoder_id: self.command_encoder.id().0,
+ }) {
+ warn!("Failed to send WebGPURequest::EndComputePass: {e:?}");
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup>
+ fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup, offsets: Vec<u32>) {
+ if let Err(e) = self.channel.0.send(WebGPURequest::ComputePassSetBindGroup {
+ compute_pass_id: self.compute_pass.0,
+ index,
+ bind_group_id: bind_group.id().0,
+ offsets,
+ device_id: self.command_encoder.device_id().0,
+ }) {
+ warn!("Error sending WebGPURequest::ComputePassSetBindGroup: {e:?}")
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpucomputepassencoder-setpipeline>
+ fn SetPipeline(&self, pipeline: &GPUComputePipeline) {
+ if let Err(e) = self.channel.0.send(WebGPURequest::ComputePassSetPipeline {
+ compute_pass_id: self.compute_pass.0,
+ pipeline_id: pipeline.id().0,
+ device_id: self.command_encoder.device_id().0,
+ }) {
+ warn!("Error sending WebGPURequest::ComputePassSetPipeline: {e:?}")
+ }
+ }
+}
+
+impl Drop for GPUComputePassEncoder {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropComputePass(self.compute_pass.0))
+ {
+ warn!("Failed to send WebGPURequest::DropComputePass with {e:?}");
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpucomputepipeline.rs b/components/script/dom/webgpu/gpucomputepipeline.rs
new file mode 100644
index 00000000000..4d905126c71
--- /dev/null
+++ b/components/script/dom/webgpu/gpucomputepipeline.rs
@@ -0,0 +1,154 @@
+/* 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 dom_struct::dom_struct;
+use ipc_channel::ipc::IpcSender;
+use webgpu::wgc::pipeline::ComputePipelineDescriptor;
+use webgpu::{WebGPU, WebGPUBindGroupLayout, WebGPUComputePipeline, WebGPURequest, WebGPUResponse};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUComputePipelineDescriptor, GPUComputePipelineMethods,
+};
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
+use crate::dom::gpudevice::GPUDevice;
+
+#[dom_struct]
+pub struct GPUComputePipeline {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ compute_pipeline: WebGPUComputePipeline,
+ device: Dom<GPUDevice>,
+}
+
+impl GPUComputePipeline {
+ fn new_inherited(
+ compute_pipeline: WebGPUComputePipeline,
+ label: USVString,
+ device: &GPUDevice,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel: device.channel(),
+ label: DomRefCell::new(label),
+ compute_pipeline,
+ device: Dom::from_ref(device),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ compute_pipeline: WebGPUComputePipeline,
+ label: USVString,
+ device: &GPUDevice,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUComputePipeline::new_inherited(
+ compute_pipeline,
+ label,
+ device,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUComputePipeline {
+ pub fn id(&self) -> &WebGPUComputePipeline {
+ &self.compute_pipeline
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPUComputePipelineDescriptor,
+ async_sender: Option<IpcSender<WebGPUResponse>>,
+ ) -> WebGPUComputePipeline {
+ let compute_pipeline_id = device.global().wgpu_id_hub().create_compute_pipeline_id();
+
+ let pipeline_layout = device.get_pipeline_layout_data(&descriptor.parent.layout);
+
+ let desc = ComputePipelineDescriptor {
+ label: (&descriptor.parent.parent).into(),
+ layout: pipeline_layout.explicit(),
+ stage: (&descriptor.compute).into(),
+ cache: None,
+ };
+
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateComputePipeline {
+ device_id: device.id().0,
+ compute_pipeline_id,
+ descriptor: desc,
+ implicit_ids: pipeline_layout.implicit(),
+ async_sender,
+ })
+ .expect("Failed to create WebGPU ComputePipeline");
+
+ WebGPUComputePipeline(compute_pipeline_id)
+ }
+}
+
+impl GPUComputePipelineMethods<crate::DomTypeHolder> for GPUComputePipeline {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpupipelinebase-getbindgrouplayout>
+ fn GetBindGroupLayout(&self, index: u32) -> Fallible<DomRoot<GPUBindGroupLayout>> {
+ let id = self.global().wgpu_id_hub().create_bind_group_layout_id();
+
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::ComputeGetBindGroupLayout {
+ device_id: self.device.id().0,
+ pipeline_id: self.compute_pipeline.0,
+ index,
+ id,
+ })
+ {
+ warn!("Failed to send WebGPURequest::ComputeGetBindGroupLayout {e:?}");
+ }
+
+ Ok(GPUBindGroupLayout::new(
+ &self.global(),
+ self.channel.clone(),
+ WebGPUBindGroupLayout(id),
+ USVString::default(),
+ ))
+ }
+}
+
+impl Drop for GPUComputePipeline {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropComputePipeline(self.compute_pipeline.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DropComputePipeline({:?}) ({})",
+ self.compute_pipeline.0, e
+ );
+ };
+ }
+}
diff --git a/components/script/dom/webgpu/gpuconvert.rs b/components/script/dom/webgpu/gpuconvert.rs
new file mode 100644
index 00000000000..db2390a03b0
--- /dev/null
+++ b/components/script/dom/webgpu/gpuconvert.rs
@@ -0,0 +1,680 @@
+/* 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 std::borrow::Cow;
+use std::num::NonZeroU64;
+
+use webgpu::wgc::binding_model::{BindGroupEntry, BindingResource, BufferBinding};
+use webgpu::wgc::command as wgpu_com;
+use webgpu::wgc::pipeline::ProgrammableStageDescriptor;
+use webgpu::wgc::resource::TextureDescriptor;
+use webgpu::wgt::{self, AstcBlock, AstcChannel};
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUAddressMode, GPUBindGroupEntry, GPUBindGroupLayoutEntry, GPUBindingResource,
+ GPUBlendComponent, GPUBlendFactor, GPUBlendOperation, GPUBufferBindingType, GPUColor,
+ GPUCompareFunction, GPUCullMode, GPUExtent3D, GPUFilterMode, GPUFrontFace, GPUImageCopyBuffer,
+ GPUImageCopyTexture, GPUImageDataLayout, GPUIndexFormat, GPULoadOp, GPUObjectDescriptorBase,
+ GPUOrigin3D, GPUPrimitiveState, GPUPrimitiveTopology, GPUProgrammableStage,
+ GPUSamplerBindingType, GPUStencilOperation, GPUStorageTextureAccess, GPUStoreOp,
+ GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
+ GPUTextureSampleType, GPUTextureViewDimension, GPUVertexFormat,
+};
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::types::GPUDevice;
+
+impl From<GPUTextureFormat> for wgt::TextureFormat {
+ fn from(format: GPUTextureFormat) -> Self {
+ match format {
+ GPUTextureFormat::R8unorm => wgt::TextureFormat::R8Unorm,
+ GPUTextureFormat::R8snorm => wgt::TextureFormat::R8Snorm,
+ GPUTextureFormat::R8uint => wgt::TextureFormat::R8Uint,
+ GPUTextureFormat::R8sint => wgt::TextureFormat::R8Sint,
+ GPUTextureFormat::R16uint => wgt::TextureFormat::R16Uint,
+ GPUTextureFormat::R16sint => wgt::TextureFormat::R16Sint,
+ GPUTextureFormat::R16float => wgt::TextureFormat::R16Float,
+ GPUTextureFormat::Rg8unorm => wgt::TextureFormat::Rg8Unorm,
+ GPUTextureFormat::Rg8snorm => wgt::TextureFormat::Rg8Snorm,
+ GPUTextureFormat::Rg8uint => wgt::TextureFormat::Rg8Uint,
+ GPUTextureFormat::Rg8sint => wgt::TextureFormat::Rg8Sint,
+ GPUTextureFormat::R32uint => wgt::TextureFormat::R32Uint,
+ GPUTextureFormat::R32sint => wgt::TextureFormat::R32Sint,
+ GPUTextureFormat::R32float => wgt::TextureFormat::R32Float,
+ GPUTextureFormat::Rg16uint => wgt::TextureFormat::Rg16Uint,
+ GPUTextureFormat::Rg16sint => wgt::TextureFormat::Rg16Sint,
+ GPUTextureFormat::Rg16float => wgt::TextureFormat::Rg16Float,
+ GPUTextureFormat::Rgba8unorm => wgt::TextureFormat::Rgba8Unorm,
+ GPUTextureFormat::Rgba8unorm_srgb => wgt::TextureFormat::Rgba8UnormSrgb,
+ GPUTextureFormat::Rgba8snorm => wgt::TextureFormat::Rgba8Snorm,
+ GPUTextureFormat::Rgba8uint => wgt::TextureFormat::Rgba8Uint,
+ GPUTextureFormat::Rgba8sint => wgt::TextureFormat::Rgba8Sint,
+ GPUTextureFormat::Bgra8unorm => wgt::TextureFormat::Bgra8Unorm,
+ GPUTextureFormat::Bgra8unorm_srgb => wgt::TextureFormat::Bgra8UnormSrgb,
+ GPUTextureFormat::Rgb10a2unorm => wgt::TextureFormat::Rgb10a2Unorm,
+ GPUTextureFormat::Rg32uint => wgt::TextureFormat::Rg32Uint,
+ GPUTextureFormat::Rg32sint => wgt::TextureFormat::Rg32Sint,
+ GPUTextureFormat::Rg32float => wgt::TextureFormat::Rg32Float,
+ GPUTextureFormat::Rgba16uint => wgt::TextureFormat::Rgba16Uint,
+ GPUTextureFormat::Rgba16sint => wgt::TextureFormat::Rgba16Sint,
+ GPUTextureFormat::Rgba16float => wgt::TextureFormat::Rgba16Float,
+ GPUTextureFormat::Rgba32uint => wgt::TextureFormat::Rgba32Uint,
+ GPUTextureFormat::Rgba32sint => wgt::TextureFormat::Rgba32Sint,
+ GPUTextureFormat::Rgba32float => wgt::TextureFormat::Rgba32Float,
+ GPUTextureFormat::Depth32float => wgt::TextureFormat::Depth32Float,
+ GPUTextureFormat::Depth24plus => wgt::TextureFormat::Depth24Plus,
+ GPUTextureFormat::Depth24plus_stencil8 => wgt::TextureFormat::Depth24PlusStencil8,
+ GPUTextureFormat::Bc1_rgba_unorm => wgt::TextureFormat::Bc1RgbaUnorm,
+ GPUTextureFormat::Bc1_rgba_unorm_srgb => wgt::TextureFormat::Bc1RgbaUnormSrgb,
+ GPUTextureFormat::Bc2_rgba_unorm => wgt::TextureFormat::Bc2RgbaUnorm,
+ GPUTextureFormat::Bc2_rgba_unorm_srgb => wgt::TextureFormat::Bc2RgbaUnormSrgb,
+ GPUTextureFormat::Bc3_rgba_unorm => wgt::TextureFormat::Bc3RgbaUnorm,
+ GPUTextureFormat::Bc3_rgba_unorm_srgb => wgt::TextureFormat::Bc3RgbaUnormSrgb,
+ GPUTextureFormat::Bc4_r_unorm => wgt::TextureFormat::Bc4RUnorm,
+ GPUTextureFormat::Bc4_r_snorm => wgt::TextureFormat::Bc4RSnorm,
+ GPUTextureFormat::Bc5_rg_unorm => wgt::TextureFormat::Bc5RgUnorm,
+ GPUTextureFormat::Bc5_rg_snorm => wgt::TextureFormat::Bc5RgSnorm,
+ GPUTextureFormat::Bc6h_rgb_ufloat => wgt::TextureFormat::Bc6hRgbUfloat,
+ GPUTextureFormat::Bc7_rgba_unorm => wgt::TextureFormat::Bc7RgbaUnorm,
+ GPUTextureFormat::Bc7_rgba_unorm_srgb => wgt::TextureFormat::Bc7RgbaUnormSrgb,
+ GPUTextureFormat::Bc6h_rgb_float => wgt::TextureFormat::Bc6hRgbFloat,
+ GPUTextureFormat::Rgb9e5ufloat => wgt::TextureFormat::Rgb9e5Ufloat,
+ GPUTextureFormat::Rgb10a2uint => wgt::TextureFormat::Rgb10a2Uint,
+ GPUTextureFormat::Rg11b10ufloat => wgt::TextureFormat::Rg11b10Ufloat,
+ GPUTextureFormat::Stencil8 => wgt::TextureFormat::Stencil8,
+ GPUTextureFormat::Depth16unorm => wgt::TextureFormat::Depth16Unorm,
+ GPUTextureFormat::Depth32float_stencil8 => wgt::TextureFormat::Depth32FloatStencil8,
+ GPUTextureFormat::Etc2_rgb8unorm => wgt::TextureFormat::Etc2Rgb8Unorm,
+ GPUTextureFormat::Etc2_rgb8unorm_srgb => wgt::TextureFormat::Etc2Rgb8UnormSrgb,
+ GPUTextureFormat::Etc2_rgb8a1unorm => wgt::TextureFormat::Etc2Rgb8A1Unorm,
+ GPUTextureFormat::Etc2_rgb8a1unorm_srgb => wgt::TextureFormat::Etc2Rgb8A1UnormSrgb,
+ GPUTextureFormat::Etc2_rgba8unorm => wgt::TextureFormat::Etc2Rgba8Unorm,
+ GPUTextureFormat::Etc2_rgba8unorm_srgb => wgt::TextureFormat::Etc2Rgba8UnormSrgb,
+ GPUTextureFormat::Eac_r11unorm => wgt::TextureFormat::EacR11Unorm,
+ GPUTextureFormat::Eac_r11snorm => wgt::TextureFormat::EacR11Snorm,
+ GPUTextureFormat::Eac_rg11unorm => wgt::TextureFormat::EacRg11Unorm,
+ GPUTextureFormat::Eac_rg11snorm => wgt::TextureFormat::EacRg11Snorm,
+ GPUTextureFormat::Astc_4x4_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B4x4,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_4x4_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B4x4,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_5x4_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B5x4,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_5x4_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B5x4,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_5x5_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B5x5,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_5x5_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B5x5,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_6x5_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B6x5,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_6x5_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B6x5,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_6x6_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B6x6,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_6x6_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B6x6,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_8x5_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B8x5,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_8x5_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B8x5,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_8x6_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B8x6,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_8x6_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B8x6,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_8x8_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B8x8,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_8x8_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B8x8,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_10x5_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x5,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_10x5_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x5,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_10x6_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x6,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_10x6_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x6,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_10x8_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x8,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_10x8_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x8,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_10x10_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x10,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_10x10_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B10x10,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_12x10_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B12x10,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_12x10_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B12x10,
+ channel: AstcChannel::UnormSrgb,
+ },
+ GPUTextureFormat::Astc_12x12_unorm => wgt::TextureFormat::Astc {
+ block: AstcBlock::B12x12,
+ channel: AstcChannel::Unorm,
+ },
+ GPUTextureFormat::Astc_12x12_unorm_srgb => wgt::TextureFormat::Astc {
+ block: AstcBlock::B12x12,
+ channel: AstcChannel::UnormSrgb,
+ },
+ }
+ }
+}
+
+impl TryFrom<&GPUExtent3D> for wgt::Extent3d {
+ type Error = Error;
+
+ fn try_from(size: &GPUExtent3D) -> Result<Self, Self::Error> {
+ match *size {
+ GPUExtent3D::GPUExtent3DDict(ref dict) => Ok(wgt::Extent3d {
+ width: dict.width,
+ height: dict.height,
+ depth_or_array_layers: dict.depthOrArrayLayers,
+ }),
+ GPUExtent3D::RangeEnforcedUnsignedLongSequence(ref v) => {
+ // https://gpuweb.github.io/gpuweb/#abstract-opdef-validate-gpuextent3d-shape
+ if v.is_empty() || v.len() > 3 {
+ Err(Error::Type(
+ "GPUExtent3D size must be between 1 and 3 (inclusive)".to_string(),
+ ))
+ } else {
+ Ok(wgt::Extent3d {
+ width: v[0],
+ height: v.get(1).copied().unwrap_or(1),
+ depth_or_array_layers: v.get(2).copied().unwrap_or(1),
+ })
+ }
+ },
+ }
+ }
+}
+
+impl From<&GPUImageDataLayout> for wgt::ImageDataLayout {
+ fn from(data_layout: &GPUImageDataLayout) -> Self {
+ wgt::ImageDataLayout {
+ offset: data_layout.offset as wgt::BufferAddress,
+ bytes_per_row: data_layout.bytesPerRow,
+ rows_per_image: data_layout.rowsPerImage,
+ }
+ }
+}
+
+impl From<GPUVertexFormat> for wgt::VertexFormat {
+ fn from(format: GPUVertexFormat) -> Self {
+ match format {
+ GPUVertexFormat::Uint8x2 => wgt::VertexFormat::Uint8x2,
+ GPUVertexFormat::Uint8x4 => wgt::VertexFormat::Uint8x4,
+ GPUVertexFormat::Sint8x2 => wgt::VertexFormat::Sint8x2,
+ GPUVertexFormat::Sint8x4 => wgt::VertexFormat::Sint8x4,
+ GPUVertexFormat::Unorm8x2 => wgt::VertexFormat::Unorm8x2,
+ GPUVertexFormat::Unorm8x4 => wgt::VertexFormat::Unorm8x4,
+ GPUVertexFormat::Snorm8x2 => wgt::VertexFormat::Unorm8x2,
+ GPUVertexFormat::Snorm8x4 => wgt::VertexFormat::Unorm8x4,
+ GPUVertexFormat::Uint16x2 => wgt::VertexFormat::Uint16x2,
+ GPUVertexFormat::Uint16x4 => wgt::VertexFormat::Uint16x4,
+ GPUVertexFormat::Sint16x2 => wgt::VertexFormat::Sint16x2,
+ GPUVertexFormat::Sint16x4 => wgt::VertexFormat::Sint16x4,
+ GPUVertexFormat::Unorm16x2 => wgt::VertexFormat::Unorm16x2,
+ GPUVertexFormat::Unorm16x4 => wgt::VertexFormat::Unorm16x4,
+ GPUVertexFormat::Snorm16x2 => wgt::VertexFormat::Snorm16x2,
+ GPUVertexFormat::Snorm16x4 => wgt::VertexFormat::Snorm16x4,
+ GPUVertexFormat::Float16x2 => wgt::VertexFormat::Float16x2,
+ GPUVertexFormat::Float16x4 => wgt::VertexFormat::Float16x4,
+ GPUVertexFormat::Float32 => wgt::VertexFormat::Float32,
+ GPUVertexFormat::Float32x2 => wgt::VertexFormat::Float32x2,
+ GPUVertexFormat::Float32x3 => wgt::VertexFormat::Float32x3,
+ GPUVertexFormat::Float32x4 => wgt::VertexFormat::Float32x4,
+ GPUVertexFormat::Uint32 => wgt::VertexFormat::Uint32,
+ GPUVertexFormat::Uint32x2 => wgt::VertexFormat::Uint32x2,
+ GPUVertexFormat::Uint32x3 => wgt::VertexFormat::Uint32x3,
+ GPUVertexFormat::Uint32x4 => wgt::VertexFormat::Uint32x4,
+ GPUVertexFormat::Sint32 => wgt::VertexFormat::Sint32,
+ GPUVertexFormat::Sint32x2 => wgt::VertexFormat::Sint32x2,
+ GPUVertexFormat::Sint32x3 => wgt::VertexFormat::Sint32x3,
+ GPUVertexFormat::Sint32x4 => wgt::VertexFormat::Sint32x4,
+ }
+ }
+}
+
+impl From<&GPUPrimitiveState> for wgt::PrimitiveState {
+ fn from(primitive_state: &GPUPrimitiveState) -> Self {
+ wgt::PrimitiveState {
+ topology: wgt::PrimitiveTopology::from(&primitive_state.topology),
+ strip_index_format: primitive_state.stripIndexFormat.map(|index_format| {
+ match index_format {
+ GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16,
+ GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32,
+ }
+ }),
+ front_face: match primitive_state.frontFace {
+ GPUFrontFace::Ccw => wgt::FrontFace::Ccw,
+ GPUFrontFace::Cw => wgt::FrontFace::Cw,
+ },
+ cull_mode: match primitive_state.cullMode {
+ GPUCullMode::None => None,
+ GPUCullMode::Front => Some(wgt::Face::Front),
+ GPUCullMode::Back => Some(wgt::Face::Back),
+ },
+ unclipped_depth: primitive_state.clampDepth,
+ ..Default::default()
+ }
+ }
+}
+
+impl From<&GPUPrimitiveTopology> for wgt::PrimitiveTopology {
+ fn from(primitive_topology: &GPUPrimitiveTopology) -> Self {
+ match primitive_topology {
+ 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,
+ }
+ }
+}
+
+impl From<GPUAddressMode> for wgt::AddressMode {
+ fn from(address_mode: GPUAddressMode) -> Self {
+ match address_mode {
+ GPUAddressMode::Clamp_to_edge => wgt::AddressMode::ClampToEdge,
+ GPUAddressMode::Repeat => wgt::AddressMode::Repeat,
+ GPUAddressMode::Mirror_repeat => wgt::AddressMode::MirrorRepeat,
+ }
+ }
+}
+
+impl From<GPUFilterMode> for wgt::FilterMode {
+ fn from(filter_mode: GPUFilterMode) -> Self {
+ match filter_mode {
+ GPUFilterMode::Nearest => wgt::FilterMode::Nearest,
+ GPUFilterMode::Linear => wgt::FilterMode::Linear,
+ }
+ }
+}
+
+impl From<GPUTextureViewDimension> for wgt::TextureViewDimension {
+ fn from(view_dimension: GPUTextureViewDimension) -> Self {
+ match view_dimension {
+ GPUTextureViewDimension::_1d => wgt::TextureViewDimension::D1,
+ GPUTextureViewDimension::_2d => wgt::TextureViewDimension::D2,
+ GPUTextureViewDimension::_2d_array => wgt::TextureViewDimension::D2Array,
+ GPUTextureViewDimension::Cube => wgt::TextureViewDimension::Cube,
+ GPUTextureViewDimension::Cube_array => wgt::TextureViewDimension::CubeArray,
+ GPUTextureViewDimension::_3d => wgt::TextureViewDimension::D3,
+ }
+ }
+}
+
+impl From<GPUCompareFunction> for wgt::CompareFunction {
+ fn from(compare: GPUCompareFunction) -> Self {
+ 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,
+ }
+ }
+}
+
+impl From<&GPUBlendFactor> for wgt::BlendFactor {
+ fn from(factor: &GPUBlendFactor) -> Self {
+ match factor {
+ GPUBlendFactor::Zero => wgt::BlendFactor::Zero,
+ GPUBlendFactor::One => wgt::BlendFactor::One,
+ GPUBlendFactor::Src => wgt::BlendFactor::Src,
+ GPUBlendFactor::One_minus_src => wgt::BlendFactor::OneMinusSrc,
+ GPUBlendFactor::Src_alpha => wgt::BlendFactor::SrcAlpha,
+ GPUBlendFactor::One_minus_src_alpha => wgt::BlendFactor::OneMinusSrcAlpha,
+ GPUBlendFactor::Dst => wgt::BlendFactor::Dst,
+ GPUBlendFactor::One_minus_dst => wgt::BlendFactor::OneMinusDst,
+ GPUBlendFactor::Dst_alpha => wgt::BlendFactor::DstAlpha,
+ GPUBlendFactor::One_minus_dst_alpha => wgt::BlendFactor::OneMinusDstAlpha,
+ GPUBlendFactor::Src_alpha_saturated => wgt::BlendFactor::SrcAlphaSaturated,
+ GPUBlendFactor::Constant => wgt::BlendFactor::Constant,
+ GPUBlendFactor::One_minus_constant => wgt::BlendFactor::OneMinusConstant,
+ }
+ }
+}
+
+impl From<&GPUBlendComponent> for wgt::BlendComponent {
+ fn from(blend_component: &GPUBlendComponent) -> Self {
+ wgt::BlendComponent {
+ src_factor: wgt::BlendFactor::from(&blend_component.srcFactor),
+ dst_factor: wgt::BlendFactor::from(&blend_component.dstFactor),
+ operation: match blend_component.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,
+ },
+ }
+ }
+}
+
+pub fn convert_load_op(op: Option<GPULoadOp>) -> wgpu_com::LoadOp {
+ match op {
+ Some(GPULoadOp::Load) => wgpu_com::LoadOp::Load,
+ Some(GPULoadOp::Clear) => wgpu_com::LoadOp::Clear,
+ None => wgpu_com::LoadOp::Clear,
+ }
+}
+
+pub fn convert_store_op(op: Option<GPUStoreOp>) -> wgpu_com::StoreOp {
+ match op {
+ Some(GPUStoreOp::Store) => wgpu_com::StoreOp::Store,
+ Some(GPUStoreOp::Discard) => wgpu_com::StoreOp::Discard,
+ None => wgpu_com::StoreOp::Discard,
+ }
+}
+
+impl From<GPUStencilOperation> for wgt::StencilOperation {
+ fn from(operation: GPUStencilOperation) -> Self {
+ 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,
+ }
+ }
+}
+
+impl From<&GPUImageCopyBuffer> for wgpu_com::ImageCopyBuffer {
+ fn from(ic_buffer: &GPUImageCopyBuffer) -> Self {
+ wgpu_com::ImageCopyBuffer {
+ buffer: ic_buffer.buffer.id().0,
+ layout: wgt::ImageDataLayout::from(&ic_buffer.parent),
+ }
+ }
+}
+
+impl TryFrom<&GPUOrigin3D> for wgt::Origin3d {
+ type Error = Error;
+
+ fn try_from(origin: &GPUOrigin3D) -> Result<Self, Self::Error> {
+ match origin {
+ GPUOrigin3D::RangeEnforcedUnsignedLongSequence(v) => {
+ // https://gpuweb.github.io/gpuweb/#abstract-opdef-validate-gpuorigin3d-shape
+ if v.len() > 3 {
+ Err(Error::Type(
+ "sequence is too long for GPUOrigin3D".to_string(),
+ ))
+ } else {
+ Ok(wgt::Origin3d {
+ x: v.first().copied().unwrap_or(0),
+ y: v.get(1).copied().unwrap_or(0),
+ z: v.get(2).copied().unwrap_or(0),
+ })
+ }
+ },
+ GPUOrigin3D::GPUOrigin3DDict(d) => Ok(wgt::Origin3d {
+ x: d.x,
+ y: d.y,
+ z: d.z,
+ }),
+ }
+ }
+}
+
+impl TryFrom<&GPUImageCopyTexture> for wgpu_com::ImageCopyTexture {
+ type Error = Error;
+
+ fn try_from(ic_texture: &GPUImageCopyTexture) -> Result<Self, Self::Error> {
+ Ok(wgpu_com::ImageCopyTexture {
+ texture: ic_texture.texture.id().0,
+ mip_level: ic_texture.mipLevel,
+ origin: ic_texture
+ .origin
+ .as_ref()
+ .map(wgt::Origin3d::try_from)
+ .transpose()?
+ .unwrap_or_default(),
+ aspect: match ic_texture.aspect {
+ GPUTextureAspect::All => wgt::TextureAspect::All,
+ GPUTextureAspect::Stencil_only => wgt::TextureAspect::StencilOnly,
+ GPUTextureAspect::Depth_only => wgt::TextureAspect::DepthOnly,
+ },
+ })
+ }
+}
+
+impl<'a> From<&GPUObjectDescriptorBase> for Option<Cow<'a, str>> {
+ fn from(val: &GPUObjectDescriptorBase) -> Self {
+ if val.label.is_empty() {
+ None
+ } else {
+ Some(Cow::Owned(val.label.to_string()))
+ }
+ }
+}
+
+pub fn convert_bind_group_layout_entry(
+ bgle: &GPUBindGroupLayoutEntry,
+ device: &GPUDevice,
+) -> Fallible<Result<wgt::BindGroupLayoutEntry, webgpu::Error>> {
+ let number_of_provided_bindings = bgle.buffer.is_some() as u8 +
+ bgle.sampler.is_some() as u8 +
+ bgle.storageTexture.is_some() as u8 +
+ bgle.texture.is_some() as u8;
+ let ty = if let Some(buffer) = &bgle.buffer {
+ Some(wgt::BindingType::Buffer {
+ ty: match buffer.type_ {
+ GPUBufferBindingType::Uniform => wgt::BufferBindingType::Uniform,
+ GPUBufferBindingType::Storage => {
+ wgt::BufferBindingType::Storage { read_only: false }
+ },
+ GPUBufferBindingType::Read_only_storage => {
+ wgt::BufferBindingType::Storage { read_only: true }
+ },
+ },
+ has_dynamic_offset: buffer.hasDynamicOffset,
+ min_binding_size: NonZeroU64::new(buffer.minBindingSize),
+ })
+ } else if let Some(sampler) = &bgle.sampler {
+ Some(wgt::BindingType::Sampler(match sampler.type_ {
+ GPUSamplerBindingType::Filtering => wgt::SamplerBindingType::Filtering,
+ GPUSamplerBindingType::Non_filtering => wgt::SamplerBindingType::NonFiltering,
+ GPUSamplerBindingType::Comparison => wgt::SamplerBindingType::Comparison,
+ }))
+ } else if let Some(storage) = &bgle.storageTexture {
+ Some(wgt::BindingType::StorageTexture {
+ access: match storage.access {
+ GPUStorageTextureAccess::Write_only => wgt::StorageTextureAccess::WriteOnly,
+ GPUStorageTextureAccess::Read_only => wgt::StorageTextureAccess::ReadOnly,
+ GPUStorageTextureAccess::Read_write => wgt::StorageTextureAccess::ReadWrite,
+ },
+ format: device.validate_texture_format_required_features(&storage.format)?,
+ view_dimension: storage.viewDimension.into(),
+ })
+ } else if let Some(texture) = &bgle.texture {
+ Some(wgt::BindingType::Texture {
+ sample_type: match texture.sampleType {
+ GPUTextureSampleType::Float => wgt::TextureSampleType::Float { filterable: true },
+ GPUTextureSampleType::Unfilterable_float => {
+ wgt::TextureSampleType::Float { filterable: false }
+ },
+ GPUTextureSampleType::Depth => wgt::TextureSampleType::Depth,
+ GPUTextureSampleType::Sint => wgt::TextureSampleType::Sint,
+ GPUTextureSampleType::Uint => wgt::TextureSampleType::Uint,
+ },
+ view_dimension: texture.viewDimension.into(),
+ multisampled: texture.multisampled,
+ })
+ } else {
+ assert_eq!(number_of_provided_bindings, 0);
+ None
+ };
+ // Check for number of bindings should actually be done in device-timeline,
+ // but we do it last on content-timeline to have some visible effect
+ let ty = if number_of_provided_bindings != 1 {
+ None
+ } else {
+ ty
+ }
+ .ok_or(webgpu::Error::Validation(
+ "Exactly on entry type must be provided".to_string(),
+ ));
+
+ Ok(ty.map(|ty| wgt::BindGroupLayoutEntry {
+ binding: bgle.binding,
+ visibility: wgt::ShaderStages::from_bits_retain(bgle.visibility),
+ ty,
+ count: None,
+ }))
+}
+
+pub fn convert_texture_descriptor(
+ descriptor: &GPUTextureDescriptor,
+ device: &GPUDevice,
+) -> Fallible<(TextureDescriptor<'static>, wgt::Extent3d)> {
+ let size = (&descriptor.size).try_into()?;
+ let desc = TextureDescriptor {
+ label: (&descriptor.parent).into(),
+ size,
+ mip_level_count: descriptor.mipLevelCount,
+ sample_count: descriptor.sampleCount,
+ dimension: descriptor.dimension.into(),
+ format: device.validate_texture_format_required_features(&descriptor.format)?,
+ usage: wgt::TextureUsages::from_bits_retain(descriptor.usage),
+ view_formats: descriptor
+ .viewFormats
+ .iter()
+ .map(|tf| device.validate_texture_format_required_features(tf))
+ .collect::<Fallible<_>>()?,
+ };
+ Ok((desc, size))
+}
+
+impl TryFrom<&GPUColor> for wgt::Color {
+ type Error = Error;
+
+ fn try_from(color: &GPUColor) -> Result<Self, Self::Error> {
+ match color {
+ GPUColor::DoubleSequence(s) => {
+ // https://gpuweb.github.io/gpuweb/#abstract-opdef-validate-gpucolor-shape
+ if s.len() != 4 {
+ Err(Error::Type("GPUColor sequence must be len 4".to_string()))
+ } else {
+ Ok(wgt::Color {
+ r: *s[0],
+ g: *s[1],
+ b: *s[2],
+ a: *s[3],
+ })
+ }
+ },
+ GPUColor::GPUColorDict(d) => Ok(wgt::Color {
+ r: *d.r,
+ g: *d.g,
+ b: *d.b,
+ a: *d.a,
+ }),
+ }
+ }
+}
+
+impl<'a> From<&GPUProgrammableStage> for ProgrammableStageDescriptor<'a> {
+ fn from(stage: &GPUProgrammableStage) -> Self {
+ Self {
+ module: stage.module.id().0,
+ entry_point: stage
+ .entryPoint
+ .as_ref()
+ .map(|ep| Cow::Owned(ep.to_string())),
+ constants: Cow::Owned(
+ stage
+ .constants
+ .as_ref()
+ .map(|records| records.iter().map(|(k, v)| (k.0.clone(), **v)).collect())
+ .unwrap_or_default(),
+ ),
+ zero_initialize_workgroup_memory: true,
+ }
+ }
+}
+
+impl From<&GPUBindGroupEntry> for BindGroupEntry<'_> {
+ fn from(entry: &GPUBindGroupEntry) -> Self {
+ Self {
+ binding: entry.binding,
+ resource: match entry.resource {
+ GPUBindingResource::GPUSampler(ref s) => BindingResource::Sampler(s.id().0),
+ GPUBindingResource::GPUTextureView(ref t) => BindingResource::TextureView(t.id().0),
+ GPUBindingResource::GPUBufferBinding(ref b) => {
+ BindingResource::Buffer(BufferBinding {
+ buffer_id: b.buffer.id().0,
+ offset: b.offset,
+ size: b.size.and_then(wgt::BufferSize::new),
+ })
+ },
+ },
+ }
+ }
+}
+
+impl From<GPUTextureDimension> for wgt::TextureDimension {
+ fn from(dimension: GPUTextureDimension) -> Self {
+ match dimension {
+ GPUTextureDimension::_1d => wgt::TextureDimension::D1,
+ GPUTextureDimension::_2d => wgt::TextureDimension::D2,
+ GPUTextureDimension::_3d => wgt::TextureDimension::D3,
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpudevice.rs b/components/script/dom/webgpu/gpudevice.rs
new file mode 100644
index 00000000000..945c7491874
--- /dev/null
+++ b/components/script/dom/webgpu/gpudevice.rs
@@ -0,0 +1,652 @@
+/* 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/. */
+
+#![allow(unsafe_code)]
+
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::rc::Rc;
+
+use dom_struct::dom_struct;
+use js::jsapi::{Heap, JSObject};
+use webgpu::wgc::id::{BindGroupLayoutId, PipelineLayoutId};
+use webgpu::wgc::pipeline as wgpu_pipe;
+use webgpu::wgc::pipeline::RenderPipelineDescriptor;
+use webgpu::wgt::TextureFormat;
+use webgpu::{
+ wgt, PopError, WebGPU, WebGPUComputePipeline, WebGPURenderPipeline, WebGPURequest,
+ WebGPUResponse,
+};
+
+use super::gpu::AsyncWGPUListener;
+use super::gpudevicelostinfo::GPUDeviceLostInfo;
+use super::gpupipelineerror::GPUPipelineError;
+use super::gpusupportedlimits::GPUSupportedLimits;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
+use crate::dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBufferDescriptor,
+ GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, GPUDeviceLostReason,
+ GPUDeviceMethods, GPUErrorFilter, GPUPipelineErrorReason, GPUPipelineLayoutDescriptor,
+ GPURenderBundleEncoderDescriptor, GPURenderPipelineDescriptor, GPUSamplerDescriptor,
+ GPUShaderModuleDescriptor, GPUSupportedLimitsMethods, GPUTextureDescriptor, GPUTextureFormat,
+ GPUUncapturedErrorEventInit, GPUVertexStepMode,
+};
+use crate::dom::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::{DOMString, USVString};
+use crate::dom::bindings::trace::RootedTraceableBox;
+use crate::dom::eventtarget::EventTarget;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpuadapter::GPUAdapter;
+use crate::dom::gpubindgroup::GPUBindGroup;
+use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
+use crate::dom::gpubuffer::GPUBuffer;
+use crate::dom::gpucommandencoder::GPUCommandEncoder;
+use crate::dom::gpucomputepipeline::GPUComputePipeline;
+use crate::dom::gpupipelinelayout::GPUPipelineLayout;
+use crate::dom::gpuqueue::GPUQueue;
+use crate::dom::gpurenderbundleencoder::GPURenderBundleEncoder;
+use crate::dom::gpurenderpipeline::GPURenderPipeline;
+use crate::dom::gpusampler::GPUSampler;
+use crate::dom::gpushadermodule::GPUShaderModule;
+use crate::dom::gpusupportedfeatures::GPUSupportedFeatures;
+use crate::dom::gputexture::GPUTexture;
+use crate::dom::gpuuncapturederrorevent::GPUUncapturedErrorEvent;
+use crate::dom::promise::Promise;
+use crate::dom::types::GPUError;
+use crate::dom::webgpu::gpu::response_async;
+use crate::realms::InRealm;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUDevice {
+ eventtarget: EventTarget,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ adapter: Dom<GPUAdapter>,
+ #[ignore_malloc_size_of = "mozjs"]
+ extensions: Heap<*mut JSObject>,
+ features: Dom<GPUSupportedFeatures>,
+ limits: Dom<GPUSupportedLimits>,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ device: webgpu::WebGPUDevice,
+ default_queue: Dom<GPUQueue>,
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
+ #[ignore_malloc_size_of = "promises are hard"]
+ lost_promise: DomRefCell<Rc<Promise>>,
+ valid: Cell<bool>,
+}
+
+pub enum PipelineLayout {
+ Implicit(PipelineLayoutId, Vec<BindGroupLayoutId>),
+ Explicit(PipelineLayoutId),
+}
+
+impl PipelineLayout {
+ pub fn explicit(&self) -> Option<PipelineLayoutId> {
+ match self {
+ PipelineLayout::Explicit(layout_id) => Some(*layout_id),
+ _ => None,
+ }
+ }
+
+ pub fn implicit(self) -> Option<(PipelineLayoutId, Vec<BindGroupLayoutId>)> {
+ match self {
+ PipelineLayout::Implicit(layout_id, bind_group_layout_ids) => {
+ Some((layout_id, bind_group_layout_ids))
+ },
+ _ => None,
+ }
+ }
+}
+
+impl GPUDevice {
+ #[allow(clippy::too_many_arguments)]
+ fn new_inherited(
+ channel: WebGPU,
+ adapter: &GPUAdapter,
+ extensions: Heap<*mut JSObject>,
+ features: &GPUSupportedFeatures,
+ limits: &GPUSupportedLimits,
+ device: webgpu::WebGPUDevice,
+ queue: &GPUQueue,
+ label: String,
+ lost_promise: Rc<Promise>,
+ ) -> Self {
+ Self {
+ eventtarget: EventTarget::new_inherited(),
+ channel,
+ adapter: Dom::from_ref(adapter),
+ extensions,
+ features: Dom::from_ref(features),
+ limits: Dom::from_ref(limits),
+ label: DomRefCell::new(USVString::from(label)),
+ device,
+ default_queue: Dom::from_ref(queue),
+ lost_promise: DomRefCell::new(lost_promise),
+ valid: Cell::new(true),
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ adapter: &GPUAdapter,
+ extensions: Heap<*mut JSObject>,
+ features: wgt::Features,
+ limits: wgt::Limits,
+ device: webgpu::WebGPUDevice,
+ queue: webgpu::WebGPUQueue,
+ label: String,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ let queue = GPUQueue::new(global, channel.clone(), queue);
+ let limits = GPUSupportedLimits::new(global, limits);
+ let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
+ let lost_promise = Promise::new(global, can_gc);
+ let device = reflect_dom_object(
+ Box::new(GPUDevice::new_inherited(
+ channel,
+ adapter,
+ extensions,
+ &features,
+ &limits,
+ device,
+ &queue,
+ label,
+ lost_promise,
+ )),
+ global,
+ );
+ queue.set_device(&device);
+ device
+ }
+}
+
+impl GPUDevice {
+ pub fn id(&self) -> webgpu::WebGPUDevice {
+ self.device
+ }
+
+ pub fn queue_id(&self) -> webgpu::WebGPUQueue {
+ self.default_queue.id()
+ }
+
+ pub fn channel(&self) -> WebGPU {
+ self.channel.clone()
+ }
+
+ pub fn dispatch_error(&self, error: webgpu::Error) {
+ if let Err(e) = self.channel.0.send(WebGPURequest::DispatchError {
+ device_id: self.device.0,
+ error,
+ }) {
+ warn!("Failed to send WebGPURequest::DispatchError due to {e:?}");
+ }
+ }
+
+ pub fn fire_uncaptured_error(&self, error: webgpu::Error, can_gc: CanGc) {
+ let error = GPUError::from_error(&self.global(), error, can_gc);
+ let ev = GPUUncapturedErrorEvent::new(
+ &self.global(),
+ DOMString::from("uncapturederror"),
+ &GPUUncapturedErrorEventInit {
+ error,
+ parent: EventInit::empty(),
+ },
+ can_gc,
+ );
+ let _ = self.eventtarget.DispatchEvent(ev.event(), can_gc);
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-validate-texture-format-required-features>
+ ///
+ /// Validates that the device suppports required features,
+ /// and if so returns an ok containing wgpu's `TextureFormat`
+ pub fn validate_texture_format_required_features(
+ &self,
+ format: &GPUTextureFormat,
+ ) -> Fallible<TextureFormat> {
+ let texture_format: TextureFormat = (*format).into();
+ if self
+ .features
+ .wgpu_features()
+ .contains(texture_format.required_features())
+ {
+ Ok(texture_format)
+ } else {
+ Err(Error::Type(format!(
+ "{texture_format:?} is not supported by this GPUDevice"
+ )))
+ }
+ }
+
+ pub fn is_lost(&self) -> bool {
+ self.lost_promise.borrow().is_fulfilled()
+ }
+
+ pub fn get_pipeline_layout_data(
+ &self,
+ layout: &GPUPipelineLayoutOrGPUAutoLayoutMode,
+ ) -> PipelineLayout {
+ if let GPUPipelineLayoutOrGPUAutoLayoutMode::GPUPipelineLayout(ref layout) = layout {
+ PipelineLayout::Explicit(layout.id().0)
+ } else {
+ let layout_id = self.global().wgpu_id_hub().create_pipeline_layout_id();
+ let max_bind_grps = self.limits.MaxBindGroups();
+ let mut bgl_ids = Vec::with_capacity(max_bind_grps as usize);
+ for _ in 0..max_bind_grps {
+ let bgl = self.global().wgpu_id_hub().create_bind_group_layout_id();
+ bgl_ids.push(bgl);
+ }
+ PipelineLayout::Implicit(layout_id, bgl_ids)
+ }
+ }
+
+ pub fn parse_render_pipeline<'a>(
+ &self,
+ descriptor: &GPURenderPipelineDescriptor,
+ ) -> Fallible<(PipelineLayout, RenderPipelineDescriptor<'a>)> {
+ let pipeline_layout = self.get_pipeline_layout_data(&descriptor.parent.layout);
+
+ let desc = wgpu_pipe::RenderPipelineDescriptor {
+ label: (&descriptor.parent.parent).into(),
+ layout: pipeline_layout.explicit(),
+ cache: None,
+ vertex: wgpu_pipe::VertexState {
+ stage: (&descriptor.vertex.parent).into(),
+ buffers: Cow::Owned(
+ descriptor
+ .vertex
+ .buffers
+ .iter()
+ .map(|buffer| wgpu_pipe::VertexBufferLayout {
+ array_stride: buffer.arrayStride,
+ step_mode: match buffer.stepMode {
+ GPUVertexStepMode::Vertex => wgt::VertexStepMode::Vertex,
+ GPUVertexStepMode::Instance => wgt::VertexStepMode::Instance,
+ },
+ attributes: Cow::Owned(
+ buffer
+ .attributes
+ .iter()
+ .map(|att| wgt::VertexAttribute {
+ format: att.format.into(),
+ offset: att.offset,
+ shader_location: att.shaderLocation,
+ })
+ .collect::<Vec<_>>(),
+ ),
+ })
+ .collect::<Vec<_>>(),
+ ),
+ },
+ fragment: descriptor
+ .fragment
+ .as_ref()
+ .map(|stage| -> Fallible<wgpu_pipe::FragmentState> {
+ Ok(wgpu_pipe::FragmentState {
+ stage: (&stage.parent).into(),
+ targets: Cow::Owned(
+ stage
+ .targets
+ .iter()
+ .map(|state| {
+ self.validate_texture_format_required_features(&state.format)
+ .map(|format| {
+ Some(wgt::ColorTargetState {
+ format,
+ write_mask: wgt::ColorWrites::from_bits_retain(
+ state.writeMask,
+ ),
+ blend: state.blend.as_ref().map(|blend| {
+ wgt::BlendState {
+ color: (&blend.color).into(),
+ alpha: (&blend.alpha).into(),
+ }
+ }),
+ })
+ })
+ })
+ .collect::<Result<Vec<_>, _>>()?,
+ ),
+ })
+ })
+ .transpose()?,
+ primitive: (&descriptor.primitive).into(),
+ depth_stencil: descriptor
+ .depthStencil
+ .as_ref()
+ .map(|dss_desc| {
+ self.validate_texture_format_required_features(&dss_desc.format)
+ .map(|format| wgt::DepthStencilState {
+ format,
+ depth_write_enabled: dss_desc.depthWriteEnabled,
+ depth_compare: dss_desc.depthCompare.into(),
+ stencil: wgt::StencilState {
+ front: wgt::StencilFaceState {
+ compare: dss_desc.stencilFront.compare.into(),
+
+ fail_op: dss_desc.stencilFront.failOp.into(),
+ depth_fail_op: dss_desc.stencilFront.depthFailOp.into(),
+ pass_op: dss_desc.stencilFront.passOp.into(),
+ },
+ back: wgt::StencilFaceState {
+ compare: dss_desc.stencilBack.compare.into(),
+ fail_op: dss_desc.stencilBack.failOp.into(),
+ depth_fail_op: dss_desc.stencilBack.depthFailOp.into(),
+ pass_op: dss_desc.stencilBack.passOp.into(),
+ },
+ read_mask: dss_desc.stencilReadMask,
+ write_mask: dss_desc.stencilWriteMask,
+ },
+ bias: wgt::DepthBiasState {
+ constant: dss_desc.depthBias,
+ slope_scale: *dss_desc.depthBiasSlopeScale,
+ clamp: *dss_desc.depthBiasClamp,
+ },
+ })
+ })
+ .transpose()?,
+ multisample: wgt::MultisampleState {
+ count: descriptor.multisample.count,
+ mask: descriptor.multisample.mask as u64,
+ alpha_to_coverage_enabled: descriptor.multisample.alphaToCoverageEnabled,
+ },
+ multiview: None,
+ };
+ Ok((pipeline_layout, desc))
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#lose-the-device>
+ pub fn lose(&self, reason: GPUDeviceLostReason, msg: String) {
+ let lost_promise = &(*self.lost_promise.borrow());
+ let global = &self.global();
+ let lost = GPUDeviceLostInfo::new(global, msg.into(), reason);
+ lost_promise.resolve_native(&*lost);
+ }
+}
+
+impl GPUDeviceMethods<crate::DomTypeHolder> for GPUDevice {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-features>
+ fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
+ DomRoot::from_ref(&self.features)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-limits>
+ fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
+ DomRoot::from_ref(&self.limits)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-queue>
+ fn GetQueue(&self) -> DomRoot<GPUQueue> {
+ DomRoot::from_ref(&self.default_queue)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
+ fn Lost(&self) -> Rc<Promise> {
+ self.lost_promise.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer>
+ fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> Fallible<DomRoot<GPUBuffer>> {
+ GPUBuffer::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
+ #[allow(non_snake_case)]
+ fn CreateBindGroupLayout(
+ &self,
+ descriptor: &GPUBindGroupLayoutDescriptor,
+ ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
+ GPUBindGroupLayout::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
+ fn CreatePipelineLayout(
+ &self,
+ descriptor: &GPUPipelineLayoutDescriptor,
+ ) -> DomRoot<GPUPipelineLayout> {
+ GPUPipelineLayout::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup>
+ fn CreateBindGroup(&self, descriptor: &GPUBindGroupDescriptor) -> DomRoot<GPUBindGroup> {
+ GPUBindGroup::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
+ fn CreateShaderModule(
+ &self,
+ descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> DomRoot<GPUShaderModule> {
+ GPUShaderModule::create(self, descriptor, comp, can_gc)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline>
+ fn CreateComputePipeline(
+ &self,
+ descriptor: &GPUComputePipelineDescriptor,
+ ) -> DomRoot<GPUComputePipeline> {
+ let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
+ GPUComputePipeline::new(
+ &self.global(),
+ compute_pipeline,
+ descriptor.parent.parent.label.clone(),
+ self,
+ )
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipelineasync>
+ fn CreateComputePipelineAsync(
+ &self,
+ descriptor: &GPUComputePipelineDescriptor,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> Rc<Promise> {
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ let sender = response_async(&promise, self);
+ GPUComputePipeline::create(self, descriptor, Some(sender));
+ promise
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
+ fn CreateCommandEncoder(
+ &self,
+ descriptor: &GPUCommandEncoderDescriptor,
+ ) -> DomRoot<GPUCommandEncoder> {
+ GPUCommandEncoder::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
+ fn CreateTexture(&self, descriptor: &GPUTextureDescriptor) -> Fallible<DomRoot<GPUTexture>> {
+ GPUTexture::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler>
+ fn CreateSampler(&self, descriptor: &GPUSamplerDescriptor) -> DomRoot<GPUSampler> {
+ GPUSampler::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline>
+ fn CreateRenderPipeline(
+ &self,
+ descriptor: &GPURenderPipelineDescriptor,
+ ) -> Fallible<DomRoot<GPURenderPipeline>> {
+ let (pipeline_layout, desc) = self.parse_render_pipeline(descriptor)?;
+ let render_pipeline = GPURenderPipeline::create(self, pipeline_layout, desc, None)?;
+ Ok(GPURenderPipeline::new(
+ &self.global(),
+ render_pipeline,
+ descriptor.parent.parent.label.clone(),
+ self,
+ ))
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipelineasync>
+ fn CreateRenderPipelineAsync(
+ &self,
+ descriptor: &GPURenderPipelineDescriptor,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ let (implicit_ids, desc) = self.parse_render_pipeline(descriptor)?;
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ let sender = response_async(&promise, self);
+ GPURenderPipeline::create(self, implicit_ids, desc, Some(sender))?;
+ Ok(promise)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
+ fn CreateRenderBundleEncoder(
+ &self,
+ descriptor: &GPURenderBundleEncoderDescriptor,
+ ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
+ GPURenderBundleEncoder::create(self, descriptor)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
+ fn PushErrorScope(&self, filter: GPUErrorFilter) {
+ if self
+ .channel
+ .0
+ .send(WebGPURequest::PushErrorScope {
+ device_id: self.device.0,
+ filter: filter.as_webgpu(),
+ })
+ .is_err()
+ {
+ warn!("Failed sending WebGPURequest::PushErrorScope");
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope>
+ fn PopErrorScope(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ let sender = response_async(&promise, self);
+ if self
+ .channel
+ .0
+ .send(WebGPURequest::PopErrorScope {
+ device_id: self.device.0,
+ sender,
+ })
+ .is_err()
+ {
+ warn!("Error when sending WebGPURequest::PopErrorScope");
+ }
+ promise
+ }
+
+ // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
+ event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
+ fn Destroy(&self) {
+ if self.valid.get() {
+ self.valid.set(false);
+
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DestroyDevice(self.device.0))
+ {
+ warn!("Failed to send DestroyDevice ({:?}) ({})", self.device.0, e);
+ }
+ }
+ }
+}
+
+impl AsyncWGPUListener for GPUDevice {
+ fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>, can_gc: CanGc) {
+ match response {
+ WebGPUResponse::PoppedErrorScope(result) => match result {
+ Ok(None) | Err(PopError::Lost) => promise.resolve_native(&None::<Option<GPUError>>),
+ Err(PopError::Empty) => promise.reject_error(Error::Operation),
+ Ok(Some(error)) => {
+ let error = GPUError::from_error(&self.global(), error, can_gc);
+ promise.resolve_native(&error);
+ },
+ },
+ WebGPUResponse::ComputePipeline(result) => match result {
+ Ok(pipeline) => promise.resolve_native(&GPUComputePipeline::new(
+ &self.global(),
+ WebGPUComputePipeline(pipeline.id),
+ pipeline.label.into(),
+ self,
+ )),
+ Err(webgpu::Error::Validation(msg)) => {
+ promise.reject_native(&GPUPipelineError::new(
+ &self.global(),
+ msg.into(),
+ GPUPipelineErrorReason::Validation,
+ can_gc,
+ ))
+ },
+ Err(webgpu::Error::OutOfMemory(msg) | webgpu::Error::Internal(msg)) => promise
+ .reject_native(&GPUPipelineError::new(
+ &self.global(),
+ msg.into(),
+ GPUPipelineErrorReason::Internal,
+ can_gc,
+ )),
+ },
+ WebGPUResponse::RenderPipeline(result) => match result {
+ Ok(pipeline) => promise.resolve_native(&GPURenderPipeline::new(
+ &self.global(),
+ WebGPURenderPipeline(pipeline.id),
+ pipeline.label.into(),
+ self,
+ )),
+ Err(webgpu::Error::Validation(msg)) => {
+ promise.reject_native(&GPUPipelineError::new(
+ &self.global(),
+ msg.into(),
+ GPUPipelineErrorReason::Validation,
+ can_gc,
+ ))
+ },
+ Err(webgpu::Error::OutOfMemory(msg) | webgpu::Error::Internal(msg)) => promise
+ .reject_native(&GPUPipelineError::new(
+ &self.global(),
+ msg.into(),
+ GPUPipelineErrorReason::Internal,
+ can_gc,
+ )),
+ },
+ _ => unreachable!("Wrong response received on AsyncWGPUListener for GPUDevice"),
+ }
+ }
+}
+
+impl Drop for GPUDevice {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropDevice(self.device.0))
+ {
+ warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpudevicelostinfo.rs b/components/script/dom/webgpu/gpudevicelostinfo.rs
new file mode 100644
index 00000000000..ea33986a84f
--- /dev/null
+++ b/components/script/dom/webgpu/gpudevicelostinfo.rs
@@ -0,0 +1,55 @@
+/* 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/. */
+
+#![allow(dead_code)]
+
+use dom_struct::dom_struct;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUDeviceLostInfoMethods, GPUDeviceLostReason,
+};
+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;
+
+#[dom_struct]
+pub struct GPUDeviceLostInfo {
+ reflector_: Reflector,
+ message: DOMString,
+ reason: GPUDeviceLostReason,
+}
+
+impl GPUDeviceLostInfo {
+ fn new_inherited(message: DOMString, reason: GPUDeviceLostReason) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ message,
+ reason,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ message: DOMString,
+ reason: GPUDeviceLostReason,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUDeviceLostInfo::new_inherited(message, reason)),
+ global,
+ )
+ }
+}
+
+impl GPUDeviceLostInfoMethods<crate::DomTypeHolder> for GPUDeviceLostInfo {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevicelostinfo-message>
+ fn Message(&self) -> DOMString {
+ self.message.clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevicelostinfo-reason>
+ fn Reason(&self) -> GPUDeviceLostReason {
+ self.reason
+ }
+}
diff --git a/components/script/dom/webgpu/gpuerror.rs b/components/script/dom/webgpu/gpuerror.rs
new file mode 100644
index 00000000000..a4de08cc18b
--- /dev/null
+++ b/components/script/dom/webgpu/gpuerror.rs
@@ -0,0 +1,100 @@
+/* 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 dom_struct::dom_struct;
+use js::rust::HandleObject;
+use webgpu::{Error, ErrorFilter};
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{GPUErrorFilter, GPUErrorMethods};
+use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::types::{GPUInternalError, GPUOutOfMemoryError, GPUValidationError};
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUError {
+ reflector_: Reflector,
+ message: DOMString,
+}
+
+impl GPUError {
+ pub fn new_inherited(message: DOMString) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ message,
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn new(global: &GlobalScope, message: DOMString, can_gc: CanGc) -> DomRoot<Self> {
+ Self::new_with_proto(global, None, message, can_gc)
+ }
+
+ #[allow(dead_code)]
+ pub fn new_with_proto(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ message: DOMString,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ reflect_dom_object_with_proto(
+ Box::new(GPUError::new_inherited(message)),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+
+ pub fn from_error(global: &GlobalScope, error: Error, can_gc: CanGc) -> DomRoot<Self> {
+ match error {
+ Error::Validation(msg) => DomRoot::upcast(GPUValidationError::new_with_proto(
+ global,
+ None,
+ DOMString::from_string(msg),
+ can_gc,
+ )),
+ Error::OutOfMemory(msg) => DomRoot::upcast(GPUOutOfMemoryError::new_with_proto(
+ global,
+ None,
+ DOMString::from_string(msg),
+ can_gc,
+ )),
+ Error::Internal(msg) => DomRoot::upcast(GPUInternalError::new_with_proto(
+ global,
+ None,
+ DOMString::from_string(msg),
+ can_gc,
+ )),
+ }
+ }
+}
+
+impl GPUErrorMethods<crate::DomTypeHolder> for GPUError {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuerror-message>
+ fn Message(&self) -> DOMString {
+ self.message.clone()
+ }
+}
+
+impl From<ErrorFilter> for GPUErrorFilter {
+ fn from(filter: ErrorFilter) -> Self {
+ match filter {
+ ErrorFilter::Validation => GPUErrorFilter::Validation,
+ ErrorFilter::OutOfMemory => GPUErrorFilter::Out_of_memory,
+ ErrorFilter::Internal => GPUErrorFilter::Internal,
+ }
+ }
+}
+
+impl GPUErrorFilter {
+ pub fn as_webgpu(&self) -> ErrorFilter {
+ match self {
+ GPUErrorFilter::Validation => ErrorFilter::Validation,
+ GPUErrorFilter::Out_of_memory => ErrorFilter::OutOfMemory,
+ GPUErrorFilter::Internal => ErrorFilter::Internal,
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpuinternalerror.rs b/components/script/dom/webgpu/gpuinternalerror.rs
new file mode 100644
index 00000000000..c622d4ff495
--- /dev/null
+++ b/components/script/dom/webgpu/gpuinternalerror.rs
@@ -0,0 +1,53 @@
+/* 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 dom_struct::dom_struct;
+use js::rust::HandleObject;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUInternalError_Binding::GPUInternalErrorMethods;
+use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::types::GPUError;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUInternalError {
+ gpu_error: GPUError,
+}
+
+impl GPUInternalError {
+ fn new_inherited(message: DOMString) -> Self {
+ Self {
+ gpu_error: GPUError::new_inherited(message),
+ }
+ }
+
+ pub fn new_with_proto(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ message: DOMString,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ reflect_dom_object_with_proto(
+ Box::new(Self::new_inherited(message)),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+}
+
+impl GPUInternalErrorMethods<crate::DomTypeHolder> for GPUInternalError {
+ /// <https://gpuweb.github.io/gpuweb/#dom-GPUInternalError-GPUInternalError>
+ fn Constructor(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ message: DOMString,
+ ) -> DomRoot<Self> {
+ Self::new_with_proto(global, proto, message, can_gc)
+ }
+}
diff --git a/components/script/dom/webgpu/gpumapmode.rs b/components/script/dom/webgpu/gpumapmode.rs
new file mode 100644
index 00000000000..4db3455483d
--- /dev/null
+++ b/components/script/dom/webgpu/gpumapmode.rs
@@ -0,0 +1,12 @@
+/* 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 dom_struct::dom_struct;
+
+use crate::dom::bindings::reflector::Reflector;
+
+#[dom_struct]
+pub struct GPUMapMode {
+ reflector_: Reflector,
+}
diff --git a/components/script/dom/webgpu/gpuoutofmemoryerror.rs b/components/script/dom/webgpu/gpuoutofmemoryerror.rs
new file mode 100644
index 00000000000..01c77ef8185
--- /dev/null
+++ b/components/script/dom/webgpu/gpuoutofmemoryerror.rs
@@ -0,0 +1,53 @@
+/* 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 dom_struct::dom_struct;
+use js::rust::HandleObject;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUOutOfMemoryError_Binding::GPUOutOfMemoryErrorMethods;
+use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::types::GPUError;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUOutOfMemoryError {
+ gpu_error: GPUError,
+}
+
+impl GPUOutOfMemoryError {
+ fn new_inherited(message: DOMString) -> Self {
+ Self {
+ gpu_error: GPUError::new_inherited(message),
+ }
+ }
+
+ pub fn new_with_proto(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ message: DOMString,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ reflect_dom_object_with_proto(
+ Box::new(Self::new_inherited(message)),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+}
+
+impl GPUOutOfMemoryErrorMethods<crate::DomTypeHolder> for GPUOutOfMemoryError {
+ /// <https://gpuweb.github.io/gpuweb/#dom-GPUOutOfMemoryError-GPUOutOfMemoryError>
+ fn Constructor(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ message: DOMString,
+ ) -> DomRoot<Self> {
+ Self::new_with_proto(global, proto, message, can_gc)
+ }
+}
diff --git a/components/script/dom/webgpu/gpupipelineerror.rs b/components/script/dom/webgpu/gpupipelineerror.rs
new file mode 100644
index 00000000000..ae2b57bc781
--- /dev/null
+++ b/components/script/dom/webgpu/gpupipelineerror.rs
@@ -0,0 +1,74 @@
+/* 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 dom_struct::dom_struct;
+use js::rust::HandleObject;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUPipelineErrorInit, GPUPipelineErrorMethods, GPUPipelineErrorReason,
+};
+use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::domexception::DOMException;
+use crate::dom::globalscope::GlobalScope;
+use crate::script_runtime::CanGc;
+
+/// <https://gpuweb.github.io/gpuweb/#gpupipelineerror>
+#[dom_struct]
+pub struct GPUPipelineError {
+ exception: DOMException,
+ reason: GPUPipelineErrorReason,
+}
+
+impl GPUPipelineError {
+ fn new_inherited(message: DOMString, reason: GPUPipelineErrorReason) -> Self {
+ Self {
+ exception: DOMException::new_inherited(message, "GPUPipelineError".into()),
+ reason,
+ }
+ }
+
+ pub fn new_with_proto(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ message: DOMString,
+ reason: GPUPipelineErrorReason,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ reflect_dom_object_with_proto(
+ Box::new(Self::new_inherited(message, reason)),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ message: DOMString,
+ reason: GPUPipelineErrorReason,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ Self::new_with_proto(global, None, message, reason, can_gc)
+ }
+}
+
+impl GPUPipelineErrorMethods<crate::DomTypeHolder> for GPUPipelineError {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpupipelineerror-constructor>
+ fn Constructor(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ message: DOMString,
+ options: &GPUPipelineErrorInit,
+ ) -> DomRoot<Self> {
+ Self::new_with_proto(global, proto, message, options.reason, can_gc)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpupipelineerror-reason>
+ fn Reason(&self) -> GPUPipelineErrorReason {
+ self.reason
+ }
+}
diff --git a/components/script/dom/webgpu/gpupipelinelayout.rs b/components/script/dom/webgpu/gpupipelinelayout.rs
new file mode 100644
index 00000000000..c1cbbdb72ab
--- /dev/null
+++ b/components/script/dom/webgpu/gpupipelinelayout.rs
@@ -0,0 +1,142 @@
+/* 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 std::borrow::Cow;
+
+use dom_struct::dom_struct;
+use webgpu::wgc::binding_model::PipelineLayoutDescriptor;
+use webgpu::{WebGPU, WebGPUBindGroupLayout, WebGPUPipelineLayout, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUPipelineLayoutDescriptor, GPUPipelineLayoutMethods,
+};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpudevice::GPUDevice;
+
+#[dom_struct]
+pub struct GPUPipelineLayout {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ pipeline_layout: WebGPUPipelineLayout,
+ #[no_trace]
+ bind_group_layouts: Vec<WebGPUBindGroupLayout>,
+}
+
+impl GPUPipelineLayout {
+ fn new_inherited(
+ channel: WebGPU,
+ pipeline_layout: WebGPUPipelineLayout,
+ label: USVString,
+ bgls: Vec<WebGPUBindGroupLayout>,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ label: DomRefCell::new(label),
+ pipeline_layout,
+ bind_group_layouts: bgls,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ pipeline_layout: WebGPUPipelineLayout,
+ label: USVString,
+ bgls: Vec<WebGPUBindGroupLayout>,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUPipelineLayout::new_inherited(
+ channel,
+ pipeline_layout,
+ label,
+ bgls,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUPipelineLayout {
+ pub fn id(&self) -> WebGPUPipelineLayout {
+ self.pipeline_layout
+ }
+
+ pub fn bind_group_layouts(&self) -> Vec<WebGPUBindGroupLayout> {
+ self.bind_group_layouts.clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPUPipelineLayoutDescriptor,
+ ) -> DomRoot<GPUPipelineLayout> {
+ let bgls = descriptor
+ .bindGroupLayouts
+ .iter()
+ .map(|each| each.id())
+ .collect::<Vec<_>>();
+
+ let desc = PipelineLayoutDescriptor {
+ label: (&descriptor.parent).into(),
+ bind_group_layouts: Cow::Owned(bgls.iter().map(|l| l.0).collect::<Vec<_>>()),
+ push_constant_ranges: Cow::Owned(vec![]),
+ };
+
+ let pipeline_layout_id = device.global().wgpu_id_hub().create_pipeline_layout_id();
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreatePipelineLayout {
+ device_id: device.id().0,
+ pipeline_layout_id,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU PipelineLayout");
+
+ let pipeline_layout = WebGPUPipelineLayout(pipeline_layout_id);
+ GPUPipelineLayout::new(
+ &device.global(),
+ device.channel().clone(),
+ pipeline_layout,
+ descriptor.parent.label.clone(),
+ bgls,
+ )
+ }
+}
+
+impl GPUPipelineLayoutMethods<crate::DomTypeHolder> for GPUPipelineLayout {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+}
+
+impl Drop for GPUPipelineLayout {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropPipelineLayout(self.pipeline_layout.0))
+ {
+ warn!(
+ "Failed to send DropPipelineLayout ({:?}) ({})",
+ self.pipeline_layout.0, e
+ );
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpuqueryset.rs b/components/script/dom/webgpu/gpuqueryset.rs
new file mode 100644
index 00000000000..d2c3eb03336
--- /dev/null
+++ b/components/script/dom/webgpu/gpuqueryset.rs
@@ -0,0 +1,35 @@
+/* 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/. */
+
+#![allow(dead_code)] // this file is stub
+
+use dom_struct::dom_struct;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUQuerySetMethods;
+use crate::dom::bindings::reflector::Reflector;
+use crate::dom::bindings::str::USVString;
+
+#[dom_struct]
+pub struct GPUQuerySet {
+ reflector_: Reflector,
+ // #[ignore_malloc_size_of = "defined in wgpu-types"]
+}
+
+// TODO: wgpu does not expose right fields right now
+impl GPUQuerySetMethods<crate::DomTypeHolder> for GPUQuerySet {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuqueryset-destroy>
+ fn Destroy(&self) {
+ todo!()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ todo!()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, _value: USVString) {
+ todo!()
+ }
+}
diff --git a/components/script/dom/webgpu/gpuqueue.rs b/components/script/dom/webgpu/gpuqueue.rs
new file mode 100644
index 00000000000..403628737d3
--- /dev/null
+++ b/components/script/dom/webgpu/gpuqueue.rs
@@ -0,0 +1,225 @@
+/* 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 std::rc::Rc;
+
+use dom_struct::dom_struct;
+use ipc_channel::ipc::IpcSharedMemory;
+use webgpu::{wgt, WebGPU, WebGPUQueue, WebGPURequest, WebGPUResponse};
+
+use super::gpu::{response_async, AsyncWGPUListener};
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUExtent3D, GPUImageCopyTexture, GPUImageDataLayout, GPUQueueMethods, GPUSize64,
+};
+use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer as BufferSource;
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubuffer::GPUBuffer;
+use crate::dom::gpucommandbuffer::GPUCommandBuffer;
+use crate::dom::gpudevice::GPUDevice;
+use crate::dom::promise::Promise;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUQueue {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ device: DomRefCell<Option<Dom<GPUDevice>>>,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ queue: WebGPUQueue,
+}
+
+impl GPUQueue {
+ fn new_inherited(channel: WebGPU, queue: WebGPUQueue) -> Self {
+ GPUQueue {
+ channel,
+ reflector_: Reflector::new(),
+ device: DomRefCell::new(None),
+ label: DomRefCell::new(USVString::default()),
+ queue,
+ }
+ }
+
+ pub fn new(global: &GlobalScope, channel: WebGPU, queue: WebGPUQueue) -> DomRoot<Self> {
+ reflect_dom_object(Box::new(GPUQueue::new_inherited(channel, queue)), global)
+ }
+}
+
+impl GPUQueue {
+ pub fn set_device(&self, device: &GPUDevice) {
+ *self.device.borrow_mut() = Some(Dom::from_ref(device));
+ }
+
+ pub fn id(&self) -> WebGPUQueue {
+ self.queue
+ }
+}
+
+impl GPUQueueMethods<crate::DomTypeHolder> for GPUQueue {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuqueue-submit>
+ fn Submit(&self, command_buffers: Vec<DomRoot<GPUCommandBuffer>>) {
+ let command_buffers = command_buffers.iter().map(|cb| cb.id().0).collect();
+ self.channel
+ .0
+ .send(WebGPURequest::Submit {
+ device_id: self.device.borrow().as_ref().unwrap().id().0,
+ queue_id: self.queue.0,
+ command_buffers,
+ })
+ .unwrap();
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuqueue-writebuffer>
+ #[allow(unsafe_code)]
+ fn WriteBuffer(
+ &self,
+ buffer: &GPUBuffer,
+ buffer_offset: GPUSize64,
+ data: BufferSource,
+ data_offset: GPUSize64,
+ size: Option<GPUSize64>,
+ ) -> Fallible<()> {
+ // Step 1
+ let sizeof_element: usize = match data {
+ BufferSource::ArrayBufferView(ref d) => d.get_array_type().byte_size().unwrap_or(1),
+ BufferSource::ArrayBuffer(_) => 1,
+ };
+ let data = match data {
+ BufferSource::ArrayBufferView(d) => d.to_vec(),
+ BufferSource::ArrayBuffer(d) => d.to_vec(),
+ };
+ // Step 2
+ let data_size: usize = data.len() / sizeof_element;
+ debug_assert_eq!(data.len() % sizeof_element, 0);
+ // Step 3
+ let content_size = if let Some(s) = size {
+ s
+ } else {
+ (data_size as GPUSize64)
+ .checked_sub(data_offset)
+ .ok_or(Error::Operation)?
+ };
+
+ // Step 4
+ let valid = data_offset + content_size <= data_size as u64 &&
+ content_size * sizeof_element as u64 % wgt::COPY_BUFFER_ALIGNMENT == 0;
+ if !valid {
+ return Err(Error::Operation);
+ }
+
+ // Step 5&6
+ let contents = IpcSharedMemory::from_bytes(
+ &data[(data_offset as usize) * sizeof_element..
+ ((data_offset + content_size) as usize) * sizeof_element],
+ );
+ if let Err(e) = self.channel.0.send(WebGPURequest::WriteBuffer {
+ device_id: self.device.borrow().as_ref().unwrap().id().0,
+ queue_id: self.queue.0,
+ buffer_id: buffer.id().0,
+ buffer_offset,
+ data: contents,
+ }) {
+ warn!("Failed to send WriteBuffer({:?}) ({})", buffer.id(), e);
+ return Err(Error::Operation);
+ }
+
+ Ok(())
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuqueue-writetexture>
+ fn WriteTexture(
+ &self,
+ destination: &GPUImageCopyTexture,
+ data: BufferSource,
+ data_layout: &GPUImageDataLayout,
+ size: GPUExtent3D,
+ ) -> Fallible<()> {
+ let (bytes, len) = match data {
+ BufferSource::ArrayBufferView(d) => (d.to_vec(), d.len() as u64),
+ BufferSource::ArrayBuffer(d) => (d.to_vec(), d.len() as u64),
+ };
+ let valid = data_layout.offset <= len;
+
+ if !valid {
+ return Err(Error::Operation);
+ }
+
+ let texture_cv = destination.try_into()?;
+ let texture_layout = data_layout.into();
+ let write_size = (&size).try_into()?;
+ let final_data = IpcSharedMemory::from_bytes(&bytes);
+
+ if let Err(e) = self.channel.0.send(WebGPURequest::WriteTexture {
+ device_id: self.device.borrow().as_ref().unwrap().id().0,
+ queue_id: self.queue.0,
+ texture_cv,
+ data_layout: texture_layout,
+ size: write_size,
+ data: final_data,
+ }) {
+ warn!(
+ "Failed to send WriteTexture({:?}) ({})",
+ destination.texture.id().0,
+ e
+ );
+ return Err(Error::Operation);
+ }
+
+ Ok(())
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuqueue-onsubmittedworkdone>
+ fn OnSubmittedWorkDone(&self, can_gc: CanGc) -> Rc<Promise> {
+ let global = self.global();
+ let promise = Promise::new(&global, can_gc);
+ let sender = response_async(&promise, self);
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::QueueOnSubmittedWorkDone {
+ sender,
+ queue_id: self.queue.0,
+ })
+ {
+ warn!("QueueOnSubmittedWorkDone failed with {e}")
+ }
+ promise
+ }
+}
+
+impl AsyncWGPUListener for GPUQueue {
+ fn handle_response(
+ &self,
+ response: webgpu::WebGPUResponse,
+ promise: &Rc<Promise>,
+ _can_gc: CanGc,
+ ) {
+ match response {
+ WebGPUResponse::SubmittedWorkDone => {
+ promise.resolve_native(&());
+ },
+ _ => {
+ warn!("GPUQueue received wrong WebGPUResponse");
+ promise.reject_error(Error::Operation);
+ },
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpurenderbundle.rs b/components/script/dom/webgpu/gpurenderbundle.rs
new file mode 100644
index 00000000000..aae120b970b
--- /dev/null
+++ b/components/script/dom/webgpu/gpurenderbundle.rs
@@ -0,0 +1,94 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::{WebGPU, WebGPUDevice, WebGPURenderBundle, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPURenderBundleMethods;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+
+#[dom_struct]
+pub struct GPURenderBundle {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ #[no_trace]
+ device: WebGPUDevice,
+ #[no_trace]
+ render_bundle: WebGPURenderBundle,
+ label: DomRefCell<USVString>,
+}
+
+impl GPURenderBundle {
+ fn new_inherited(
+ render_bundle: WebGPURenderBundle,
+ device: WebGPUDevice,
+ channel: WebGPU,
+ label: USVString,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ render_bundle,
+ device,
+ channel,
+ label: DomRefCell::new(label),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ render_bundle: WebGPURenderBundle,
+ device: WebGPUDevice,
+ channel: WebGPU,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPURenderBundle::new_inherited(
+ render_bundle,
+ device,
+ channel,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPURenderBundle {
+ pub fn id(&self) -> WebGPURenderBundle {
+ self.render_bundle
+ }
+}
+
+impl GPURenderBundleMethods<crate::DomTypeHolder> for GPURenderBundle {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+}
+
+impl Drop for GPURenderBundle {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropRenderBundle(self.render_bundle.0))
+ {
+ warn!(
+ "Failed to send DropRenderBundle ({:?}) ({})",
+ self.render_bundle.0, e
+ );
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpurenderbundleencoder.rs b/components/script/dom/webgpu/gpurenderbundleencoder.rs
new file mode 100644
index 00000000000..3f23db60706
--- /dev/null
+++ b/components/script/dom/webgpu/gpurenderbundleencoder.rs
@@ -0,0 +1,279 @@
+/* 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 std::borrow::Cow;
+
+use dom_struct::dom_struct;
+use webgpu::wgc::command::{
+ bundle_ffi as wgpu_bundle, RenderBundleEncoder, RenderBundleEncoderDescriptor,
+};
+use webgpu::{wgt, WebGPU, WebGPURenderBundle, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUIndexFormat, GPURenderBundleDescriptor, GPURenderBundleEncoderDescriptor,
+ GPURenderBundleEncoderMethods,
+};
+use crate::dom::bindings::import::module::Fallible;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubindgroup::GPUBindGroup;
+use crate::dom::gpubuffer::GPUBuffer;
+use crate::dom::gpudevice::GPUDevice;
+use crate::dom::gpurenderbundle::GPURenderBundle;
+use crate::dom::gpurenderpipeline::GPURenderPipeline;
+
+#[dom_struct]
+pub struct GPURenderBundleEncoder {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ device: Dom<GPUDevice>,
+ #[ignore_malloc_size_of = "defined in wgpu-core"]
+ #[no_trace]
+ render_bundle_encoder: DomRefCell<Option<RenderBundleEncoder>>,
+ label: DomRefCell<USVString>,
+}
+
+impl GPURenderBundleEncoder {
+ fn new_inherited(
+ render_bundle_encoder: RenderBundleEncoder,
+ device: &GPUDevice,
+ channel: WebGPU,
+ label: USVString,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ render_bundle_encoder: DomRefCell::new(Some(render_bundle_encoder)),
+ device: Dom::from_ref(device),
+ channel,
+ label: DomRefCell::new(label),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ render_bundle_encoder: RenderBundleEncoder,
+ device: &GPUDevice,
+ channel: WebGPU,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPURenderBundleEncoder::new_inherited(
+ render_bundle_encoder,
+ device,
+ channel,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPURenderBundleEncoder {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPURenderBundleEncoderDescriptor,
+ ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
+ let desc = RenderBundleEncoderDescriptor {
+ label: (&descriptor.parent.parent).into(),
+ color_formats: Cow::Owned(
+ descriptor
+ .parent
+ .colorFormats
+ .iter()
+ .map(|format| {
+ device
+ .validate_texture_format_required_features(format)
+ .map(Some)
+ })
+ .collect::<Fallible<Vec<_>>>()?,
+ ),
+ depth_stencil: descriptor
+ .parent
+ .depthStencilFormat
+ .map(|dsf| {
+ device
+ .validate_texture_format_required_features(&dsf)
+ .map(|format| wgt::RenderBundleDepthStencil {
+ format,
+ depth_read_only: descriptor.depthReadOnly,
+ stencil_read_only: descriptor.stencilReadOnly,
+ })
+ })
+ .transpose()?,
+ sample_count: descriptor.parent.sampleCount,
+ multiview: None,
+ };
+
+ // Handle error gracefully
+ let render_bundle_encoder = RenderBundleEncoder::new(&desc, device.id().0, None).unwrap();
+
+ Ok(GPURenderBundleEncoder::new(
+ &device.global(),
+ render_bundle_encoder,
+ device,
+ device.channel().clone(),
+ descriptor.parent.parent.label.clone(),
+ ))
+ }
+}
+
+impl GPURenderBundleEncoderMethods<crate::DomTypeHolder> for GPURenderBundleEncoder {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup>
+ #[allow(unsafe_code)]
+ fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup, dynamic_offsets: Vec<u32>) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ unsafe {
+ wgpu_bundle::wgpu_render_bundle_set_bind_group(
+ encoder,
+ index,
+ Some(bind_group.id().0),
+ dynamic_offsets.as_ptr(),
+ dynamic_offsets.len(),
+ )
+ };
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setpipeline>
+ fn SetPipeline(&self, pipeline: &GPURenderPipeline) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ wgpu_bundle::wgpu_render_bundle_set_pipeline(encoder, pipeline.id().0);
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setindexbuffer>
+ fn SetIndexBuffer(
+ &self,
+ buffer: &GPUBuffer,
+ index_format: GPUIndexFormat,
+ offset: u64,
+ size: u64,
+ ) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ wgpu_bundle::wgpu_render_bundle_set_index_buffer(
+ encoder,
+ buffer.id().0,
+ match index_format {
+ GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16,
+ GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32,
+ },
+ offset,
+ wgt::BufferSize::new(size),
+ );
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setvertexbuffer>
+ fn SetVertexBuffer(&self, slot: u32, buffer: &GPUBuffer, offset: u64, size: u64) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ wgpu_bundle::wgpu_render_bundle_set_vertex_buffer(
+ encoder,
+ slot,
+ buffer.id().0,
+ offset,
+ wgt::BufferSize::new(size),
+ );
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-draw>
+ fn Draw(&self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ wgpu_bundle::wgpu_render_bundle_draw(
+ encoder,
+ vertex_count,
+ instance_count,
+ first_vertex,
+ first_instance,
+ );
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexed>
+ fn DrawIndexed(
+ &self,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+ ) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ wgpu_bundle::wgpu_render_bundle_draw_indexed(
+ encoder,
+ index_count,
+ instance_count,
+ first_index,
+ base_vertex,
+ first_instance,
+ );
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindirect>
+ fn DrawIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ wgpu_bundle::wgpu_render_bundle_draw_indirect(
+ encoder,
+ indirect_buffer.id().0,
+ indirect_offset,
+ );
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexedindirect>
+ fn DrawIndexedIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
+ if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
+ wgpu_bundle::wgpu_render_bundle_draw_indexed_indirect(
+ encoder,
+ indirect_buffer.id().0,
+ indirect_offset,
+ );
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderbundleencoder-finish>
+ fn Finish(&self, descriptor: &GPURenderBundleDescriptor) -> DomRoot<GPURenderBundle> {
+ let desc = wgt::RenderBundleDescriptor {
+ label: (&descriptor.parent).into(),
+ };
+ let encoder = self.render_bundle_encoder.borrow_mut().take().unwrap();
+ let render_bundle_id = self.global().wgpu_id_hub().create_render_bundle_id();
+
+ self.channel
+ .0
+ .send(WebGPURequest::RenderBundleEncoderFinish {
+ render_bundle_encoder: encoder,
+ descriptor: desc,
+ render_bundle_id,
+ device_id: self.device.id().0,
+ })
+ .expect("Failed to send RenderBundleEncoderFinish");
+
+ let render_bundle = WebGPURenderBundle(render_bundle_id);
+ GPURenderBundle::new(
+ &self.global(),
+ render_bundle,
+ self.device.id(),
+ self.channel.clone(),
+ descriptor.parent.label.clone(),
+ )
+ }
+}
diff --git a/components/script/dom/webgpu/gpurenderpassencoder.rs b/components/script/dom/webgpu/gpurenderpassencoder.rs
new file mode 100644
index 00000000000..ea77df69d02
--- /dev/null
+++ b/components/script/dom/webgpu/gpurenderpassencoder.rs
@@ -0,0 +1,249 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::{wgt, RenderCommand, WebGPU, WebGPURenderPass, WebGPURequest};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUColor, GPUIndexFormat, GPURenderPassEncoderMethods,
+};
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::num::Finite;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubindgroup::GPUBindGroup;
+use crate::dom::gpubuffer::GPUBuffer;
+use crate::dom::gpucommandencoder::GPUCommandEncoder;
+use crate::dom::gpurenderbundle::GPURenderBundle;
+use crate::dom::gpurenderpipeline::GPURenderPipeline;
+
+#[dom_struct]
+pub struct GPURenderPassEncoder {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ render_pass: WebGPURenderPass,
+ command_encoder: Dom<GPUCommandEncoder>,
+}
+
+impl GPURenderPassEncoder {
+ fn new_inherited(
+ channel: WebGPU,
+ render_pass: WebGPURenderPass,
+ parent: &GPUCommandEncoder,
+ label: USVString,
+ ) -> Self {
+ Self {
+ channel,
+ reflector_: Reflector::new(),
+ label: DomRefCell::new(label),
+ render_pass,
+ command_encoder: Dom::from_ref(parent),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ render_pass: WebGPURenderPass,
+ parent: &GPUCommandEncoder,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPURenderPassEncoder::new_inherited(
+ channel,
+ render_pass,
+ parent,
+ label,
+ )),
+ global,
+ )
+ }
+
+ fn send_render_command(&self, render_command: RenderCommand) {
+ if let Err(e) = self.channel.0.send(WebGPURequest::RenderPassCommand {
+ render_pass_id: self.render_pass.0,
+ render_command,
+ device_id: self.command_encoder.device_id().0,
+ }) {
+ warn!("Error sending WebGPURequest::RenderPassCommand: {e:?}")
+ }
+ }
+}
+
+impl GPURenderPassEncoderMethods<crate::DomTypeHolder> for GPURenderPassEncoder {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup>
+ fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup, offsets: Vec<u32>) {
+ self.send_render_command(RenderCommand::SetBindGroup {
+ index,
+ bind_group_id: bind_group.id().0,
+ offsets,
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setviewport>
+ fn SetViewport(
+ &self,
+ x: Finite<f32>,
+ y: Finite<f32>,
+ width: Finite<f32>,
+ height: Finite<f32>,
+ min_depth: Finite<f32>,
+ max_depth: Finite<f32>,
+ ) {
+ self.send_render_command(RenderCommand::SetViewport {
+ x: *x,
+ y: *y,
+ width: *width,
+ height: *height,
+ min_depth: *min_depth,
+ max_depth: *max_depth,
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setscissorrect>
+ fn SetScissorRect(&self, x: u32, y: u32, width: u32, height: u32) {
+ self.send_render_command(RenderCommand::SetScissorRect {
+ x,
+ y,
+ width,
+ height,
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setblendcolor>
+ fn SetBlendConstant(&self, color: GPUColor) -> Fallible<()> {
+ self.send_render_command(RenderCommand::SetBlendConstant((&color).try_into()?));
+ Ok(())
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setstencilreference>
+ fn SetStencilReference(&self, reference: u32) {
+ self.send_render_command(RenderCommand::SetStencilReference(reference))
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-end>
+ fn End(&self) {
+ if let Err(e) = self.channel.0.send(WebGPURequest::EndRenderPass {
+ render_pass_id: self.render_pass.0,
+ device_id: self.command_encoder.device_id().0,
+ command_encoder_id: self.command_encoder.id().0,
+ }) {
+ warn!("Failed to send WebGPURequest::EndRenderPass: {e:?}");
+ }
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setpipeline>
+ fn SetPipeline(&self, pipeline: &GPURenderPipeline) {
+ self.send_render_command(RenderCommand::SetPipeline(pipeline.id().0))
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-setindexbuffer>
+ fn SetIndexBuffer(
+ &self,
+ buffer: &GPUBuffer,
+ index_format: GPUIndexFormat,
+ offset: u64,
+ size: u64,
+ ) {
+ self.send_render_command(RenderCommand::SetIndexBuffer {
+ buffer_id: buffer.id().0,
+ index_format: match index_format {
+ GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16,
+ GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32,
+ },
+ offset,
+ size: wgt::BufferSize::new(size),
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setvertexbuffer>
+ fn SetVertexBuffer(&self, slot: u32, buffer: &GPUBuffer, offset: u64, size: u64) {
+ self.send_render_command(RenderCommand::SetVertexBuffer {
+ slot,
+ buffer_id: buffer.id().0,
+ offset,
+ size: wgt::BufferSize::new(size),
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-draw>
+ fn Draw(&self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) {
+ self.send_render_command(RenderCommand::Draw {
+ vertex_count,
+ instance_count,
+ first_vertex,
+ first_instance,
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexed>
+ fn DrawIndexed(
+ &self,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+ ) {
+ self.send_render_command(RenderCommand::DrawIndexed {
+ index_count,
+ instance_count,
+ first_index,
+ base_vertex,
+ first_instance,
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindirect>
+ fn DrawIndirect(&self, buffer: &GPUBuffer, offset: u64) {
+ self.send_render_command(RenderCommand::DrawIndirect {
+ buffer_id: buffer.id().0,
+ offset,
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexedindirect>
+ fn DrawIndexedIndirect(&self, buffer: &GPUBuffer, offset: u64) {
+ self.send_render_command(RenderCommand::DrawIndexedIndirect {
+ buffer_id: buffer.id().0,
+ offset,
+ })
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-executebundles>
+ #[allow(unsafe_code)]
+ fn ExecuteBundles(&self, bundles: Vec<DomRoot<GPURenderBundle>>) {
+ let bundle_ids: Vec<_> = bundles.iter().map(|b| b.id().0).collect();
+ self.send_render_command(RenderCommand::ExecuteBundles(bundle_ids))
+ }
+}
+
+impl Drop for GPURenderPassEncoder {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropRenderPass(self.render_pass.0))
+ {
+ warn!("Failed to send WebGPURequest::DropRenderPass with {e:?}");
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpurenderpipeline.rs b/components/script/dom/webgpu/gpurenderpipeline.rs
new file mode 100644
index 00000000000..0cf8754ecbf
--- /dev/null
+++ b/components/script/dom/webgpu/gpurenderpipeline.rs
@@ -0,0 +1,144 @@
+/* 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 dom_struct::dom_struct;
+use ipc_channel::ipc::IpcSender;
+use webgpu::wgc::pipeline::RenderPipelineDescriptor;
+use webgpu::{WebGPU, WebGPUBindGroupLayout, WebGPURenderPipeline, WebGPURequest, WebGPUResponse};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPURenderPipelineMethods;
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
+use crate::dom::gpudevice::{GPUDevice, PipelineLayout};
+
+#[dom_struct]
+pub struct GPURenderPipeline {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ render_pipeline: WebGPURenderPipeline,
+ device: Dom<GPUDevice>,
+}
+
+impl GPURenderPipeline {
+ fn new_inherited(
+ render_pipeline: WebGPURenderPipeline,
+ label: USVString,
+ device: &GPUDevice,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel: device.channel(),
+ label: DomRefCell::new(label),
+ render_pipeline,
+ device: Dom::from_ref(device),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ render_pipeline: WebGPURenderPipeline,
+ label: USVString,
+ device: &GPUDevice,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPURenderPipeline::new_inherited(
+ render_pipeline,
+ label,
+ device,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPURenderPipeline {
+ pub fn id(&self) -> WebGPURenderPipeline {
+ self.render_pipeline
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline>
+ pub fn create(
+ device: &GPUDevice,
+ pipeline_layout: PipelineLayout,
+ descriptor: RenderPipelineDescriptor<'static>,
+ async_sender: Option<IpcSender<WebGPUResponse>>,
+ ) -> Fallible<WebGPURenderPipeline> {
+ let render_pipeline_id = device.global().wgpu_id_hub().create_render_pipeline_id();
+
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateRenderPipeline {
+ device_id: device.id().0,
+ render_pipeline_id,
+ descriptor,
+ implicit_ids: pipeline_layout.implicit(),
+ async_sender,
+ })
+ .expect("Failed to create WebGPU render pipeline");
+
+ Ok(WebGPURenderPipeline(render_pipeline_id))
+ }
+}
+
+impl GPURenderPipelineMethods<crate::DomTypeHolder> for GPURenderPipeline {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpupipelinebase-getbindgrouplayout>
+ fn GetBindGroupLayout(&self, index: u32) -> Fallible<DomRoot<GPUBindGroupLayout>> {
+ let id = self.global().wgpu_id_hub().create_bind_group_layout_id();
+
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::RenderGetBindGroupLayout {
+ device_id: self.device.id().0,
+ pipeline_id: self.render_pipeline.0,
+ index,
+ id,
+ })
+ {
+ warn!("Failed to send WebGPURequest::RenderGetBindGroupLayout {e:?}");
+ }
+
+ Ok(GPUBindGroupLayout::new(
+ &self.global(),
+ self.channel.clone(),
+ WebGPUBindGroupLayout(id),
+ USVString::default(),
+ ))
+ }
+}
+
+impl Drop for GPURenderPipeline {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropRenderPipeline(self.render_pipeline.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DropRenderPipeline({:?}) ({})",
+ self.render_pipeline.0, e
+ );
+ };
+ }
+}
diff --git a/components/script/dom/webgpu/gpusampler.rs b/components/script/dom/webgpu/gpusampler.rs
new file mode 100644
index 00000000000..ad7aa3e8b89
--- /dev/null
+++ b/components/script/dom/webgpu/gpusampler.rs
@@ -0,0 +1,143 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::wgc::resource::SamplerDescriptor;
+use webgpu::{WebGPU, WebGPUDevice, WebGPURequest, WebGPUSampler};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUSamplerDescriptor, GPUSamplerMethods,
+};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpudevice::GPUDevice;
+
+#[dom_struct]
+pub struct GPUSampler {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ device: WebGPUDevice,
+ compare_enable: bool,
+ #[no_trace]
+ sampler: WebGPUSampler,
+}
+
+impl GPUSampler {
+ fn new_inherited(
+ channel: WebGPU,
+ device: WebGPUDevice,
+ compare_enable: bool,
+ sampler: WebGPUSampler,
+ label: USVString,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ label: DomRefCell::new(label),
+ device,
+ sampler,
+ compare_enable,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ device: WebGPUDevice,
+ compare_enable: bool,
+ sampler: WebGPUSampler,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUSampler::new_inherited(
+ channel,
+ device,
+ compare_enable,
+ sampler,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUSampler {
+ pub fn id(&self) -> WebGPUSampler {
+ self.sampler
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler>
+ pub fn create(device: &GPUDevice, descriptor: &GPUSamplerDescriptor) -> DomRoot<GPUSampler> {
+ let sampler_id = device.global().wgpu_id_hub().create_sampler_id();
+ let compare_enable = descriptor.compare.is_some();
+ let desc = SamplerDescriptor {
+ label: (&descriptor.parent).into(),
+ address_modes: [
+ descriptor.addressModeU.into(),
+ descriptor.addressModeV.into(),
+ descriptor.addressModeW.into(),
+ ],
+ mag_filter: descriptor.magFilter.into(),
+ min_filter: descriptor.minFilter.into(),
+ mipmap_filter: descriptor.mipmapFilter.into(),
+ lod_min_clamp: *descriptor.lodMinClamp,
+ lod_max_clamp: *descriptor.lodMaxClamp,
+ compare: descriptor.compare.map(Into::into),
+ anisotropy_clamp: 1,
+ border_color: None,
+ };
+
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateSampler {
+ device_id: device.id().0,
+ sampler_id,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU sampler");
+
+ let sampler = WebGPUSampler(sampler_id);
+
+ GPUSampler::new(
+ &device.global(),
+ device.channel().clone(),
+ device.id(),
+ compare_enable,
+ sampler,
+ descriptor.parent.label.clone(),
+ )
+ }
+}
+
+impl GPUSamplerMethods<crate::DomTypeHolder> for GPUSampler {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+}
+
+impl Drop for GPUSampler {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropSampler(self.sampler.0))
+ {
+ warn!("Failed to send DropSampler ({:?}) ({})", self.sampler.0, e);
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpushadermodule.rs b/components/script/dom/webgpu/gpushadermodule.rs
new file mode 100644
index 00000000000..44cc8fa8115
--- /dev/null
+++ b/components/script/dom/webgpu/gpushadermodule.rs
@@ -0,0 +1,154 @@
+/* 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 std::rc::Rc;
+
+use dom_struct::dom_struct;
+use webgpu::{WebGPU, WebGPURequest, WebGPUResponse, WebGPUShaderModule};
+
+use super::gpu::AsyncWGPUListener;
+use super::gpucompilationinfo::GPUCompilationInfo;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUShaderModuleDescriptor, GPUShaderModuleMethods,
+};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::USVString;
+use crate::dom::bindings::trace::RootedTraceableBox;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::promise::Promise;
+use crate::dom::types::GPUDevice;
+use crate::dom::webgpu::gpu::response_async;
+use crate::realms::InRealm;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUShaderModule {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ shader_module: WebGPUShaderModule,
+ #[ignore_malloc_size_of = "promise"]
+ compilation_info_promise: Rc<Promise>,
+}
+
+impl GPUShaderModule {
+ fn new_inherited(
+ channel: WebGPU,
+ shader_module: WebGPUShaderModule,
+ label: USVString,
+ promise: Rc<Promise>,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ label: DomRefCell::new(label),
+ shader_module,
+ compilation_info_promise: promise,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ shader_module: WebGPUShaderModule,
+ label: USVString,
+ promise: Rc<Promise>,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUShaderModule::new_inherited(
+ channel,
+ shader_module,
+ label,
+ promise,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUShaderModule {
+ pub fn id(&self) -> WebGPUShaderModule {
+ self.shader_module
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
+ comp: InRealm,
+ can_gc: CanGc,
+ ) -> DomRoot<GPUShaderModule> {
+ let program_id = device.global().wgpu_id_hub().create_shader_module_id();
+ let promise = Promise::new_in_current_realm(comp, can_gc);
+ let shader_module = GPUShaderModule::new(
+ &device.global(),
+ device.channel().clone(),
+ WebGPUShaderModule(program_id),
+ descriptor.parent.label.clone(),
+ promise.clone(),
+ );
+ let sender = response_async(&promise, &*shader_module);
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateShaderModule {
+ device_id: device.id().0,
+ program_id,
+ program: descriptor.code.0.clone(),
+ label: None,
+ sender,
+ })
+ .expect("Failed to create WebGPU ShaderModule");
+ shader_module
+ }
+}
+
+impl GPUShaderModuleMethods<crate::DomTypeHolder> for GPUShaderModule {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpushadermodule-getcompilationinfo>
+ fn GetCompilationInfo(&self) -> Rc<Promise> {
+ self.compilation_info_promise.clone()
+ }
+}
+
+impl AsyncWGPUListener for GPUShaderModule {
+ fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>, can_gc: CanGc) {
+ match response {
+ WebGPUResponse::CompilationInfo(info) => {
+ let info = GPUCompilationInfo::from(&self.global(), info, can_gc);
+ promise.resolve_native(&info);
+ },
+ _ => unreachable!("Wrong response received on AsyncWGPUListener for GPUShaderModule"),
+ }
+ }
+}
+
+impl Drop for GPUShaderModule {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropShaderModule(self.shader_module.0))
+ {
+ warn!(
+ "Failed to send DropShaderModule ({:?}) ({})",
+ self.shader_module.0, e
+ );
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpushaderstage.rs b/components/script/dom/webgpu/gpushaderstage.rs
new file mode 100644
index 00000000000..611ffb12283
--- /dev/null
+++ b/components/script/dom/webgpu/gpushaderstage.rs
@@ -0,0 +1,12 @@
+/* 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 dom_struct::dom_struct;
+
+use crate::dom::bindings::reflector::Reflector;
+
+#[dom_struct]
+pub struct GPUShaderStage {
+ reflector_: Reflector,
+}
diff --git a/components/script/dom/webgpu/gpusupportedfeatures.rs b/components/script/dom/webgpu/gpusupportedfeatures.rs
new file mode 100644
index 00000000000..8cc161e172c
--- /dev/null
+++ b/components/script/dom/webgpu/gpusupportedfeatures.rs
@@ -0,0 +1,177 @@
+/* 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/. */
+
+// check-tidy: no specs after this line
+
+use dom_struct::dom_struct;
+use indexmap::IndexSet;
+use js::rust::HandleObject;
+use webgpu::wgt::Features;
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUFeatureName, GPUSupportedFeaturesMethods,
+};
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::like::Setlike;
+use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUSupportedFeatures {
+ reflector: Reflector,
+ // internal storage for features
+ #[custom_trace]
+ internal: DomRefCell<IndexSet<GPUFeatureName>>,
+ #[ignore_malloc_size_of = "defined in wgpu-types"]
+ #[no_trace]
+ features: Features,
+}
+
+impl GPUSupportedFeatures {
+ fn new(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ features: Features,
+ can_gc: CanGc,
+ ) -> DomRoot<GPUSupportedFeatures> {
+ let mut set = IndexSet::new();
+ if features.contains(Features::DEPTH_CLIP_CONTROL) {
+ set.insert(GPUFeatureName::Depth_clip_control);
+ }
+ if features.contains(Features::DEPTH32FLOAT_STENCIL8) {
+ set.insert(GPUFeatureName::Depth32float_stencil8);
+ }
+ if features.contains(Features::TEXTURE_COMPRESSION_BC) {
+ set.insert(GPUFeatureName::Texture_compression_bc);
+ }
+ // TODO: texture-compression-bc-sliced-3d when wgpu supports it
+ if features.contains(Features::TEXTURE_COMPRESSION_ETC2) {
+ set.insert(GPUFeatureName::Texture_compression_etc2);
+ }
+ if features.contains(Features::TEXTURE_COMPRESSION_ASTC) {
+ set.insert(GPUFeatureName::Texture_compression_astc);
+ }
+ if features.contains(Features::TIMESTAMP_QUERY) {
+ set.insert(GPUFeatureName::Timestamp_query);
+ }
+ if features.contains(Features::INDIRECT_FIRST_INSTANCE) {
+ set.insert(GPUFeatureName::Indirect_first_instance);
+ }
+ // While this feature exists in wgpu, it's not supported by naga yet
+ // https://github.com/gfx-rs/wgpu/issues/4384
+ /*
+ if features.contains(Features::SHADER_F16) {
+ set.insert(GPUFeatureName::Shader_f16);
+ }
+ */
+ if features.contains(Features::RG11B10UFLOAT_RENDERABLE) {
+ set.insert(GPUFeatureName::Rg11b10ufloat_renderable);
+ }
+ if features.contains(Features::BGRA8UNORM_STORAGE) {
+ set.insert(GPUFeatureName::Bgra8unorm_storage);
+ }
+ if features.contains(Features::FLOAT32_FILTERABLE) {
+ set.insert(GPUFeatureName::Float32_filterable);
+ }
+ // TODO: clip-distances when wgpu supports it
+ if features.contains(Features::DUAL_SOURCE_BLENDING) {
+ set.insert(GPUFeatureName::Dual_source_blending);
+ }
+
+ reflect_dom_object_with_proto(
+ Box::new(GPUSupportedFeatures {
+ reflector: Reflector::new(),
+ internal: DomRefCell::new(set),
+ features,
+ }),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+
+ #[allow(non_snake_case)]
+ pub fn Constructor(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ features: Features,
+ can_gc: CanGc,
+ ) -> Fallible<DomRoot<GPUSupportedFeatures>> {
+ Ok(GPUSupportedFeatures::new(global, proto, features, can_gc))
+ }
+}
+
+impl GPUSupportedFeatures {
+ pub fn wgpu_features(&self) -> Features {
+ self.features
+ }
+}
+
+impl GPUSupportedFeaturesMethods<crate::DomTypeHolder> for GPUSupportedFeatures {
+ fn Size(&self) -> u32 {
+ self.internal.size()
+ }
+}
+
+pub fn gpu_to_wgt_feature(feature: GPUFeatureName) -> Option<Features> {
+ match feature {
+ GPUFeatureName::Depth_clip_control => Some(Features::DEPTH_CLIP_CONTROL),
+ GPUFeatureName::Depth32float_stencil8 => Some(Features::DEPTH32FLOAT_STENCIL8),
+ GPUFeatureName::Texture_compression_bc => Some(Features::TEXTURE_COMPRESSION_BC),
+ GPUFeatureName::Texture_compression_etc2 => Some(Features::TEXTURE_COMPRESSION_ETC2),
+ GPUFeatureName::Texture_compression_astc => Some(Features::TEXTURE_COMPRESSION_ASTC),
+ GPUFeatureName::Timestamp_query => Some(Features::TIMESTAMP_QUERY),
+ GPUFeatureName::Indirect_first_instance => Some(Features::INDIRECT_FIRST_INSTANCE),
+ // While this feature exists in wgpu, it's not supported by naga yet
+ // https://github.com/gfx-rs/wgpu/issues/4384
+ GPUFeatureName::Shader_f16 => None,
+ GPUFeatureName::Rg11b10ufloat_renderable => Some(Features::RG11B10UFLOAT_RENDERABLE),
+ GPUFeatureName::Bgra8unorm_storage => Some(Features::BGRA8UNORM_STORAGE),
+ GPUFeatureName::Float32_filterable => Some(Features::FLOAT32_FILTERABLE),
+ GPUFeatureName::Dual_source_blending => Some(Features::DUAL_SOURCE_BLENDING),
+ GPUFeatureName::Texture_compression_bc_sliced_3d => None,
+ GPUFeatureName::Clip_distances => None,
+ }
+}
+
+// this error is wrong because if we inline Self::Key and Self::Value all errors are gone
+#[allow(crown::unrooted_must_root)]
+impl Setlike for GPUSupportedFeatures {
+ type Key = DOMString;
+
+ #[inline(always)]
+ fn get_index(&self, index: u32) -> Option<Self::Key> {
+ self.internal
+ .get_index(index)
+ .map(|k| DOMString::from_string(k.as_str().to_owned()))
+ }
+ #[inline(always)]
+ fn size(&self) -> u32 {
+ self.internal.size()
+ }
+ #[inline(always)]
+ fn add(&self, _key: Self::Key) {
+ unreachable!("readonly");
+ }
+ #[inline(always)]
+ fn has(&self, key: Self::Key) -> bool {
+ if let Ok(key) = key.parse() {
+ self.internal.has(key)
+ } else {
+ false
+ }
+ }
+ #[inline(always)]
+ fn clear(&self) {
+ unreachable!("readonly");
+ }
+ #[inline(always)]
+ fn delete(&self, _key: Self::Key) -> bool {
+ unreachable!("readonly");
+ }
+}
diff --git a/components/script/dom/webgpu/gpusupportedlimits.rs b/components/script/dom/webgpu/gpusupportedlimits.rs
new file mode 100644
index 00000000000..15f1172da18
--- /dev/null
+++ b/components/script/dom/webgpu/gpusupportedlimits.rs
@@ -0,0 +1,316 @@
+/* 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 dom_struct::dom_struct;
+use num_traits::bounds::UpperBounded;
+use webgpu::wgt::Limits;
+use GPUSupportedLimits_Binding::GPUSupportedLimitsMethods;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUSupportedLimits_Binding;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::globalscope::GlobalScope;
+
+#[dom_struct]
+pub struct GPUSupportedLimits {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in wgpu-types"]
+ #[no_trace]
+ limits: Limits,
+}
+
+impl GPUSupportedLimits {
+ fn new_inherited(limits: Limits) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ limits,
+ }
+ }
+
+ pub fn new(global: &GlobalScope, limits: Limits) -> DomRoot<Self> {
+ reflect_dom_object(Box::new(Self::new_inherited(limits)), global)
+ }
+}
+
+impl GPUSupportedLimitsMethods<crate::DomTypeHolder> for GPUSupportedLimits {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxtexturedimension1d>
+ fn MaxTextureDimension1D(&self) -> u32 {
+ self.limits.max_texture_dimension_1d
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxtexturedimension2d>
+ fn MaxTextureDimension2D(&self) -> u32 {
+ self.limits.max_texture_dimension_2d
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxtexturedimension3d>
+ fn MaxTextureDimension3D(&self) -> u32 {
+ self.limits.max_texture_dimension_3d
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxtexturearraylayers>
+ fn MaxTextureArrayLayers(&self) -> u32 {
+ self.limits.max_texture_array_layers
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxbindgroups>
+ fn MaxBindGroups(&self) -> u32 {
+ self.limits.max_bind_groups
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxbindingsperbindgroup>
+ fn MaxBindingsPerBindGroup(&self) -> u32 {
+ self.limits.max_bindings_per_bind_group
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxdynamicuniformbuffersperpipelinelayout>
+ fn MaxDynamicUniformBuffersPerPipelineLayout(&self) -> u32 {
+ self.limits.max_dynamic_uniform_buffers_per_pipeline_layout
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxdynamicstoragebuffersperpipelinelayout>
+ fn MaxDynamicStorageBuffersPerPipelineLayout(&self) -> u32 {
+ self.limits.max_dynamic_storage_buffers_per_pipeline_layout
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxsampledtexturespershaderstage>
+ fn MaxSampledTexturesPerShaderStage(&self) -> u32 {
+ self.limits.max_sampled_textures_per_shader_stage
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxsamplerspershaderstage>
+ fn MaxSamplersPerShaderStage(&self) -> u32 {
+ self.limits.max_samplers_per_shader_stage
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxstoragebufferspershaderstage>
+ fn MaxStorageBuffersPerShaderStage(&self) -> u32 {
+ self.limits.max_storage_buffers_per_shader_stage
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxstoragetexturespershaderstage>
+ fn MaxStorageTexturesPerShaderStage(&self) -> u32 {
+ self.limits.max_storage_textures_per_shader_stage
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxuniformbufferspershaderstage>
+ fn MaxUniformBuffersPerShaderStage(&self) -> u32 {
+ self.limits.max_uniform_buffers_per_shader_stage
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxuniformbufferbindingsize>
+ fn MaxUniformBufferBindingSize(&self) -> u64 {
+ self.limits.max_uniform_buffer_binding_size as u64
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxstoragebufferbindingsize>
+ fn MaxStorageBufferBindingSize(&self) -> u64 {
+ self.limits.max_storage_buffer_binding_size as u64
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-minuniformbufferoffsetalignment>
+ fn MinUniformBufferOffsetAlignment(&self) -> u32 {
+ self.limits.min_uniform_buffer_offset_alignment
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-minstoragebufferoffsetalignment>
+ fn MinStorageBufferOffsetAlignment(&self) -> u32 {
+ self.limits.min_storage_buffer_offset_alignment
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxvertexbuffers>
+ fn MaxVertexBuffers(&self) -> u32 {
+ self.limits.max_vertex_buffers
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxbuffersize>
+ fn MaxBufferSize(&self) -> u64 {
+ self.limits.max_buffer_size
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxvertexattributes>
+ fn MaxVertexAttributes(&self) -> u32 {
+ self.limits.max_vertex_attributes
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxvertexbufferarraystride>
+ fn MaxVertexBufferArrayStride(&self) -> u32 {
+ self.limits.max_vertex_buffer_array_stride
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxinterstageshadercomponents>
+ fn MaxInterStageShaderComponents(&self) -> u32 {
+ self.limits.max_inter_stage_shader_components
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcomputeworkgroupstoragesize>
+ fn MaxComputeWorkgroupStorageSize(&self) -> u32 {
+ self.limits.max_compute_workgroup_storage_size
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcomputeinvocationsperworkgroup>
+ fn MaxComputeInvocationsPerWorkgroup(&self) -> u32 {
+ self.limits.max_compute_invocations_per_workgroup
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcomputeworkgroupsizex>
+ fn MaxComputeWorkgroupSizeX(&self) -> u32 {
+ self.limits.max_compute_workgroup_size_x
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcomputeworkgroupsizey>
+ fn MaxComputeWorkgroupSizeY(&self) -> u32 {
+ self.limits.max_compute_workgroup_size_y
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcomputeworkgroupsizez>
+ fn MaxComputeWorkgroupSizeZ(&self) -> u32 {
+ self.limits.max_compute_workgroup_size_z
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcomputeworkgroupsperdimension>
+ fn MaxComputeWorkgroupsPerDimension(&self) -> u32 {
+ self.limits.max_compute_workgroups_per_dimension
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxbindgroupsplusvertexbuffers>
+ fn MaxBindGroupsPlusVertexBuffers(&self) -> u32 {
+ // Not on wgpu yet, so we craft it manually
+ self.limits.max_bind_groups + self.limits.max_vertex_buffers
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxinterstageshadervariables>
+ fn MaxInterStageShaderVariables(&self) -> u32 {
+ // Not in wgpu yet, so we use default value from spec
+ 16
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcolorattachments>
+ fn MaxColorAttachments(&self) -> u32 {
+ self.limits.max_color_attachments
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcolorattachmentbytespersample>
+ fn MaxColorAttachmentBytesPerSample(&self) -> u32 {
+ self.limits.max_color_attachment_bytes_per_sample
+ }
+}
+
+/// Returns false if unknown limit or other value error
+pub fn set_limit(limits: &mut Limits, limit: &str, value: u64) -> bool {
+ /// per spec defaults are lower bounds for values
+ ///
+ /// <https://www.w3.org/TR/webgpu/#limit-class-maximum>
+ fn set_maximum<T>(limit: &mut T, value: u64) -> bool
+ where
+ T: Ord + Copy + TryFrom<u64> + UpperBounded,
+ {
+ if let Ok(value) = T::try_from(value) {
+ *limit = value.max(*limit);
+ true
+ } else {
+ false
+ }
+ }
+
+ /// per spec defaults are higher bounds for values
+ ///
+ /// <https://www.w3.org/TR/webgpu/#limit-class-alignment>
+ fn set_alignment<T>(limit: &mut T, value: u64) -> bool
+ where
+ T: Ord + Copy + TryFrom<u64> + UpperBounded,
+ {
+ if !value.is_power_of_two() {
+ return false;
+ }
+ if let Ok(value) = T::try_from(value) {
+ *limit = value.min(*limit);
+ true
+ } else {
+ false
+ }
+ }
+
+ match limit {
+ "maxTextureDimension1D" => set_maximum(&mut limits.max_texture_dimension_1d, value),
+ "maxTextureDimension2D" => set_maximum(&mut limits.max_texture_dimension_2d, value),
+ "maxTextureDimension3D" => set_maximum(&mut limits.max_texture_dimension_3d, value),
+ "maxTextureArrayLayers" => set_maximum(&mut limits.max_texture_array_layers, value),
+ "maxBindGroups" => set_maximum(&mut limits.max_bind_groups, value),
+ "maxBindGroupsPlusVertexBuffers" => {
+ // not in wgpu but we're allowed to give back better limits than requested.
+ // we use dummy value to still produce value verification
+ let mut v: u32 = 0;
+ set_maximum(&mut v, value)
+ },
+ "maxBindingsPerBindGroup" => set_maximum(&mut limits.max_bindings_per_bind_group, value),
+ "maxDynamicUniformBuffersPerPipelineLayout" => set_maximum(
+ &mut limits.max_dynamic_uniform_buffers_per_pipeline_layout,
+ value,
+ ),
+ "maxDynamicStorageBuffersPerPipelineLayout" => set_maximum(
+ &mut limits.max_dynamic_storage_buffers_per_pipeline_layout,
+ value,
+ ),
+ "maxSampledTexturesPerShaderStage" => {
+ set_maximum(&mut limits.max_sampled_textures_per_shader_stage, value)
+ },
+ "maxSamplersPerShaderStage" => {
+ set_maximum(&mut limits.max_samplers_per_shader_stage, value)
+ },
+ "maxStorageBuffersPerShaderStage" => {
+ set_maximum(&mut limits.max_storage_buffers_per_shader_stage, value)
+ },
+ "maxStorageTexturesPerShaderStage" => {
+ set_maximum(&mut limits.max_storage_textures_per_shader_stage, value)
+ },
+ "maxUniformBuffersPerShaderStage" => {
+ set_maximum(&mut limits.max_uniform_buffers_per_shader_stage, value)
+ },
+ "maxUniformBufferBindingSize" => {
+ set_maximum(&mut limits.max_uniform_buffer_binding_size, value)
+ },
+ "maxStorageBufferBindingSize" => {
+ set_maximum(&mut limits.max_storage_buffer_binding_size, value)
+ },
+ "minUniformBufferOffsetAlignment" => {
+ set_alignment(&mut limits.min_uniform_buffer_offset_alignment, value)
+ },
+ "minStorageBufferOffsetAlignment" => {
+ set_alignment(&mut limits.min_storage_buffer_offset_alignment, value)
+ },
+ "maxVertexBuffers" => set_maximum(&mut limits.max_vertex_buffers, value),
+ "maxBufferSize" => set_maximum(&mut limits.max_buffer_size, value),
+ "maxVertexAttributes" => set_maximum(&mut limits.max_vertex_attributes, value),
+ "maxVertexBufferArrayStride" => {
+ set_maximum(&mut limits.max_vertex_buffer_array_stride, value)
+ },
+ "maxInterStageShaderComponents" => {
+ set_maximum(&mut limits.max_inter_stage_shader_components, value)
+ },
+ "maxInterStageShaderVariables" => {
+ // not in wgpu but we're allowed to give back better limits than requested.
+ // we use dummy value to still produce value verification
+ let mut v: u32 = 0;
+ set_maximum(&mut v, value)
+ },
+ "maxColorAttachments" => set_maximum(&mut limits.max_color_attachments, value),
+ "maxColorAttachmentBytesPerSample" => {
+ set_maximum(&mut limits.max_color_attachment_bytes_per_sample, value)
+ },
+ "maxComputeWorkgroupStorageSize" => {
+ set_maximum(&mut limits.max_compute_workgroup_storage_size, value)
+ },
+ "maxComputeInvocationsPerWorkgroup" => {
+ set_maximum(&mut limits.max_compute_invocations_per_workgroup, value)
+ },
+ "maxComputeWorkgroupSizeX" => set_maximum(&mut limits.max_compute_workgroup_size_x, value),
+ "maxComputeWorkgroupSizeY" => set_maximum(&mut limits.max_compute_workgroup_size_y, value),
+ "maxComputeWorkgroupSizeZ" => set_maximum(&mut limits.max_compute_workgroup_size_z, value),
+ "maxComputeWorkgroupsPerDimension" => {
+ set_maximum(&mut limits.max_compute_workgroups_per_dimension, value)
+ },
+ _ => false,
+ }
+}
diff --git a/components/script/dom/webgpu/gputexture.rs b/components/script/dom/webgpu/gputexture.rs
new file mode 100644
index 00000000000..b37197f4a90
--- /dev/null
+++ b/components/script/dom/webgpu/gputexture.rs
@@ -0,0 +1,285 @@
+/* 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 std::string::String;
+
+use dom_struct::dom_struct;
+use webgpu::wgc::resource;
+use webgpu::{wgt, WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView};
+
+use super::gpuconvert::convert_texture_descriptor;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
+ GPUTextureMethods, GPUTextureViewDescriptor,
+};
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpudevice::GPUDevice;
+use crate::dom::gputextureview::GPUTextureView;
+
+#[dom_struct]
+pub struct GPUTexture {
+ reflector_: Reflector,
+ #[no_trace]
+ texture: WebGPUTexture,
+ label: DomRefCell<USVString>,
+ device: Dom<GPUDevice>,
+ #[ignore_malloc_size_of = "channels are hard"]
+ #[no_trace]
+ channel: WebGPU,
+ #[ignore_malloc_size_of = "defined in wgpu"]
+ #[no_trace]
+ texture_size: wgt::Extent3d,
+ mip_level_count: u32,
+ sample_count: u32,
+ dimension: GPUTextureDimension,
+ format: GPUTextureFormat,
+ texture_usage: u32,
+}
+
+impl GPUTexture {
+ #[allow(clippy::too_many_arguments)]
+ fn new_inherited(
+ texture: WebGPUTexture,
+ device: &GPUDevice,
+ channel: WebGPU,
+ texture_size: wgt::Extent3d,
+ mip_level_count: u32,
+ sample_count: u32,
+ dimension: GPUTextureDimension,
+ format: GPUTextureFormat,
+ texture_usage: u32,
+ label: USVString,
+ ) -> Self {
+ Self {
+ reflector_: Reflector::new(),
+ texture,
+ label: DomRefCell::new(label),
+ device: Dom::from_ref(device),
+ channel,
+ texture_size,
+ mip_level_count,
+ sample_count,
+ dimension,
+ format,
+ texture_usage,
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ global: &GlobalScope,
+ texture: WebGPUTexture,
+ device: &GPUDevice,
+ channel: WebGPU,
+ texture_size: wgt::Extent3d,
+ mip_level_count: u32,
+ sample_count: u32,
+ dimension: GPUTextureDimension,
+ format: GPUTextureFormat,
+ texture_usage: u32,
+ label: USVString,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(GPUTexture::new_inherited(
+ texture,
+ device,
+ channel,
+ texture_size,
+ mip_level_count,
+ sample_count,
+ dimension,
+ format,
+ texture_usage,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl Drop for GPUTexture {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropTexture(self.texture.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DropTexture({:?}) ({})",
+ self.texture.0, e
+ );
+ };
+ }
+}
+
+impl GPUTexture {
+ pub fn id(&self) -> WebGPUTexture {
+ self.texture
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
+ pub fn create(
+ device: &GPUDevice,
+ descriptor: &GPUTextureDescriptor,
+ ) -> Fallible<DomRoot<GPUTexture>> {
+ let (desc, size) = convert_texture_descriptor(descriptor, device)?;
+
+ let texture_id = device.global().wgpu_id_hub().create_texture_id();
+
+ device
+ .channel()
+ .0
+ .send(WebGPURequest::CreateTexture {
+ device_id: device.id().0,
+ texture_id,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU Texture");
+
+ let texture = WebGPUTexture(texture_id);
+
+ Ok(GPUTexture::new(
+ &device.global(),
+ texture,
+ device,
+ device.channel().clone(),
+ size,
+ descriptor.mipLevelCount,
+ descriptor.sampleCount,
+ descriptor.dimension,
+ descriptor.format,
+ descriptor.usage,
+ descriptor.parent.label.clone(),
+ ))
+ }
+}
+
+impl GPUTextureMethods<crate::DomTypeHolder> for GPUTexture {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-createview>
+ fn CreateView(
+ &self,
+ descriptor: &GPUTextureViewDescriptor,
+ ) -> Fallible<DomRoot<GPUTextureView>> {
+ let desc = if !matches!(descriptor.mipLevelCount, Some(0)) &&
+ !matches!(descriptor.arrayLayerCount, Some(0))
+ {
+ Some(resource::TextureViewDescriptor {
+ label: (&descriptor.parent).into(),
+ format: descriptor
+ .format
+ .map(|f| self.device.validate_texture_format_required_features(&f))
+ .transpose()?,
+ dimension: descriptor.dimension.map(|dimension| dimension.into()),
+ range: wgt::ImageSubresourceRange {
+ aspect: match descriptor.aspect {
+ GPUTextureAspect::All => wgt::TextureAspect::All,
+ GPUTextureAspect::Stencil_only => wgt::TextureAspect::StencilOnly,
+ GPUTextureAspect::Depth_only => wgt::TextureAspect::DepthOnly,
+ },
+ base_mip_level: descriptor.baseMipLevel,
+ mip_level_count: descriptor.mipLevelCount,
+ base_array_layer: descriptor.baseArrayLayer,
+ array_layer_count: descriptor.arrayLayerCount,
+ },
+ })
+ } else {
+ self.device
+ .dispatch_error(webgpu::Error::Validation(String::from(
+ "arrayLayerCount and mipLevelCount cannot be 0",
+ )));
+ None
+ };
+
+ let texture_view_id = self.global().wgpu_id_hub().create_texture_view_id();
+
+ self.channel
+ .0
+ .send(WebGPURequest::CreateTextureView {
+ texture_id: self.texture.0,
+ texture_view_id,
+ device_id: self.device.id().0,
+ descriptor: desc,
+ })
+ .expect("Failed to create WebGPU texture view");
+
+ let texture_view = WebGPUTextureView(texture_view_id);
+
+ Ok(GPUTextureView::new(
+ &self.global(),
+ self.channel.clone(),
+ texture_view,
+ self,
+ descriptor.parent.label.clone(),
+ ))
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-destroy>
+ fn Destroy(&self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DestroyTexture(self.texture.0))
+ {
+ warn!(
+ "Failed to send WebGPURequest::DestroyTexture({:?}) ({})",
+ self.texture.0, e
+ );
+ };
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-width>
+ fn Width(&self) -> u32 {
+ self.texture_size.width
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-height>
+ fn Height(&self) -> u32 {
+ self.texture_size.height
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-depthorarraylayers>
+ fn DepthOrArrayLayers(&self) -> u32 {
+ self.texture_size.depth_or_array_layers
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-miplevelcount>
+ fn MipLevelCount(&self) -> u32 {
+ self.mip_level_count
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-samplecount>
+ fn SampleCount(&self) -> u32 {
+ self.sample_count
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-dimension>
+ fn Dimension(&self) -> GPUTextureDimension {
+ self.dimension
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-format>
+ fn Format(&self) -> GPUTextureFormat {
+ self.format
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-usage>
+ fn Usage(&self) -> u32 {
+ self.texture_usage
+ }
+}
diff --git a/components/script/dom/webgpu/gputextureusage.rs b/components/script/dom/webgpu/gputextureusage.rs
new file mode 100644
index 00000000000..f2d9645364b
--- /dev/null
+++ b/components/script/dom/webgpu/gputextureusage.rs
@@ -0,0 +1,12 @@
+/* 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 dom_struct::dom_struct;
+
+use crate::dom::bindings::reflector::Reflector;
+
+#[dom_struct]
+pub struct GPUTextureUsage {
+ reflector_: Reflector,
+}
diff --git a/components/script/dom/webgpu/gputextureview.rs b/components/script/dom/webgpu/gputextureview.rs
new file mode 100644
index 00000000000..3c8d484fca3
--- /dev/null
+++ b/components/script/dom/webgpu/gputextureview.rs
@@ -0,0 +1,94 @@
+/* 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 dom_struct::dom_struct;
+use webgpu::{WebGPU, WebGPURequest, WebGPUTextureView};
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUTextureViewMethods;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::USVString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gputexture::GPUTexture;
+
+#[dom_struct]
+pub struct GPUTextureView {
+ reflector_: Reflector,
+ #[ignore_malloc_size_of = "defined in webgpu"]
+ #[no_trace]
+ channel: WebGPU,
+ label: DomRefCell<USVString>,
+ #[no_trace]
+ texture_view: WebGPUTextureView,
+ texture: Dom<GPUTexture>,
+}
+
+impl GPUTextureView {
+ fn new_inherited(
+ channel: WebGPU,
+ texture_view: WebGPUTextureView,
+ texture: &GPUTexture,
+ label: USVString,
+ ) -> GPUTextureView {
+ Self {
+ reflector_: Reflector::new(),
+ channel,
+ texture: Dom::from_ref(texture),
+ label: DomRefCell::new(label),
+ texture_view,
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ channel: WebGPU,
+ texture_view: WebGPUTextureView,
+ texture: &GPUTexture,
+ label: USVString,
+ ) -> DomRoot<GPUTextureView> {
+ reflect_dom_object(
+ Box::new(GPUTextureView::new_inherited(
+ channel,
+ texture_view,
+ texture,
+ label,
+ )),
+ global,
+ )
+ }
+}
+
+impl GPUTextureView {
+ pub fn id(&self) -> WebGPUTextureView {
+ self.texture_view
+ }
+}
+
+impl GPUTextureViewMethods<crate::DomTypeHolder> for GPUTextureView {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn Label(&self) -> USVString {
+ self.label.borrow().clone()
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
+ fn SetLabel(&self, value: USVString) {
+ *self.label.borrow_mut() = value;
+ }
+}
+
+impl Drop for GPUTextureView {
+ fn drop(&mut self) {
+ if let Err(e) = self
+ .channel
+ .0
+ .send(WebGPURequest::DropTextureView(self.texture_view.0))
+ {
+ warn!(
+ "Failed to send DropTextureView ({:?}) ({})",
+ self.texture_view.0, e
+ );
+ }
+ }
+}
diff --git a/components/script/dom/webgpu/gpuuncapturederrorevent.rs b/components/script/dom/webgpu/gpuuncapturederrorevent.rs
new file mode 100644
index 00000000000..0ee3871d98d
--- /dev/null
+++ b/components/script/dom/webgpu/gpuuncapturederrorevent.rs
@@ -0,0 +1,92 @@
+/* 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 dom_struct::dom_struct;
+use js::rust::HandleObject;
+use servo_atoms::Atom;
+
+use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
+ GPUUncapturedErrorEventInit, GPUUncapturedErrorEventMethods,
+};
+use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::str::DOMString;
+use crate::dom::event::Event;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::gpuerror::GPUError;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUUncapturedErrorEvent {
+ event: Event,
+ #[ignore_malloc_size_of = "Because it is non-owning"]
+ gpu_error: Dom<GPUError>,
+}
+
+impl GPUUncapturedErrorEvent {
+ fn new_inherited(init: &GPUUncapturedErrorEventInit) -> Self {
+ Self {
+ gpu_error: Dom::from_ref(&init.error),
+ event: Event::new_inherited(),
+ }
+ }
+
+ pub fn new(
+ global: &GlobalScope,
+ type_: DOMString,
+ init: &GPUUncapturedErrorEventInit,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ Self::new_with_proto(global, None, type_, init, can_gc)
+ }
+
+ fn new_with_proto(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ type_: DOMString,
+ init: &GPUUncapturedErrorEventInit,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ let ev = reflect_dom_object_with_proto(
+ Box::new(GPUUncapturedErrorEvent::new_inherited(init)),
+ global,
+ proto,
+ can_gc,
+ );
+ ev.event.init_event(
+ Atom::from(type_),
+ init.parent.bubbles,
+ init.parent.cancelable,
+ );
+ ev
+ }
+
+ pub fn event(&self) -> &Event {
+ &self.event
+ }
+}
+
+impl GPUUncapturedErrorEventMethods<crate::DomTypeHolder> for GPUUncapturedErrorEvent {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuuncapturederrorevent-gpuuncapturederrorevent>
+ fn Constructor(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ type_: DOMString,
+ init: &GPUUncapturedErrorEventInit,
+ ) -> DomRoot<Self> {
+ GPUUncapturedErrorEvent::new_with_proto(global, proto, type_, init, can_gc)
+ }
+
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuuncapturederrorevent-error>
+ fn Error(&self) -> DomRoot<GPUError> {
+ DomRoot::from_ref(&self.gpu_error)
+ }
+
+ /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
+ fn IsTrusted(&self) -> bool {
+ self.event.IsTrusted()
+ }
+}
diff --git a/components/script/dom/webgpu/gpuvalidationerror.rs b/components/script/dom/webgpu/gpuvalidationerror.rs
new file mode 100644
index 00000000000..0b20375e728
--- /dev/null
+++ b/components/script/dom/webgpu/gpuvalidationerror.rs
@@ -0,0 +1,53 @@
+/* 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 dom_struct::dom_struct;
+use js::rust::HandleObject;
+
+use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUValidationError_Binding::GPUValidationErrorMethods;
+use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::types::GPUError;
+use crate::script_runtime::CanGc;
+
+#[dom_struct]
+pub struct GPUValidationError {
+ gpu_error: GPUError,
+}
+
+impl GPUValidationError {
+ fn new_inherited(message: DOMString) -> Self {
+ Self {
+ gpu_error: GPUError::new_inherited(message),
+ }
+ }
+
+ pub fn new_with_proto(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ message: DOMString,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ reflect_dom_object_with_proto(
+ Box::new(Self::new_inherited(message)),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+}
+
+impl GPUValidationErrorMethods<crate::DomTypeHolder> for GPUValidationError {
+ /// <https://gpuweb.github.io/gpuweb/#dom-gpuvalidationerror-gpuvalidationerror>
+ fn Constructor(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ message: DOMString,
+ ) -> DomRoot<Self> {
+ Self::new_with_proto(global, proto, message, can_gc)
+ }
+}
diff --git a/components/script/dom/webgpu/mod.rs b/components/script/dom/webgpu/mod.rs
new file mode 100644
index 00000000000..22994a942bc
--- /dev/null
+++ b/components/script/dom/webgpu/mod.rs
@@ -0,0 +1,44 @@
+/* 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/. */
+
+pub mod gpu;
+pub mod gpuadapter;
+pub mod gpuadapterinfo;
+pub mod gpubindgroup;
+pub mod gpubindgrouplayout;
+pub mod gpubuffer;
+pub mod gpubufferusage;
+pub mod gpucanvascontext;
+pub mod gpucolorwrite;
+pub mod gpucommandbuffer;
+pub mod gpucommandencoder;
+pub mod gpucompilationinfo;
+pub mod gpucompilationmessage;
+pub mod gpucomputepassencoder;
+pub mod gpucomputepipeline;
+pub mod gpuconvert;
+pub mod gpudevice;
+pub mod gpudevicelostinfo;
+pub mod gpuerror;
+pub mod gpuinternalerror;
+pub mod gpumapmode;
+pub mod gpuoutofmemoryerror;
+pub mod gpupipelineerror;
+pub mod gpupipelinelayout;
+pub mod gpuqueryset;
+pub mod gpuqueue;
+pub mod gpurenderbundle;
+pub mod gpurenderbundleencoder;
+pub mod gpurenderpassencoder;
+pub mod gpurenderpipeline;
+pub mod gpusampler;
+pub mod gpushadermodule;
+pub mod gpushaderstage;
+pub mod gpusupportedfeatures;
+pub mod gpusupportedlimits;
+pub mod gputexture;
+pub mod gputextureusage;
+pub mod gputextureview;
+pub mod gpuuncapturederrorevent;
+pub mod gpuvalidationerror;