Move script gpu files into webgpu folder (#34415)

Signed-off-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
atbrakhi 2024-11-28 15:24:15 +01:00 committed by GitHub
parent a37ccc3e64
commit d2d3407501
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 108 additions and 118 deletions

View file

@ -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"),
}
}
}

View file

@ -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"),
}
}
}

View file

@ -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())
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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"),
}
}
}

View file

@ -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,
}

View file

@ -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
);
}
}
}

View file

@ -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,
}

View file

@ -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;
}
}

View file

@ -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(),
)
}
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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:?}");
}
}
}

View file

@ -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
);
};
}
}

View file

@ -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,
}
}
}

View file

@ -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);
}
}
}

View file

@ -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
}
}

View file

@ -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,
}
}
}

View file

@ -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)
}
}

View file

@ -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,
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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
);
}
}
}

View file

@ -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!()
}
}

View file

@ -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);
},
}
}
}

View file

@ -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
);
}
}
}

View file

@ -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(),
)
}
}

View file

@ -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:?}");
}
}
}

View file

@ -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
);
};
}
}

View file

@ -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);
}
}
}

View file

@ -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
);
}
}
}

View file

@ -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,
}

View file

@ -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");
}
}

View file

@ -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,
}
}

View file

@ -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
}
}

View file

@ -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,
}

View file

@ -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
);
}
}
}

View file

@ -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()
}
}

View file

@ -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)
}
}

View file

@ -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;