Skip to content

Commit

Permalink
Initial implementation of WebGPU API
Browse files Browse the repository at this point in the history
  • Loading branch information
Zakor Gyula committed Nov 21, 2019
1 parent 47e39af commit 12893aa
Show file tree
Hide file tree
Showing 30 changed files with 923 additions and 7 deletions.
395 changes: 389 additions & 6 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions components/config/opts.rs
Expand Up @@ -713,6 +713,12 @@ pub fn from_cmdline_args(mut opts: Options, args: &[String]) -> ArgumentParsingR
"A preference to set to enable",
"dom.bluetooth.enabled",
);
opts.optmulti(
"",
"pref",
"A preference to set to enable",
"dom.webgpu.enabled",
);
opts.optflag("b", "no-native-titlebar", "Do not use native titlebar");
opts.optflag("w", "webrender", "Use webrender backend");
opts.optopt("G", "graphics", "Select graphics backend (gl or es2)", "gl");
Expand Down
3 changes: 3 additions & 0 deletions components/config/prefs.rs
Expand Up @@ -159,6 +159,9 @@ mod gen {
},
},
dom: {
webgpu: {
enabled: bool,
},
bluetooth: {
enabled: bool,
testing: {
Expand Down
1 change: 1 addition & 0 deletions components/constellation/Cargo.toml
Expand Up @@ -47,6 +47,7 @@ servo_geometry = {path = "../geometry"}
servo_rand = {path = "../rand"}
servo_remutex = {path = "../remutex"}
servo_url = {path = "../url"}
webgpu = {path = "../webgpu"}
webvr_traits = {path = "../webvr_traits"}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]}
Expand Down
21 changes: 21 additions & 0 deletions components/constellation/constellation.rs
Expand Up @@ -172,6 +172,7 @@ use std::sync::Arc;
use std::thread;
use style_traits::viewport::ViewportConstraints;
use style_traits::CSSPixel;
use webgpu::WebGPU;
use webvr_traits::{WebVREvent, WebVRMsg};

type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, HistoryEntryReplacement)>;
Expand Down Expand Up @@ -440,6 +441,10 @@ pub struct Constellation<Message, LTF, STF> {
/// Entry point to create and get channels to a WebGLThread.
webgl_threads: Option<WebGLThreads>,

/// An IPC channel for the constellation to send messages to the
/// WebGPU threads.
webgpu: Option<WebGPU>,

/// A channel through which messages can be sent to the webvr thread.
webvr_chan: Option<IpcSender<WebVRMsg>>,

Expand Down Expand Up @@ -521,6 +526,9 @@ pub struct InitialConstellationState {
/// Entry point to create and get channels to a WebGLThread.
pub webgl_threads: Option<WebGLThreads>,

/// A channel to the WebGPU threads.
pub webgpu: Option<WebGPU>,

/// A channel to the webgl thread.
pub webvr_chan: Option<IpcSender<WebVRMsg>>,

Expand Down Expand Up @@ -836,6 +844,7 @@ where
(rng, prob)
}),
webgl_threads: state.webgl_threads,
webgpu: state.webgpu,
webvr_chan: state.webvr_chan,
webxr_registry: state.webxr_registry,
canvas_chan: CanvasPaintThread::start(),
Expand Down Expand Up @@ -1090,6 +1099,7 @@ where
.webgl_threads
.as_ref()
.map(|threads| threads.pipeline()),
webgpu: self.webgpu.clone(),
webvr_chan: self.webvr_chan.clone(),
webxr_registry: self.webxr_registry.clone(),
player_context: self.player_context.clone(),
Expand Down Expand Up @@ -2355,6 +2365,17 @@ where
}
}

if let Some(webgpu) = self.webgpu.as_ref() {
debug!("Exiting WebGPU thread.");
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
if let Err(e) = webgpu.exit(sender) {
warn!("Exit WebGPU Thread failed ({})", e);
}
if let Err(e) = receiver.recv() {
warn!("Failed to receive exit response from WebGPU ({})", e);
}
}

if let Some(chan) = self.webvr_chan.as_ref() {
debug!("Exiting WebVR thread.");
if let Err(e) = chan.send(WebVRMsg::Exit) {
Expand Down
7 changes: 7 additions & 0 deletions components/constellation/pipeline.rs
Expand Up @@ -46,6 +46,7 @@ use std::process;
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use webgpu::WebGPU;
use webvr_traits::WebVRMsg;

/// A `Pipeline` is the constellation's view of a `Document`. Each pipeline has an
Expand Down Expand Up @@ -188,6 +189,9 @@ pub struct InitialPipelineState {
/// A channel to the WebGL thread.
pub webgl_chan: Option<WebGLPipeline>,

/// A channel to the WebGPU threads.
pub webgpu: Option<WebGPU>,

/// A channel to the webvr thread.
pub webvr_chan: Option<IpcSender<WebVRMsg>>,

Expand Down Expand Up @@ -299,6 +303,7 @@ impl Pipeline {
webrender_document: state.webrender_document,
webgl_chan: state.webgl_chan,
webvr_chan: state.webvr_chan,
webgpu: state.webgpu,
webxr_registry: state.webxr_registry,
player_context: state.player_context,
};
Expand Down Expand Up @@ -504,6 +509,7 @@ pub struct UnprivilegedPipelineContent {
webrender_api_sender: webrender_api::RenderApiSender,
webrender_document: webrender_api::DocumentId,
webgl_chan: Option<WebGLPipeline>,
webgpu: Option<WebGPU>,
webvr_chan: Option<IpcSender<WebVRMsg>>,
webxr_registry: webxr_api::Registry,
player_context: WindowGLContext,
Expand Down Expand Up @@ -556,6 +562,7 @@ impl UnprivilegedPipelineContent {
pipeline_namespace_id: self.pipeline_namespace_id,
content_process_shutdown_chan: content_process_shutdown_chan,
webgl_chan: self.webgl_chan,
webgpu: self.webgpu,
webvr_chan: self.webvr_chan,
webxr_registry: self.webxr_registry,
webrender_document: self.webrender_document,
Expand Down
1 change: 1 addition & 0 deletions components/script/Cargo.toml
Expand Up @@ -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"]}
Expand Down
5 changes: 4 additions & 1 deletion components/script/dom/bindings/codegen/Bindings.conf
Expand Up @@ -134,7 +134,10 @@ DOMInterfaces = {

'XR': {
'inCompartments': ['SupportsSessionMode', 'RequestSession'],
}
},

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

}
3 changes: 3 additions & 0 deletions components/script/dom/bindings/trace.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
158 changes: 158 additions & 0 deletions components/script/dom/gpu.rs
@@ -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,
))),
}
}
}
63 changes: 63 additions & 0 deletions components/script/dom/gpuadapter.rs
@@ -0,0 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

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

0 comments on commit 12893aa

Please sign in to comment.