Auto merge of #24708 - szeged:webgpu-base, r=gterzian,kvark

Initial implementation of WebGPU API

<!-- Please describe your changes on the following line: -->
- Added the WebIDL bindings for GPU and GPUadapter interfaces.
- Created a background thread for WebGPU api calls.
- Established the async communication between the background thread and the WebGPU interfaces.
- Implemented the `requesAdapter` function of `navigator.gpu`

`./mach test-tidy` reports an error due to the different `arrayvec` version used in `servo` and `webgpu`, so added it to the ignore list in `servo-tidy.toml`

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [ ] `./mach test-tidy` does not report any errors
- [ ] These changes addresses a part of #https://github.com/servo/servo/issues/24706

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

cc @jdm, cc @kvark

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/24708)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-11-25 00:00:46 -05:00 committed by GitHub
commit ea32495504
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 853 additions and 7 deletions

View file

@ -113,6 +113,7 @@ utf-8 = "0.7"
uuid = {version = "0.8", features = ["v4"]}
xml5ever = "0.16"
webdriver = "0.40"
webgpu = {path = "../webgpu"}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
webvr_traits = {path = "../webvr_traits"}
webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]}

View file

@ -134,7 +134,10 @@ DOMInterfaces = {
'XR': {
'inCompartments': ['SupportsSessionMode', 'RequestSession'],
},
'GPU': {
'inCompartments': ['RequestAdapter'],
}
}

View file

@ -145,6 +145,7 @@ use tendril::stream::LossyDecoder;
use tendril::{StrTendril, TendrilSink};
use time::{Duration, Timespec};
use uuid::Uuid;
use webgpu::{WebGPU, WebGPUAdapter};
use webrender_api::{DocumentId, ImageKey, RenderApiSender};
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
use webxr_api::SwapChainId as WebXRSwapChainId;
@ -502,6 +503,8 @@ 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!(WebGPUAdapter);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
unsafe_no_jsmanaged_fields!(MediaList);
unsafe_no_jsmanaged_fields!(WebVRGamepadData, WebVRGamepadState, WebVRGamepadHand);

View file

