Skip to content

Commit

Permalink
Initial implementation of GPUDevice for WebGPU
Browse files Browse the repository at this point in the history
Added the WebIDL bindigs for GPUDevice, GPUObjectDescriptorBase, GPUDeviceDescriptor, GPUObjectBase
Implemented the `requestDevice` function of `GPUAdapter`
  • Loading branch information
Istvan Miklos committed Dec 5, 2019
1 parent 7aa68c8 commit b15d2bb
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 16 deletions.
4 changes: 4 additions & 0 deletions components/script/dom/bindings/codegen/Bindings.conf
Expand Up @@ -138,6 +138,10 @@ DOMInterfaces = {

'GPU': {
'inCompartments': ['RequestAdapter'],
},

'GPUAdapter': {
'inCompartments': ['RequestDevice'],
}

}
5 changes: 3 additions & 2 deletions components/script/dom/bindings/trace.rs
Expand Up @@ -146,7 +146,7 @@ use tendril::stream::LossyDecoder;
use tendril::{StrTendril, TendrilSink};
use time::{Duration, Timespec};
use uuid::Uuid;
use webgpu::{WebGPU, WebGPUAdapter};
use webgpu::{WebGPU, WebGPUAdapter, WebGPUDevice};
use webrender_api::{DocumentId, ImageKey};
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
use webxr_api::SwapChainId as WebXRSwapChainId;
Expand Down Expand Up @@ -504,9 +504,10 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId);
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
unsafe_no_jsmanaged_fields!(WebGLVersion);
unsafe_no_jsmanaged_fields!(WebGLSLVersion);
unsafe_no_jsmanaged_fields!(WebGPU);
unsafe_no_jsmanaged_fields!(RefCell<Identities>);
unsafe_no_jsmanaged_fields!(WebGPU);
unsafe_no_jsmanaged_fields!(WebGPUAdapter);
unsafe_no_jsmanaged_fields!(WebGPUDevice);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
unsafe_no_jsmanaged_fields!(MediaList);
unsafe_no_jsmanaged_fields!(WebVRGamepadData, WebVRGamepadState, WebVRGamepadHand);
Expand Down
64 changes: 62 additions & 2 deletions components/script/dom/gpuadapter.rs
Expand Up @@ -2,16 +2,29 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::{self, GPUAdapterMethods};
use crate::compartments::InCompartment;
use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::{
self, GPUAdapterMethods, GPUDeviceDescriptor,
};
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::dom::gpu::response_async;
use crate::dom::gpu::AsyncWGPUListener;
use crate::dom::gpudevice::GPUDevice;
use crate::dom::promise::Promise;
use crate::dom::window::Window;
use crate::script_runtime::JSContext as SafeJSContext;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject};
use std::ptr::NonNull;
use webgpu::WebGPUAdapter;
use std::rc::Rc;
use webgpu::{wgpu, WebGPUAdapter, WebGPURequest, WebGPUResponse};

#[dom_struct]
pub struct GPUAdapter {
Expand Down Expand Up @@ -60,4 +73,51 @@ impl GPUAdapterMethods for GPUAdapter {
fn Extensions(&self, _cx: SafeJSContext) -> NonNull<JSObject> {
NonNull::new(self.extensions.get()).unwrap()
}

/// https://gpuweb.github.io/gpuweb/#dom-gpuadapter-requestdevice
fn RequestDevice(&self, descriptor: &GPUDeviceDescriptor, comp: InCompartment) -> Rc<Promise> {
let promise = Promise::new_in_current_compartment(&self.global(), comp);
let sender = response_async(&promise, self);
let desc = wgpu::DeviceDescriptor {
extensions: wgpu::Extensions {
anisotropic_filtering: descriptor.extensions.anisotropicFiltering,
},
limits: wgpu::Limits {
max_bind_groups: descriptor.limits.maxBindGroups,
},
};
if let Some(window) = self.global().downcast::<Window>() {
let id = window.Navigator().create_device_id();
match window.webgpu_channel() {
Some(thread) => thread
.0
.send(WebGPURequest::RequestDevice(sender, self.adapter, desc, id))
.unwrap(),
None => promise.reject_error(Error::Type("No WebGPU thread...".to_owned())),
}
} else {
promise.reject_error(Error::Type("No WebGPU thread...".to_owned()))
};
promise
}
}

impl AsyncWGPUListener for GPUAdapter {
fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) {
match response {
WebGPUResponse::RequestDevice(device_id, _descriptor) => {
let device = GPUDevice::new(
&self.global(),
&self,
Heap::default(),
Heap::default(),
device_id,
);
promise.resolve_native(&device);
},
_ => promise.reject_error(Error::Type(
"Wrong response type from WebGPU thread...".to_owned(),
)),
}
}
}
91 changes: 91 additions & 0 deletions components/script/dom/gpudevice.rs
@@ -0,0 +1,91 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{self, GPUDeviceMethods};
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::gpuadapter::GPUAdapter;
use crate::script_runtime::JSContext as SafeJSContext;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject};
use std::ptr::NonNull;
use webgpu::WebGPUDevice;