@ -0,0 +1,158 @@
/* 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::compartments::InCompartment;
use crate::dom::bindings::codegen::Bindings::GPUBinding::GPURequestAdapterOptions;
use crate::dom::bindings::codegen::Bindings::GPUBinding::{self, GPUMethods, GPUPowerPreference};
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::task_source::TaskSource;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::Heap;
use std::rc::Rc;
use webgpu::wgpu;
use webgpu::{WebGPU, WebGPURequest, WebGPUResponse, WebGPUResponseResult};
#[dom_struct]
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, GPUBinding::Wrap)
}
fn wgpu_channel(&self) -> Option<WebGPU> {
self.global().as_window().webgpu_channel()
}
}
pub trait AsyncWGPUListener {
fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>);
}
struct WGPUResponse<T: AsyncWGPUListener + DomObject> {
trusted: TrustedPromise,
receiver: Trusted<T>,
}
impl<T: AsyncWGPUListener + DomObject> WGPUResponse<T> {
#[allow(unrooted_must_root)]
fn response(self, response: WebGPUResponseResult) {
let promise = self.trusted.root();
match response {
Ok(response) => self.receiver.root().handle_response(response, &promise),
Err(error) => promise.reject_error(Error::Type(format!(
"Received error from WebGPU thread: {}",
error
))),
}
}
}
pub fn response_async<T: AsyncWGPUListener + DomObject + 'static>(
promise: &Rc<Promise>,
receiver: &T,
) -> IpcSender<WebGPUResponseResult> {
let (action_sender, action_receiver) = ipc::channel().unwrap();
let (task_source, canceller) = receiver
.global()
.as_window()
.task_manager()
.dom_manipulation_task_source_with_canceller();
let mut trusted = Some(TrustedPromise::new(promise.clone()));
let trusted_receiver = Trusted::new(receiver);
ROUTER.add_route(
action_receiver.to_opaque(),
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.to().unwrap());
}),
&canceller,
);
if let Err(err) = result {
error!("Failed to queue GPU listener-task: {:?}", err);
}
}),
);
action_sender
}
impl GPUMethods for GPU {
// https://gpuweb.github.io/gpuweb/#dom-gpu-requestadapter
fn RequestAdapter(
&self,
options: &GPURequestAdapterOptions,
comp: InCompartment,
) -> Rc<Promise> {
let promise = Promise::new_in_current_compartment(&self.global(), comp);
let sender = response_async(&promise, self);
let power_preference = match options.powerPreference {
Some(GPUPowerPreference::Low_power) => wgpu::PowerPreference::LowPower,
Some(GPUPowerPreference::High_performance) => wgpu::PowerPreference::HighPerformance,
None => wgpu::PowerPreference::Default,
};
match self.wgpu_channel() {
Some(channel) => {
channel
.0
.send(WebGPURequest::RequestAdapter(
sender,
wgpu::RequestAdapterOptions { power_preference },
))
.unwrap();
},
None => promise.reject_error(Error::Type("No WebGPU thread...".to_owned())),
};
promise
}
}
impl AsyncWGPUListener for GPU {
fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) {
match response {
WebGPUResponse::RequestAdapter(name, adapter) => {
let adapter = GPUAdapter::new(
&self.global(),
DOMString::from(name),
Heap::default(),
adapter,
);
promise.resolve_native(&adapter);
},
response => promise.reject_error(Error::Type(format!(
"Wrong response received for GPU from WebGPU thread {:?}",
response,
))),
}
}
}

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 crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::{self, GPUAdapterMethods};
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::script_runtime::JSContext as SafeJSContext;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject};
use std::ptr::NonNull;
use webgpu::WebGPUAdapter;
#[dom_struct]
pub struct GPUAdapter {
reflector_: Reflector,
name: DOMString,
#[ignore_malloc_size_of = "mozjs"]
extensions: Heap<*mut JSObject>,
adapter: WebGPUAdapter,
}
impl GPUAdapter {
pub fn new_inherited(
name: DOMString,
extensions: Heap<*mut JSObject>,
adapter: WebGPUAdapter,
) -> GPUAdapter {
GPUAdapter {
reflector_: Reflector::new(),
name,
extensions,
adapter,
}
}
pub fn new(
global: &GlobalScope,
name: DOMString,
extensions: Heap<*mut JSObject>,
adapter: WebGPUAdapter,
) -> DomRoot<GPUAdapter> {
reflect_dom_object(
Box::new(GPUAdapter::new_inherited(name, extensions, adapter)),
global,
GPUAdapterBinding::Wrap,
)
}
}
impl GPUAdapterMethods for GPUAdapter {
// https://gpuweb.github.io/gpuweb/#dom-gpuadapter-name
fn Name(&self) -> DOMString {
self.name.clone()
}
// https://gpuweb.github.io/gpuweb/#dom-gpuadapter-extensions
fn Extensions(&self, _cx: SafeJSContext) -> NonNull<JSObject> {
NonNull::new(self.extensions.get()).unwrap()
}
}

View file

@ -315,6 +315,8 @@ pub mod gamepadbuttonlist;
pub mod gamepadevent;
pub mod gamepadlist;
pub mod globalscope;
pub mod gpu;
pub mod gpuadapter;
pub mod hashchangeevent;
pub mod headers;
pub mod history;

View file

@ -11,6 +11,7 @@ use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::bluetooth::Bluetooth;
use crate::dom::gamepadlist::GamepadList;
use crate::dom::gpu::GPU;
use crate::dom::mediadevices::MediaDevices;
use crate::dom::mediasession::MediaSession;
use crate::dom::mimetypearray::MimeTypeArray;
@ -36,6 +37,7 @@ pub struct Navigator {
gamepads: MutNullableDom<GamepadList>,
permissions: MutNullableDom<Permissions>,
mediasession: MutNullableDom<MediaSession>,
gpu: MutNullableDom<GPU>,
}
impl Navigator {
@ -51,6 +53,7 @@ impl Navigator {
gamepads: Default::default(),
permissions: Default::default(),
mediasession: Default::default(),
gpu: Default::default(),
}
}
@ -205,4 +208,9 @@ impl NavigatorMethods for Navigator {
MediaSession::new(window)
})
}
// https://gpuweb.github.io/gpuweb/#dom-navigator-gpu
fn Gpu(&self) -> DomRoot<GPU> {
self.gpu.or_init(|| GPU::new(&self.global()))
}
}

View file

@ -0,0 +1,21 @@
/* 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/#gpu-interface
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
interface GPU {
// May reject with DOMException // TODO: DOMException("OperationError")?
Promise<GPUAdapter> requestAdapter(optional GPURequestAdapterOptions options = {});
};
// https://gpuweb.github.io/gpuweb/#dictdef-gpurequestadapteroptions
dictionary GPURequestAdapterOptions {
GPUPowerPreference powerPreference;
};
// https://gpuweb.github.io/gpuweb/#enumdef-gpupowerpreference
enum GPUPowerPreference {
"low-power",
"high-performance"
};

View file

@ -0,0 +1,14 @@
/* 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/#gpuadapter
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
interface GPUAdapter {
readonly attribute DOMString name;
readonly attribute object extensions;
//readonly attribute GPULimits limits; Dont expose higher limits for now.
// May reject with DOMException // TODO: DOMException("OperationError")?
// Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
};

View file

@ -75,3 +75,8 @@ partial interface Navigator {
partial interface Navigator {
[Pref="dom.gamepad.enabled"] GamepadList getGamepads();
};
[Exposed=Window]
partial interface Navigator {
[SameObject, Pref="dom.webgpu.enabled"] readonly attribute GPU gpu;
};

View file

@ -15,3 +15,8 @@ WorkerNavigator includes NavigatorLanguage;
partial interface WorkerNavigator {
[Pref="dom.permissions.enabled"] readonly attribute Permissions permissions;
};
[Exposed=DedicatedWorker]
partial interface WorkerNavigator {
[SameObject, Pref="dom.webgpu.enabled"] readonly attribute GPU gpu;
};

View file

@ -136,6 +136,7 @@ use style::str::HTML_SPACE_CHARACTERS;
use style::stylesheets::CssRuleType;
use style_traits::{CSSPixel, DevicePixel, ParsingMode};
use url::Position;
use webgpu::WebGPU;
use webrender_api::units::{DeviceIntPoint, DeviceIntSize, LayoutPixel};
use webrender_api::{DocumentId, ExternalScrollId, RenderApiSender};
use webvr_traits::WebVRMsg;
@ -267,6 +268,10 @@ pub struct Window {
#[ignore_malloc_size_of = "channels are hard"]
webgl_chan: Option<WebGLChan>,
#[ignore_malloc_size_of = "channels are hard"]
/// A handle for communicating messages to the WebGPU threads.
webgpu: Option<WebGPU>,
/// A handle for communicating messages to the webvr thread, if available.
#[ignore_malloc_size_of = "channels are hard"]
webvr_chan: Option<IpcSender<WebVRMsg>>,
@ -462,6 +467,10 @@ impl Window {
.map(|chan| WebGLCommandSender::new(chan.clone(), self.get_event_loop_waker()))
}
pub fn webgpu_channel(&self) -> Option<WebGPU> {
self.webgpu.clone()
}
pub fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> {
self.webvr_chan.clone()
}
@ -2206,6 +2215,7 @@ impl Window {
navigation_start: u64,
navigation_start_precise: u64,
webgl_chan: Option<WebGLChan>,
webgpu: Option<WebGPU>,
webvr_chan: Option<IpcSender<WebVRMsg>>,
webxr_registry: webxr_api::Registry,
microtask_queue: Rc<MicrotaskQueue>,
@ -2285,6 +2295,7 @@ impl Window {
media_query_lists: DOMTracker::new(),
test_runner: Default::default(),
webgl_chan,
webgpu,
webvr_chan,
webxr_registry,
permission_state_invocation_results: Default::default(),

View file

@ -7,6 +7,7 @@ use crate::dom::bindings::codegen::Bindings::WorkerNavigatorBinding::WorkerNavig
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::gpu::GPU;
use crate::dom::navigatorinfo;
use crate::dom::permissions::Permissions;
use crate::dom::workerglobalscope::WorkerGlobalScope;
@ -17,6 +18,7 @@ use dom_struct::dom_struct;
pub struct WorkerNavigator {
reflector_: Reflector,
permissions: MutNullableDom<Permissions>,
gpu: MutNullableDom<GPU>,
}
impl WorkerNavigator {
@ -24,6 +26,7 @@ impl WorkerNavigator {
WorkerNavigator {
reflector_: Reflector::new(),
permissions: Default::default(),
gpu: Default::default(),
}
}
@ -97,4 +100,9 @@ impl WorkerNavigatorMethods for WorkerNavigator {
self.permissions
.or_init(|| Permissions::new(&self.global()))
}
// https://gpuweb.github.io/gpuweb/#dom-navigator-gpu
fn Gpu(&self) -> DomRoot<GPU> {
self.gpu.or_init(|| GPU::new(&self.global()))
}
}

View file

@ -163,6 +163,7 @@ use style::dom::OpaqueNode;
use style::thread_state::{self, ThreadState};
use time::{at_utc, get_time, precise_time_ns, Timespec};
use url::Position;
use webgpu::WebGPU;
use webrender_api::units::LayoutPixel;
use webrender_api::{DocumentId, RenderApiSender};
use webvr_traits::{WebVREvent, WebVRMsg};
@ -629,6 +630,9 @@ pub struct ScriptThread {
/// A handle to the WebGL thread
webgl_chan: Option<WebGLPipeline>,
/// A handle to the WebGPU threads
webgpu: Option<WebGPU>,
/// A handle to the webvr thread, if available
webvr_chan: Option<IpcSender<WebVRMsg>>,
@ -1338,6 +1342,7 @@ impl ScriptThread {
layout_to_constellation_chan: state.layout_to_constellation_chan,
webgl_chan: state.webgl_chan,
webgpu: state.webgpu,
webvr_chan: state.webvr_chan,
webxr_registry: state.webxr_registry,
@ -3247,6 +3252,7 @@ impl ScriptThread {
incomplete.navigation_start,
incomplete.navigation_start_precise,
self.webgl_chan.as_ref().map(|chan| chan.channel()),
self.webgpu.clone(),
self.webvr_chan.clone(),
self.webxr_registry.clone(),
self.microtask_queue.clone(),