#[dom_struct]
pub struct GPUDevice {
eventtarget: EventTarget,
adapter: Dom<GPUAdapter>,
#[ignore_malloc_size_of = "mozjs"]
extensions: Heap<*mut JSObject>,
#[ignore_malloc_size_of = "mozjs"]
limits: Heap<*mut JSObject>,
label: DomRefCell<Option<DOMString>>,
device: WebGPUDevice,
}

impl GPUDevice {
fn new_inherited(
adapter: &GPUAdapter,
extensions: Heap<*mut JSObject>,
limits: Heap<*mut JSObject>,
device: WebGPUDevice,
) -> GPUDevice {
Self {
eventtarget: EventTarget::new_inherited(),
adapter: Dom::from_ref(adapter),
extensions,
limits,
label: DomRefCell::new(None),
device,
}
}

#[allow(unsafe_code)]
pub fn new(
global: &GlobalScope,
adapter: &GPUAdapter,
extensions: Heap<*mut JSObject>,
limits: Heap<*mut JSObject>,
device: WebGPUDevice,
) -> DomRoot<GPUDevice> {
reflect_dom_object(
Box::new(GPUDevice::new_inherited(
adapter, extensions, limits, device,
)),
global,
GPUDeviceBinding::Wrap,
)
}
}

impl GPUDeviceMethods for GPUDevice {
/// https://gpuweb.github.io/gpuweb/#dom-gpudevice-adapter
fn Adapter(&self) -> DomRoot<GPUAdapter> {
DomRoot::from_ref(&self.adapter)
}

/// https://gpuweb.github.io/gpuweb/#dom-gpudevice-extensions
fn Extensions(&self, _cx: SafeJSContext) -> NonNull<JSObject> {
NonNull::new(self.extensions.get()).unwrap()
}

/// https://gpuweb.github.io/gpuweb/#dom-gpudevice-limits
fn Limits(&self, _cx: SafeJSContext) -> NonNull<JSObject> {
NonNull::new(self.extensions.get()).unwrap()
}

/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn GetLabel(&self) -> Option<DOMString> {
self.label.borrow().clone()
}

/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn SetLabel(&self, value: Option<DOMString>) {
*self.label.borrow_mut() = value;
}
}
4 changes: 4 additions & 0 deletions components/script/dom/identityhub.rs
Expand Up @@ -44,4 +44,8 @@ impl Identities {
pub fn create_adapter_id(&mut self) -> AdapterId {
self.hub.adapters.alloc()
}

pub fn create_device_id(&mut self) -> DeviceId {
self.hub.devices.alloc()
}
}
1 change: 1 addition & 0 deletions components/script/dom/mod.rs
Expand Up @@ -317,6 +317,7 @@ pub mod gamepadlist;
pub mod globalscope;
pub mod gpu;
pub mod gpuadapter;
pub mod gpudevice;
pub mod hashchangeevent;
pub mod headers;
pub mod history;
Expand Down
6 changes: 5 additions & 1 deletion components/script/dom/navigator.rs
Expand Up @@ -26,7 +26,7 @@ use crate::dom::xr::XR;
use dom_struct::dom_struct;
use std::cell::RefCell;
use std::rc::Rc;
use webgpu::wgpu::AdapterId;
use webgpu::wgpu::{AdapterId, DeviceId};

#[dom_struct]
pub struct Navigator {
Expand Down Expand Up @@ -76,6 +76,10 @@ impl Navigator {
pub fn create_adapter_id(&self) -> AdapterId {
self.gpu_id_hub.borrow_mut().create_adapter_id()
}

pub fn create_device_id(&self) -> DeviceId {
self.gpu_id_hub.borrow_mut().create_device_id()
}
}

impl NavigatorMethods for Navigator {
Expand Down
22 changes: 21 additions & 1 deletion components/script/dom/webidls/GPUAdapter.webidl
Expand Up @@ -10,5 +10,25 @@ interface GPUAdapter {
//readonly attribute GPULimits limits; Don’t expose higher limits for now.

// May reject with DOMException // TODO: DOMException("OperationError")?
// Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
};

dictionary GPUDeviceDescriptor : GPUObjectDescriptorBase {
GPUExtensions extensions = {};
GPULimits limits = {};
};

dictionary GPUExtensions {
boolean anisotropicFiltering = false;
};

dictionary GPULimits {
unsigned long maxBindGroups = 4;
unsigned long maxDynamicUniformBuffersPerPipelineLayout = 8;
unsigned long maxDynamicStorageBuffersPerPipelineLayout = 4;
unsigned long maxSampledTexturesPerShaderStage = 16;
unsigned long maxSamplersPerShaderStage = 16;
unsigned long maxStorageBuffersPerShaderStage = 4;
unsigned long maxStorageTexturesPerShaderStage = 4;
unsigned long maxUniformBuffersPerShaderStage = 12;
};
31 changes: 31 additions & 0 deletions components/script/dom/webidls/GPUDevice.webidl
@@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

// https://gpuweb.github.io/gpuweb/#gpudevice
[Exposed=(Window, DedicatedWorker)/*, Serializable */, Pref="dom.webgpu.enabled"]
interface GPUDevice : EventTarget {
readonly attribute GPUAdapter adapter;
readonly attribute object extensions;
readonly attribute object limits;

/*GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
GPUTexture createTexture(GPUTextureDescriptor descriptor);
GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
GPUQueue getQueue();*/
};
GPUDevice includes GPUObjectBase;
13 changes: 13 additions & 0 deletions components/script/dom/webidls/GPUObjectBase.webidl
@@ -0,0 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

// https://gpuweb.github.io/gpuweb/#gpuobjectbase
[Exposed=(Window)]
interface mixin GPUObjectBase {
attribute DOMString? label;
};

dictionary GPUObjectDescriptorBase {
DOMString? label;
};
45 changes: 35 additions & 10 deletions components/webgpu/lib.rs
Expand Up @@ -12,12 +12,12 @@ pub extern crate wgpu_native as wgpu;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use servo_config::pref;
use wgpu::adapter_get_info;
use wgpu::{adapter_get_info, adapter_request_device};

#[derive(Debug, Deserialize, Serialize)]
pub enum WebGPUResponse {
RequestAdapter(String, WebGPUAdapter),
RequestDevice,
RequestDevice(WebGPUDevice, wgpu::DeviceDescriptor),
}

pub type WebGPUResponseResult = Result<WebGPUResponse, String>;
Expand All @@ -29,7 +29,12 @@ pub enum WebGPURequest {
wgpu::RequestAdapterOptions,
wgpu::AdapterId,
),
RequestDevice,
RequestDevice(
IpcSender<WebGPUResponseResult>,
WebGPUAdapter,
wgpu::DeviceDescriptor,
wgpu::DeviceId,
),
Exit(IpcSender<()>),
}

Expand Down Expand Up @@ -124,7 +129,18 @@ impl WGPU {
)
}
},
WebGPURequest::RequestDevice => {},
WebGPURequest::RequestDevice(sender, adapter, descriptor, id) => {
let _output = gfx_select!(id => adapter_request_device(&self.global, adapter.0, &descriptor, id));
let device = WebGPUDevice(id);
if let Err(e) =
sender.send(Ok(WebGPUResponse::RequestDevice(device, descriptor)))
{
warn!(
"Failed to send response to WebGPURequest::RequestDevice ({})",
e
)
}
},
WebGPURequest::Exit(sender) => {
self.deinit();
if let Err(e) = sender.send(()) {
Expand All @@ -137,11 +153,20 @@ impl WGPU {
}
}

#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct WebGPUAdapter(pub wgpu::AdapterId);
macro_rules! webgpu_resource {
($name:ident, $id:ty) => {
#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)]
pub struct $name(pub $id);

impl MallocSizeOf for WebGPUAdapter {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
impl MallocSizeOf for $name {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}

impl Eq for $name {}
};
}

webgpu_resource!(WebGPUAdapter, wgpu::AdapterId);
webgpu_resource!(WebGPUDevice, wgpu::DeviceId);

0 comments on commit b15d2bb

Please sign in to comment.