WebVR API Implementation #14618

Merged
merged 1 commit into from Jan 9, 2017

Projects

None yet
@MortimerGoro
Contributor
MortimerGoro commented Dec 16, 2016 edited

WebVR API Implementation with HTC Vive support on Windows. The current implementations only enables the WebVR support on Windows. In other platforms the API is available on JavaScript but navigator.vr.getDisplays() returns an empty array. This will change when we add support for more VR providers and platforms ;)

Info about the architecture:
https://blog.mozvr.com/webvr-servo-architecture-and-latency-optimizations/

  • ./mach build -d does not report any errors
  • ./mach test-tidy does not report any errors
  • These changes fix #__ (github issue number if applicable).
  • There are tests for these changes OR
  • These changes do not require tests because _____

Proprietary openvr.dll must be copied next to servo.exe in order to test on HTC Vive (https://github.com/ValveSoftware/openvr/tree/master/bin/win64) I have added some of the official WebVR samples for testing. Switch on your headset and run:

mach run tests/html/webvr/room-scale.html


This change is Reviewable

@emilio emilio was assigned by highfive Dec 16, 2016
@highfive

Heads up! This PR modifies the following files:

  • @asajeffrey: components/constellation/lib.rs, components/constellation/pipeline.rs, components/constellation/Cargo.toml, components/constellation/constellation.rs
  • @kichjang: components/script/dom/webidls/VRDisplayCapabilities.webidl, components/script/dom/navigator.rs, components/script/dom/vrdisplay.rs, components/script/dom/webidls/VRFrameData.webidl, components/script/dom/mod.rs, components/script/dom/vr.rs, components/script/dom/vrdisplaycapabilities.rs, components/script/dom/vrstageparameters.rs, components/script/dom/vrdisplayevent.rs, components/script/dom/window.rs, components/script/dom/webidls/VREyeParameters.webidl, components/script_traits/lib.rs, components/script_traits/lib.rs, components/script/lib.rs, components/script/dom/webidls/Window.webidl, components/script/dom/vreyeparameters.rs, components/script/dom/webidls/VRStageParameters.webidl, components/script/dom/vrframedata.rs, components/script/dom/macros.rs, components/script/dom/vrpose.rs, components/script/dom/webidls/Navigator.webidl, components/script/dom/webidls/VR.webidl, components/script/dom/webidls/VRPose.webidl, components/script/Cargo.toml, components/script/dom/webidls/VRDisplay.webidl, components/script/dom/webidls/VRDisplayEvent.webidl, components/script/script_thread.rs, components/script/script_runtime.rs, components/script/dom/vrfieldofview.rs, components/script/dom/webidls/VRFieldOfView.webidl, components/script/dom/bindings/conversions.rs, components/script/dom/webidls/VRLayer.webidl, components/script_traits/Cargo.toml, components/script_traits/Cargo.toml
  • @fitzgen: components/script/dom/webidls/VRDisplayCapabilities.webidl, components/script/dom/navigator.rs, components/script/dom/vrdisplay.rs, components/script/dom/webidls/VRFrameData.webidl, components/script/dom/mod.rs, components/script/dom/vr.rs, components/script/dom/vrdisplaycapabilities.rs, components/script/dom/vrstageparameters.rs, components/script/dom/vrdisplayevent.rs, components/script/dom/window.rs, components/script/dom/webidls/VREyeParameters.webidl, components/script_traits/lib.rs, components/script_traits/lib.rs, components/script/lib.rs, components/script/dom/webidls/Window.webidl, components/script/dom/vreyeparameters.rs, components/script/dom/webidls/VRStageParameters.webidl, components/script/dom/vrframedata.rs, components/script/dom/macros.rs, components/script/dom/vrpose.rs, components/script/dom/webidls/Navigator.webidl, components/script/dom/webidls/VR.webidl, components/script/dom/webidls/VRPose.webidl, components/profile/time.rs, components/script/Cargo.toml, components/script/dom/webidls/VRDisplay.webidl, components/profile_traits/time.rs, components/profile_traits/time.rs, components/script/dom/webidls/VRDisplayEvent.webidl, components/script/script_thread.rs, components/script/script_runtime.rs, components/script/dom/vrfieldofview.rs, components/script/dom/webidls/VRFieldOfView.webidl, components/script/dom/bindings/conversions.rs, components/script/dom/webidls/VRLayer.webidl, components/script_traits/Cargo.toml, components/script_traits/Cargo.toml
  • @emilio: components/canvas/webgl_paint_thread.rs
@highfive

warning Warning warning

  • These commits modify unsafe code. Please review it carefully!
  • These commits modify script code, but no tests are modified. Please consider adding a test!
@bholley bholley assigned larsbergstrom and unassigned emilio Dec 16, 2016
@emilio

Just some mostly-superficial comments, since @larsbergstrom has kindly offered to take over the review.

I guess adding automatic tests for this is not truly feasible? Is there a plan to test this stuff?

components/script/script_thread.rs
@@ -2197,6 +2207,13 @@ impl ScriptThread {
}
}
+ fn handle_webvr_event(&self, pipeline_id: PipelineId, event: WebVREventMsg) {
+ if let Some(window) = self.documents.borrow().find_window(pipeline_id) {
@emilio
emilio Dec 16, 2016 Member

Use a different variable for this, see #14589.

components/vr/webvr_thread.rs
+ sender
+ }
+
+ #[allow(unsafe_code)]
@emilio
emilio Dec 16, 2016 Member

there doesn't seem to be unsafe code here?

components/vr/webvr_thread.rs
+
+ fn poll_events(&mut self, sender: IpcSender<bool>) {
+ let events = self.service.poll_events();
+ if events.len() > 0 {
@emilio
emilio Dec 16, 2016 Member

if !events.is_empty()

components/vr/webvr_thread.rs
+ }
+
+ fn schedule_poll_events(&mut self) {
+ if self.service.is_initialized() && !self.polling_events {
@emilio
emilio Dec 16, 2016 Member

nit: Probably nicer to read as:

if !self.service.is_initialized() || self.polling_events {
    return;
}

// ...
components/vr/webvr_thread.rs
+ // WebVR Thread asked to unschedule this thread
+ break;
+ }
+ thread::sleep(time::Duration::from_millis(500));
@emilio
emilio Dec 16, 2016 Member

This seems quite arbitrary. I understand why it's this way, but probably worth moving the 500 to a constant.

components/vr/webvr_thread.rs
+// This also avoids "JS DDoS" attacks: A Second JSContext doing a lot of calls.
+// while the main one is presenting and demands both high framerate and low latency.
+
+pub struct WebVRCompositor(*mut VRDevice);
@emilio
emilio Dec 16, 2016 Member

huh, this is going to need a better comment explaining how it's used and why it's safe and not racy.

@emilio
emilio Dec 16, 2016 Member

Also, would be helpful to know when this VRDevice can be null or not, and how does is this VRDevice freed, since I don't see any Drop impl.

components/vr/webvr_thread.rs
+ (*compositor.0).sync_poses();
+ (*compositor.0).synced_frame_data(near, far).to_bytes()
+ };
+ let _result = sender.send(Ok(pose));
@emilio
emilio Dec 16, 2016 Member

The patterns let _ = sender.send(..) or drop(sender.send()) seem more used across the codebase.

components/vr/webvr_thread.rs
+impl WebVRCompositorHandler {
+ #[allow(unsafe_code)]
+ fn create_compositor(&mut self, device_id: webrender_traits::VRCompositorId) {
+ if let Some(ref sender) = self.webvr_thread_sender {
@emilio
emilio Dec 16, 2016 Member

Probably worth:

let sender = match self.webvr_thread_sender {
    Some(ref s) => s,
    None => return,
};
components/vr/webvr_thread.rs
+ }
+ }
+
+ pub fn set_webvr_thread_sender(&mut self, sender: IpcSender<WebVRMsg>) {
@emilio
emilio Dec 16, 2016 Member

Worth pointing here with a comment that this is done only a per-platform basis on initialization.

components/constellation/pipeline.rs
@@ -119,6 +120,8 @@ pub struct InitialPipelineState {
pub webrender_api_sender: webrender_traits::RenderApiSender,
/// Whether this pipeline is considered private.
pub is_private: bool,
+ /// A channel to the webvr thread.
+ pub webvr_thread: Option<IpcSender<WebVRMsg>>,
@asajeffrey
asajeffrey Dec 16, 2016 Member

When is this used?

@MortimerGoro
MortimerGoro Dec 16, 2016 edited Contributor

It's used to create the ScriptThreadFactory. When a script thread is created it gets a cloned handle to the webvr_thread sender. This sender is used by DOMObjects to comunicate with the WebVRThread and send messages like: getDisplays, requestPresent, etc.

@MortimerGoro
Contributor
MortimerGoro commented Dec 16, 2016 edited

@emilio WRT automated testing it could be done with mocked devices, but it requires more work: like setting up predefined pose animations and comparing images submitted to the display. I have an initial implementation of a MockVRDevice and @dmarcos was working on a mock openvr_api.dll. @dmarcos is there any plan for official WebVR conformance tests? Maybe you have talked about that in the latest W3C workshop/meetings.

@dmarcos
Contributor
dmarcos commented Dec 16, 2016

@MortimerGoro @emilio No official plan for conformance tests. There's a face to face meeting scheduled in January that will touch the subject (among other things). I think in the short we can use your MockVRDevice. In the mid term and for cross browser testing will be better to implement a mock OpenVR driver (instead of the full OpenVR API implementation I had started). In the long term we will probably move to the driver standard that is supposed to come from the khronos vr effort.

@Ms2ger
Contributor
Ms2ger commented Dec 17, 2016

Seems like this should all be behind a pref.

@MortimerGoro
Contributor

@Ms2ger Sounds good, I'll add a new pref to https://github.com/servo/servo/blob/master/components/config/opts.rs that decides if the WebVR API is enabled.

@jdm
Member
jdm commented Dec 17, 2016

@MortimerGoro It belongs in resources/prefs.json and annotated in the WebIDL files using [Pref=] instead of a command-line option.

@MortimerGoro
Contributor

@jdm Thanks for the info, never had played with that json before ;)

@nox

Started looking at it a bit.

@@ -463,6 +466,9 @@ pub unsafe trait ArrayBufferViewContents: Clone {
/// Check if the JS ArrayBufferView type is compatible with the implementor of the
/// trait
fn is_type_compatible(ty: Type) -> bool;
+
+ /// Creates a typed array
+ fn new(cx: *mut JSContext, num: u32) -> *mut JSObject;
@nox
nox Dec 18, 2016 Member

This should be unsafe.

@@ -595,3 +633,25 @@ pub unsafe fn is_array_like(cx: *mut JSContext, value: HandleValue) -> bool {
assert!(JS_IsArrayObject(cx, value, &mut result));
result
}
+
+/// Creates a typed JS array from a Rust slice
+pub fn slice_to_array_buffer_view<T>(cx: *mut JSContext, data: &[T]) -> *mut JSObject
@nox
nox Dec 18, 2016 Member

This should be unsafe.

components/script/dom/navigator.rs
+
+impl Navigator {
+ pub fn handle_webvr_event(&self, event: WebVREventMsg) {
+ if let Some(vr) = self.vr.get() {
@nox
nox Dec 18, 2016 Member

How can this be called with self.vr being None?

components/script/dom/vr.rs
+ return promise;
+ }
+
+ if let Some(wevbr_sender) = self.webvr_thread() {
@nox
nox Dec 18, 2016 Member

Spelling of webvr_sender is wrong, and it is weird to call the variable sender when the method says thread.

+ capabilities: DOMRefCell<WebVRDisplayCapabilities>
+}
+
+// Wrappers required to include WebVR structs in a DOM struct
@nox
nox Dec 18, 2016 Member

Why?

components/script/dom/vrdisplayevent.rs
+
+impl VRDisplayEvent {
+ fn new_inherited(display: &VRDisplay,
+ reason: &Option<VRDisplayEventReason>)
@nox
nox Dec 18, 2016 Member

Why a ref? Pass Option<VRDisplayEventReason>.

components/script/dom/vrdisplayevent.rs
+ display: &VRDisplay,
+ event: &webvr::VRDisplayEvent)
+ -> Root<VRDisplayEvent> {
+ let (name, reason) = match event {
@nox
nox Dec 18, 2016 Member

Nit: match *event.

components/script/dom/vrdisplayevent.rs
+ let (name, reason) = match event {
+ &webvr::VRDisplayEvent::Connect(_) => ("displayconnect", None),
+ &webvr::VRDisplayEvent::Disconnect(_) => ("displaydisconnect", None),
+ &webvr::VRDisplayEvent::Activate(_, ref reason) => ("activate", Some(reason)),
@nox
nox Dec 18, 2016 Member

Why ref? Seems to me like VRDisplayEventReason should be Clone and Copy.

components/script/dom/vreyeparameters.rs
+ fov: JS<VRFieldOfView>,
+}
+
+// Wrappers required to include WebVR structs in a DOM struct
@nox
nox Dec 18, 2016 Member

Why?

@MortimerGoro
MortimerGoro Dec 19, 2016 Contributor

I added the wrappers because Rust didn't allow to implement no_jsmanaged_fields macro or JSTraceable for structs defined in a different crate. I have done a test with the latest rustc compiler used in Servo and it seems to work now. I'll get rid of them now ;)

components/script/dom/vreyeparameters.rs
+
+impl VREyeParameters {
+ #[allow(unrooted_must_root)]
+ fn new_inherited(parameters: &webvr::VREyeParameters, global: &GlobalScope) -> VREyeParameters {
@nox
nox Dec 18, 2016 Member

Why a reference?

@cvan

just feedback from having worked on the spec and helped Kip with Gecko implementation + bugs in the past. I'm not familiar with Rust, so take my webdev ergonomics POV with a grain of salt.

I'd like to iron out the event issues, but all means if the reviewers here okay this PR, feel free to proceed.

truly great work - this is really inspiring to see.

+ self.webvr_thread = Some(webvr_thread)
+ }
+ FromCompositorMsg::WebVREvent(pipeline_ids, event) => {
+ debug!("constellation got webvr event message");
@cvan
cvan Dec 19, 2016

is the message part necessary?

@emilio
emilio Dec 19, 2016 Member

Also, if we go this spell-checking mode, you may want to capitalize WebVR :P

@@ -1181,6 +1195,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
}
+ if let Some(chan) = self.webvr_thread.as_ref() {
+ debug!("Exiting WebVR thread.");
@cvan
cvan Dec 19, 2016

should this be logged outside the if check (à la above)?

@MortimerGoro
MortimerGoro Dec 19, 2016 edited Contributor

I included it inside the if because WebVR can be disabled and it didn't make sense to show that message in that case. I checked and the same pattern is used in ""Exiting devtools" too ;)

components/profile/time.rs
@@ -151,6 +151,7 @@ impl Formattable for ProfilerCategory {
ProfilerCategory::ScriptServiceWorkerEvent => "Script Service Worker Event",
ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen",
ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen",
+ ProfilerCategory::ScriptWebVREvent => "Script Web VR Event",
@cvan
cvan Dec 19, 2016

one word: WebVR

components/script/dom/macros.rs
@@ -495,6 +495,13 @@ macro_rules! window_event_handlers(
event_handler!(unhandledrejection, GetOnunhandledrejection,
SetOnunhandledrejection);
event_handler!(unload, GetOnunload, SetOnunload);
+ event_handler!(vrdisplayconnect, GetOnvrdisplayconnect, SetOnvrdisplayconnect);
@cvan
cvan Dec 19, 2016

I couldn't find one, but is there an issue no track to move these events from window to the VRDisplay interface?

(See w3c/webvr#152.)

@cvan
cvan Dec 19, 2016

but then I see you're implementing navigator.vr.getDisplays instead of navigator.getVRDisplays(). these events should be emitted from the VRDisplay instances not off of window.

@MortimerGoro
MortimerGoro Dec 19, 2016 Contributor

Yes, I'll remove these ones. I started implementing 1.1 API but moved to 1.2. These events are already moved to VRDisplay instances (in the implementation and samples)

components/script/dom/vr.rs
+
+ if let Some(wevbr_sender) = self.webvr_thread() {
+ let (sender, receiver) = ipc::channel().unwrap();
+ wevbr_sender.send(WebVRMsg::GetVRDisplays(sender)).unwrap();
@cvan
cvan Dec 19, 2016

also: GetDisplays

components/script/dom/vr.rs
+
+ // https://w3c.github.io/webvr/#interface-navigator
+ fn VrEnabled(&self) -> bool {
+ // TODO: check iframe
@cvan
cvan Dec 19, 2016

though the common case is for iframes, this necessarily isn't for iframes only. can you file an issue so this isn't forgotten about?

@emilio
emilio Dec 19, 2016 Member

Yes, please fill a tracking issue with all the remaining APIs and pending changes. Feel free to cc me on that.

@MortimerGoro
MortimerGoro Dec 20, 2016 Contributor

I'll remove it because vrEnabled and allowvr are about to be removed from spec: w3c/webvr#25

+ self.displays.borrow()
+ .iter()
+ .find(|d| d.get_display_id() == display_id)
+ .map(|d| Root::from_ref(&**d))
@cvan
cvan Dec 19, 2016

intentionally no semicolons here?

@MortimerGoro
MortimerGoro Dec 19, 2016 Contributor

Yes, because it's a implicit return

+
+// https://w3c.github.io/webvr/#interface-window
+partial interface Window {
+ attribute EventHandler onvrdisplayconnect;
@cvan
cvan Dec 19, 2016

See comment above about firing these on the VRDisplay interface instances, not on window. To enable WebVR to work in workers, we need to move all the interfaces to a single, contained namespace: `navigator.vr: w3c/webvr#152

Sample usage:

navigator.vr.getDisplays().then(displays => {
  if (!displays || !displays.length) {
    return;
  }

  var display = displays[0];
  display.addEventListener('connect', e => {
    var display = e.display;
    connect('VR display <%s> (%d)', display.displayName, display.displayId);
  });
  display.addEventListener('activate', e => {
    var display = e.display;
    var reason = e.reason;
    connect('VR display <%s> (%d) connected for reason "%s"', display.displayName, display.displayId, reason);
  });
}
components/script_traits/lib.rs
@@ -710,6 +718,18 @@ pub enum ConstellationMsg {
Reload,
/// A log entry, with the top-level frame id and thread name
LogEntry(Option<FrameId>, Option<String>, LogEntry),
+ /// Sets the WebVR thread channel
@cvan
cvan Dec 19, 2016

Sets -> Set and end with a period

+}
+
+/// Messages to the constellation originating from the WebVR thread.
+/// Used to dispatch VR Headset state events: connected, unconnected, and more.
@cvan
cvan Dec 19, 2016

the original event names are vrdisplayconnect and vrdisplaydisconnect - so might want to change to those or just connect, disconnect, etc. (per the navigator.vr 1.2 spec changes)

components/servo/lib.rs
@@ -296,6 +301,16 @@ fn create_constellation(user_agent: Cow<'static, str>,
layout_thread::LayoutThread,
script::script_thread::ScriptThread>::start(initial_state);
+ if cfg!(target_os = "windows") {
@cvan
cvan Dec 19, 2016

is there any harm in filtering by target_os now? or have you just not had a chance to try other the OSs, since that wasn't in scope for this initial proof-of-concept implementation?

@MortimerGoro
MortimerGoro Dec 19, 2016 Contributor

It's just because HTC Vive only works on Windows now (no other SDKs implemented yet). I'm going to add a preference for this instead of platform filtering.

components/vr/Cargo.toml
+log = "0.3"
+msg = {path = "../msg"}
+script_traits = {path = "../script_traits"}
+vr_traits = {path = "../vr_traits" }
@cvan
cvan Dec 19, 2016

can remove trailing whitespace

components/vr/webvr_thread.rs
+use vr_traits::webvr::*;
+use webrender_traits;
+
+// Defines the polling interval time in ms for VR Events such as VRDevice connected, disconnected, etc.
@cvan
cvan Dec 19, 2016

VRDevice -> VRDisplay, and same comment as above about the correct event names

@emilio
emilio Dec 19, 2016 Member

Also, I think you can use doc comments for these.

components/vr/webvr_thread.rs
+// Defines the polling interval time in ms for VR Events such as VRDevice connected, disconnected, etc.
+const EVENT_POLLING_INTERVAL: u64 = 500;
+
+// WebVRThread owns native VRDevices, handles their life cycle inside Servo and
@cvan
cvan Dec 19, 2016

life cycle -> lifecycle

@cvan
cvan Dec 19, 2016

VRDevice -> VRDisplay

components/vr/webvr_thread.rs
+
+// WebVRThread owns native VRDevices, handles their life cycle inside Servo and
+// acts a doorman for untrusted VR requests from DOM Objects.
+// It waits for VR Commands from DOM objects and handles them in its trusted thread.
@cvan
cvan Dec 19, 2016

lowercase commands

components/vr/webvr_thread.rs
+ WebVRMsg::PollEvents(sender) => {
+ self.poll_events(sender);
+ },
+ WebVRMsg::GetVRDisplays(sender) => {
@cvan
cvan Dec 19, 2016

can we call this just GetDisplays?

components/vr/webvr_thread.rs
+ self.handle_exit_present(pipeline_id, device_id, sender);
+ },
+ WebVRMsg::CreateCompositor(device_id) => {
+ self.handle_create_compositor(device_id);
@cvan
cvan Dec 19, 2016

when does the compositor get disengaged if not needed for browsing context (non-VR) anymore?

components/vr/webvr_thread.rs
+ }
+ }
+
+ fn access_check(&self, pipeline: PipelineId, device_id: u64) -> Result<&VRDevicePtr, &'static str> {
@cvan
cvan Dec 19, 2016

can we change VRDevicePtr -> VRDisplayTr?

@cvan
cvan Dec 19, 2016

device_id -> display_id?

components/vr/webvr_thread.rs
+
+ fn access_check(&self, pipeline: PipelineId, device_id: u64) -> Result<&VRDevicePtr, &'static str> {
+ if *self.presenting.get(&device_id).unwrap_or(&pipeline) != pipeline {
+ return Err("Device owned by another context");
@cvan
cvan Dec 19, 2016

Device -> VR Display

owned by another context -> can we be more descriptive?

components/vr/webvr_thread.rs
+ }
+
+ fn handle_request_present(&mut self,
+ pipeline: PipelineId,
@cvan
cvan Dec 19, 2016

consistent indentation?

components/vr/webvr_thread.rs
+// Multithreading won't be a problem because:
+// * Thanks to the security rules implemented in the WebVRThread, when a VRDisplay is in a presenting loop
+// no other JSContext is granted access to the VRDisplay. So really there aren’t multithreading race conditions.
+// * VRDevice implementations are designed to allow calling compositor functions
@cvan
cvan Dec 19, 2016

can you change all instances of VRDevice to VRDisplay?

components/vr/webvr_thread.rs
+// VRDevices pointers are guaranteed to be valid memory:
+// * VRDevices are owned by the VRDeviceManager which lives in the WebVRThread.
+// * WebVRCompositorHandler is stopped automatically when a JS tab is closed or the whole browser is closed.
+// * WebVRThread and it's VRDevices are destroyed after all tabs are dropped and the browser is about to exit.
components/vr/webvr_thread.rs
+ self.compositors.insert(device_id, device);
+ },
+ None => {
+ error!("VRDevice not found when creating a new VRCompositor");
@cvan
cvan Dec 19, 2016

VRDisplay -> VRDisplay (or VR Display, if want to be consistent everywhere w/ that usage)

components/vr/webvr_thread.rs
+ };
+ }
+
+ // This is done only a per-platform basis on initialization.
@cvan
cvan Dec 19, 2016

done only -> done on only

components/script/dom/webidls/VR.webidl
+// https://w3c.github.io/webvr/#interface-navigator
+interface VR: EventTarget {
+ Promise<sequence<VRDisplay>> getDisplays();
+ //readonly attribute FrozenArray<VRDisplay> activeVRDisplays;
@cvan
cvan Dec 19, 2016

is there an issue filed to later support this?

components/script/dom/webidls/VR.webidl
+interface VR: EventTarget {
+ Promise<sequence<VRDisplay>> getDisplays();
+ //readonly attribute FrozenArray<VRDisplay> activeVRDisplays;
+ readonly attribute boolean vrEnabled;
@cvan
cvan Dec 19, 2016

I can file a spec issue for this, but since we're already inside the navigator.vr namespace, I would suggest changing this to be isEnabled.

Alternatively, something I've brought this up in the spec discussions a few times: this might ought to be a Promise-based method instead, like so:

var vrButton = document.querySelector('#vr-button');
navigator.vr.getAvailability(isAvailable, => {
  console.log('VR availability:', isAvailable);
  vrButton.disabled = !isAvailable
});
@MortimerGoro
MortimerGoro Dec 20, 2016 Contributor

IMO getAvailability or isEnabled() is a useful API to check VR availability because it doesn't load VRDisplays. I mean that you can check availability using the getDisplays promise but in every browser that probably wakes up the lazy loaded WebVR thread, resources, and more. In a webpage you may want to only know availability without loading all those resources.

@MortimerGoro
MortimerGoro Dec 20, 2016 Contributor

What's the alternative now that vrEnabled is going to be deleted? checking for navigator.vr existence?

@MortimerGoro
MortimerGoro Dec 20, 2016 Contributor

Probably we need to wait for this: https://github.com/WICG/feature-policy

+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrstageparameters-sittingtostandingtransform
+ unsafe fn SittingToStandingTransform(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> {
+ NonZero::new(self.transform.get())
@cvan
cvan Dec 19, 2016

semicolons are fine here, yeah?

@jdm
jdm Dec 19, 2016 Member

Nope.

+
+ // https://w3c.github.io/webvr/#dom-vrstageparameters-sizez
+ fn SizeZ(&self) -> Finite<f32> {
+ Finite::wrap(self.parameters.borrow().0.size_y)
@cvan
cvan Dec 19, 2016

size_y should besize_z

+
+// https://w3c.github.io/webvr/#interface-navigator
+partial interface Navigator {
+ readonly attribute VR vr;
@cvan
cvan Dec 19, 2016

[SameObject, Pref="dom.vr.enabled"] readonly attribute VR vr;?

+ * any of the passed layers the promise is rejected.
+ * If the source of any of the layers is not present (null), the promise is rejected.
+ */
+ Promise<void> requestPresent(sequence<VRLayer> layers);
@cvan
cvan Dec 19, 2016

FYI, this is fine for now, but see this discussion:

+ /**
+ * Get the layers currently being presented.
+ */
+ //sequence<VRLayer> getLayers();
@cvan
cvan Dec 19, 2016

can you file a new issue to support this?

+enum VRDisplayEventReason {
+ "navigation",
+ "mounted",
+ "unmounted",
@cvan
cvan Dec 19, 2016

can you also support requested (see this spec commit?

tests/html/webvr/simple-mirroring.html
+ // canvas backbuffer need to be made. As a result, we ONLY want to set
+ // that if we know the VRDisplay has an external display, which is why
+ // we defer WebGL initialization until after we've gotten results back
+ // from navigator.getVRDisplays and know which device we'll be
@cvan
cvan Dec 19, 2016

will want to update this comment (and others) to reflect your navigator.vr change

+
+// https://w3c.github.io/webvr/#interface-vrlayer
+
+//typedef (HTMLCanvasElement or OffscreenCanvas) VRSource;
@cvan
cvan Dec 19, 2016

is there any harm in introducing the VRSource without support in Servo for OffscreenCanvas yet? (we have the API already shipped in Firefox, albeit behind a flag.) filed servo issue #14627 to track the implementation of it.

@emilio
emilio Dec 19, 2016 Member

Thanks for filling that :)

It's probably fine to add typedef HTMLCanvasElement VRSource for now, but I don't think it's worth it.

+
+dictionary VRLayer {
+ HTMLCanvasElement source;
+ sequence<float> leftBounds;
@cvan
cvan Dec 19, 2016

sure these shouldn't be readonly attribute with defaults of []?

do we not want VRLayerInit, per the spec?

@MortimerGoro
MortimerGoro Dec 20, 2016 Contributor

VRLayerInit is not implemented because some problems with the python autogeneration tool (readonly sequence attributes). I'll file a issue about this

components/vr_traits/Cargo.toml
+msg = {path = "../msg"}
+serde = "0.8"
+serde_derive = "0.8"
+rust-webvr = {version = "0.1", features = ["serde-serialization"] }
@cvan
cvan Dec 19, 2016

can remove the space before the closing curly brace at the end: }

components/vr_traits/Cargo.toml
+serde_derive = "0.8"
+rust-webvr = {version = "0.1", features = ["serde-serialization"] }
+
+
@cvan
cvan Dec 19, 2016

feel free to remove these two superfluous newlines, but keep a single one at EOF

components/vr_traits/webvr_traits.rs
+
+pub type WebVRResult<T> = Result<T, String>;
+
+// Messages from Script Thread to WebVR thread.
@cvan
cvan Dec 19, 2016

Script Thread -> Script thread

components/vr_traits/webvr_traits.rs
+ RegisterContext(PipelineId),
+ UnregisterContext(PipelineId),
+ PollEvents(IpcSender<bool>),
+ GetVRDisplays(IpcSender<WebVRResult<Vec<VRDisplayData>>>),
@cvan
cvan Dec 19, 2016

same here: GetDisplays

@emilio

I took a closer look, here are a few comments :)

components/canvas/canvas_paint_thread.rs
@@ -209,6 +209,7 @@ impl<'a> CanvasPaintThread<'a> {
}
}
CanvasMsg::WebGL(_) => panic!("Wrong message sent to Canvas2D thread"),
+ CanvasMsg::WebVR(_) => panic!("Wrong message sent to Canvas2D thread"),
@emilio
emilio Dec 19, 2016 Member

It'd be nice to differentiate these error messages now.

components/canvas/webgl_paint_thread.rs
+ WebGLPaintTaskData::WebRender(ref api, id) => {
+ api.send_vr_compositor_command(id, message);
+ }
+ WebGLPaintTaskData::Readback(_, _, _) => {
@emilio
emilio Dec 19, 2016 Member

nit: You can use WebGLPaintTaskData::Readback(..) if you want.

@@ -872,6 +879,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
FromCompositorMsg::LogEntry(top_level_frame_id, thread_name, entry) => {
self.handle_log_entry(top_level_frame_id, thread_name, entry);
}
+ FromCompositorMsg::SetWebVRThread(webvr_thread) => {
+ self.webvr_thread = Some(webvr_thread)
@emilio
emilio Dec 19, 2016 Member

Probably is worth sanity-checking with something like: assert!(self.webvr_thread.is_none())

+ self.webvr_thread = Some(webvr_thread)
+ }
+ FromCompositorMsg::WebVREvent(pipeline_ids, event) => {
+ debug!("constellation got webvr event message");
@emilio
emilio Dec 19, 2016 Member

Also, if we go this spell-checking mode, you may want to capitalize WebVR :P

components/script/dom/navigator.rs
+ fn Vr(&self) -> Root<VR> {
+ self.vr.or_init(|| VR::new(&self.global()))
+ }
+
@emilio
emilio Dec 19, 2016 Member

nit: Extra newline.

components/script/dom/vr.rs
+
+ // https://w3c.github.io/webvr/#interface-navigator
+ fn VrEnabled(&self) -> bool {
+ // TODO: check iframe
@emilio
emilio Dec 19, 2016 Member

Yes, please fill a tracking issue with all the remaining APIs and pending changes. Feel free to cc me on that.

components/script/dom/vr.rs
+ }
+
+ pub fn handle_webvr_event(&self, event: WebVREventMsg) {
+ let event = match event {
@emilio
emilio Dec 19, 2016 Member

I think you can do:

let WebVREventMsg::DisplayEvent(event) = event;

That should also error out when you add more variants in the future.

components/script/dom/vr.rs
+ WebVREventMsg::DisplayEvent(display_event) => display_event
+ };
+
+ match &event {
@emilio
emilio Dec 19, 2016 Member

nit: You can do match event directly, and remove all those ampersands.

+ self.notify_event(&display, &event);
+ }
+ },
+ &webvr::VRDisplayEvent::Activate(ref display, _) |
@emilio
emilio Dec 19, 2016 Member

Oh I see, this is because you borrow event afterwards, right?

Then you can do something like:

ref event @ webvr::VRDisplayEvent::Deactivate(ref display) |
// ...

Though I'm not sure that'd be a strict improvement.

components/script/dom/vr.rs
+ &webvr::VRDisplayEvent::PresentChange(ref display, _) |
+ &webvr::VRDisplayEvent::Change(ref display) => {
+ self.sync_display(&display);
+ if let Some(display) = self.find_display(display.display_id) {
@emilio
emilio Dec 19, 2016 Member

Is it expected to find no display here? If it isn't, please expect, or at least spit an error.

components/script/dom/vrdisplay.rs
+ };
+ }
+
+ false
@emilio
emilio Dec 19, 2016 Member

Can this happen? How can you get a VRDisplay without WebVR enabled?

components/script/dom/vrdisplay.rs
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-resetpose
+ fn ResetPose(&self) -> () {
+ if let Some(wevbr_sender) = self.webvr_thread() {
@emilio
emilio Dec 19, 2016 Member

Same for most if not all of these methods. Seems like webvr_thread should be infallible, and then you should be able to dedent all this code.

@MortimerGoro
MortimerGoro Dec 19, 2016 Contributor

It shouldn't happen unless we want to allow something like stopping/disabling WebVR when Servo is already running (without requiring to restart it).

What's the guideline for this kind of cases on Servo, a panic or a console error?

@emilio
emilio Dec 19, 2016 edited Member

I think a panic is acceptable. Something like:

    fn webvr_thread(&self) -> IpcSender<WebVRMsg> {
        self.global().as_window().webvr_thread().expect("Shouldn't arrive here with WebVR disabled")
    }
@asajeffrey
asajeffrey Dec 19, 2016 Member

Either a panic! or a warn! depending on whether you want to submit a crash report or not. warn! gets included in any later crash report, info! and below does not.

+ // such as compositing WebGL and DOM elements together.
+ // That functionality is not allowed by this revision of the spec.
+ if layers.len() != 1 {
+ let msg = "The number of layers must be 1".to_string();
@emilio
emilio Dec 19, 2016 Member

You shouldn't need to convert to string, here and everywhere else in the file, str implements ToJSValConvertible.

@MortimerGoro
MortimerGoro Dec 19, 2016 Contributor

I had to use string because of this:

283 |             promise.reject_native(promise.global().get_cx(), msg);
    |                     ^^^^^^^^^^^^^ the trait `core::marker::Sized` is not implemented for `str`
+ }
+
+ // Parse and validate received VRLayer
+ let layer = validate_layer(self.global().get_cx(), &layers[0]);
@emilio
emilio Dec 19, 2016 Member

Use match instead of if let Err and then unwrap.

@MortimerGoro
MortimerGoro Dec 20, 2016 edited Contributor

Why is the match better in this case? Or do you mean that with a match I can both exit the function (returning the promise) and get the Ok(result) to continue processing the function?

@emilio
emilio Dec 20, 2016 Member

That's exactly what I meant, you don't need to unwrap if you use the
Ok(..) branch of the match.

components/script/dom/vrdisplay.rs
+ } else {
+ self.stage_params.set(None);
+ }
+
@emilio
emilio Dec 19, 2016 Member

nit: Extra newline.

components/script/dom/vrdisplay.rs
+ let near_init = self.depth_near.get();
+ let far_init = self.depth_far.get();
+
+ thread::Builder::new().name("WebVR_RAF".into()).spawn(move || {
@emilio
emilio Dec 19, 2016 Member

Hmm... We create this once per VRDisplay? Can we do better?

@MortimerGoro
MortimerGoro Dec 19, 2016 Contributor

It's must be one per display, because it waits VSync for the specific display. I'll improve the documentation ;)

components/script/dom/vrdisplay.rs
+ match self.frame_data_status.get() {
+ VRFrameDataStatus::Synced => {
+ end_sender.send(Ok((self.depth_near.get(), self.depth_far.get()))).unwrap();
+ },
@emilio
emilio Dec 19, 2016 Member

Can you use explicit matching here? It's easier to see how this finishes the RAF thread.

@emilio
emilio Dec 19, 2016 Member

In general a few notes about what's the lifetime of the display's request animation frame thread would be nice. I guess one thread per display would be acceptable, but a few more docs on how is that managed would help reviewers (blink), to not have to figure it on their own.

components/script/dom/vrdisplay.rs
+ if values.len() == 0 {
+ return Ok(())
+ }
+ if values.len() > 4 {
@emilio
emilio Dec 19, 2016 Member

Seems like this doesn't enforce that. Should this be changed to if values.len() != 4? Otherwise if I give you three values that's a panic or an out of bounds read.

+
+ // https://w3c.github.io/webvr/#dom-vrdisplaycapabilities-maxlayers
+ fn MaxLayers(&self) -> u32 {
+ if self.capabilities.borrow().0.can_present {
@emilio
emilio Dec 19, 2016 Member

nit: This may be more legible like:

if self.CanPresent() { 1 } else { 0 }
components/script/dom/vrframedata.rs
+ framedata.right_view.set(slice_to_array_buffer_view(global.get_cx(), &matrix));
+
+ reflect_dom_object(box framedata,
+ global,
@emilio
emilio Dec 19, 2016 Member

nit: indentation

components/script/dom/vrpose.rs
+}
+
+#[allow(unsafe_code)]
+fn update_typed_array(cx: *mut JSContext,
@emilio
emilio Dec 19, 2016 Member

probably should be called update_or_create_typed_array.

@emilio
emilio Dec 19, 2016 Member

And should be unsafe.

components/script/dom/vrpose.rs
+
+ pub fn new(global: &GlobalScope, pose: &webvr::VRPose) -> Root<VRPose> {
+ let root = reflect_dom_object(box VRPose::new_inherited(),
+ global,
@emilio
emilio Dec 19, 2016 Member

nit: Indentation

+
+// https://w3c.github.io/webvr/#interface-vrlayer
+
+//typedef (HTMLCanvasElement or OffscreenCanvas) VRSource;
@emilio
emilio Dec 19, 2016 Member

Thanks for filling that :)

It's probably fine to add typedef HTMLCanvasElement VRSource for now, but I don't think it's worth it.

components/vr/Cargo.toml
+default_features = false
+features = ["serde_derive"]
+
+
@emilio
emilio Dec 19, 2016 Member

Yeah, please delete these trailing newlines.

components/vr/webvr_thread.rs
+use vr_traits::webvr::*;
+use webrender_traits;
+
+// Defines the polling interval time in ms for VR Events such as VRDevice connected, disconnected, etc.
@emilio
emilio Dec 19, 2016 Member

Also, I think you can use doc comments for these.

components/vr/webvr_thread.rs
+// * WebVRCompositorHandler is stopped automatically when a JS tab is closed or the whole browser is closed.
+// * WebVRThread and it's VRDevices are destroyed after all tabs are dropped and the browser is about to exit.
+// WebVRThread is closed using the Exit message.
+
@emilio
emilio Dec 19, 2016 Member

Doc comment above, then remove this newline :)

@MortimerGoro
Contributor
MortimerGoro commented Dec 20, 2016 edited

@nox @jdm @emilio @larsbergstrom @cvan @Ms2ger Thanks for the feedback. I have updated the PR:

  • Everything is behind the dom.webvr.enabled preference now
  • Used expect instead of if let Some() in places where the webvr_thread is guaranteed to be enabled
  • Renamed VRDevice to VRDisplay
  • Fixed nits
  • There are two trivial TODOs (getLayers and activeDisplays functions) because of a issue with the python WebIDL code autogeneration tool: it crashes when using readonly attribute sequence<T> pattern. I'll fill a issue about this.
components/canvas/canvas_paint_thread.rs
@@ -208,7 +208,8 @@ impl<'a> CanvasPaintThread<'a> {
}
}
}
- CanvasMsg::WebGL(_) => panic!("Wrong message sent to Canvas2D thread"),
+ CanvasMsg::WebGL(_) => panic!("Wrong WebGLmessage sent to Canvas2D thread"),
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

nit: space between WebGL and message

components/script/dom/vrpose.rs
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrpose-linearacceleration
+ unsafe fn GetLinearAcceleration(&self, _cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> {
+ heap_to_option(&self.linear_vel)
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

Should this be &self.linear_acc?

components/script/script_thread.rs
@@ -473,6 +475,8 @@ pub struct ScriptThread {
content_process_shutdown_chan: IpcSender<()>,
promise_job_queue: PromiseJobQueue,
+ /// A handle to the webvr thread, if available
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

nit: blank line before this comment (they are roughly separated by "area")

components/script_traits/lib.rs
@@ -284,7 +289,8 @@ impl fmt::Debug for ConstellationControlMsg {
DispatchStorageEvent(..) => "DispatchStorageEvent",
FramedContentChanged(..) => "FramedContentChanged",
ReportCSSError(..) => "ReportCSSError",
- Reload(..) => "Reload"
+ Reload(..) => "Reload",
+ WebVREvent(..) => "WebVREvent"
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

nit: we usually keep the trailing , in the final line of a match. It helps both with consistency and if you add more entries to the end, as the diff is one line shorter :-)

resources/prefs.json
@@ -7,6 +7,7 @@
"dom.serviceworker.timeout_seconds": 60,
"dom.testable_crash.enabled": false,
"dom.testbinding.enabled": false,
+ "dom.webvr.enabled": true,
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

This should be false by default

components/webvr/webvr_thread.rs
+use webvr_traits::webvr::*;
+
+/// Defines the polling interval time in ms for VR Events such as VRDisplay connected, disconnected, etc.
+const EVENT_POLLING_INTERVAL: u64 = 500;
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

How was this chosen? I'm not against a hardcoded polling interval, but is there a reason to believe that 500 is the right number?

@MortimerGoro
MortimerGoro Dec 22, 2016 Contributor

Good question. There isn't a formula to decide the perfect value, but 500 seemed like a good balance between resource usage and desired precision. I saw similar values in native games and other implementations. In general these events (ie discovering new displays) don't need very high precision. We can reduce the value if needed because the pooling thread is only active when there is an active tab using WebVR APIs.

@cvan
cvan Dec 23, 2016

can this constant be a pref?

components/webvr/webvr_thread.rs
+
+/// WebVRThread owns native VRDisplays, handles their life cycle inside Servo and
+/// acts a doorman for untrusted VR requests from DOM Objects.
+/// It waits for VR Commands from DOM objects and handles them in its trusted thread.
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

Could you add some more info on the design here? There are some long-running and short-lived threads plus communication back with the constellation and the lifetimes and expectations for some of the parts was not totally clear without a pretty deep reading. e.g., which threads have a lifetime tied to a pipeline vs. a single GetDisplays request vs. etc.

components/webvr/webvr_thread.rs
+
+ fn poll_events(&mut self, sender: IpcSender<bool>) {
+ let events = self.service.poll_events();
+ if !events.is_empty() {
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

Can more events come in while you are handling the current set? Elsewhere in Servo event polling threads, we use a pattern that is more like while (events = poll_events() && !events.is_empty()) { ... } to avoid falling into the polling delay interval during periods of rapid interactions / event floods.

components/webvr/webvr_thread.rs
+ if !events.is_empty() {
+ let pipeline_ids: Vec<PipelineId> = self.contexts.iter().map(|c| *c).collect();
+ for event in events {
+ let event = WebVREventMsg::DisplayEvent(event);
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

There's a bit of duplicated code here with notify_events except for the lifting of the pipeline_ids. Can this be shared?

+/// by flooding the WebVRThread with messages while the main JavaScript tab is presenting to the headset.
+/// Multithreading won't be a problem because:
+/// * Thanks to the security rules implemented in the WebVRThread, when a VRDisplay is in a presenting loop
+/// no other JSContext is granted access to the VRDisplay. So really there aren’t multithreading race conditions.
@larsbergstrom
larsbergstrom Dec 21, 2016 Contributor

I assume that this is the access_check method above? If so, please make a comment about its importance near that method so people don't accidentally remove / "optimize" it away :-)

@MortimerGoro
MortimerGoro Dec 22, 2016 Contributor

Yes, I added more comments ;)

components/script/dom/vr.rs
+ }
+
+ fn register(&self) {
+ if let Some(wevbr_sender) = self.webvr_thread() {
@emilio
emilio Dec 21, 2016 Member

Spelling is wrong.

components/script/dom/vr.rs
+ }
+
+ fn unregister(&self) {
+ if let Some(wevbr_sender) = self.webvr_thread() {
@emilio
emilio Dec 21, 2016 Member

and here.

@larsbergstrom
Contributor

@MortimerGoro Nice work - things are getting close! I think that @emilio and I have a few more issues to address but that it's almost there. There will certainly be some follow-up issues for the bigger items especially that @cvan mentioned, but they shouldn't block getting this landed pretty soon.

@MortimerGoro
Contributor

@larsbergstrom @emilio PR updated!

@bors-servo
Contributor

☔️ The latest upstream changes (presumably #14684) made this pull request unmergeable. Please resolve the merge conflicts.

@MortimerGoro
Contributor
MortimerGoro commented Dec 26, 2016 edited

I moved the event polling interval value to a preference. Thanks @cvan for your suggestion.

Travis-ci failed with a not related problem: No space left on device

@bors-servo
Contributor

☔️ The latest upstream changes (presumably #14831) made this pull request unmergeable. Please resolve the merge conflicts.

@emilio
Member
emilio commented Jan 5, 2017

What's left to do here @larsbergstrom @MortimerGoro?

@larsbergstrom
Contributor

Looks good to me! Just rebase and r=me.

@MortimerGoro
Contributor

@larsbergstrom rebase done ;)

@jdm
Member
jdm commented Jan 6, 2017

@bors-servo: r=larsbergstrom

@bors-servo
Contributor

📌 Commit 47ccd82 has been approved by larsbergstrom

@bors-servo
Contributor

⌛️ Testing commit 47ccd82 with merge d6d6655...

@bors-servo bors-servo added a commit that referenced this pull request Jan 7, 2017
@bors-servo bors-servo Auto merge of #14618 - MortimerGoro:webvr_api, r=larsbergstrom
WebVR API Implementation

<!-- Please describe your changes on the following line: -->

WebVR API Implementation with HTC Vive support on Windows. The current implementations only enables the WebVR support on Windows. In other platforms the API is available on JavaScript but navigator.vr.getDisplays() returns an empty array. This will change when we add support for more VR providers and platforms ;)

Info about the architecture:
https://blog.mozvr.com/webvr-servo-architecture-and-latency-optimizations/
---
<!-- 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
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because _____

Proprietary openvr.dll must be copied next to servo.exe in order to test on HTC Vive (https://github.com/ValveSoftware/openvr/tree/master/bin/win64) I have added some of the official WebVR samples for testing. Switch on your headset and run:

mach run tests/html/webvr/room-scale.html

<!-- 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/14618)
<!-- Reviewable:end -->
d6d6655
@bors-servo
Contributor

💔 Test failed - mac-rel-wpt1

@bors-servo
Contributor

☔️ The latest upstream changes (presumably #14874) made this pull request unmergeable. Please resolve the merge conflicts.

@jdm
Member
jdm commented Jan 7, 2017

Sorry, that was #14323. We can keep working on merging it after a rebase.

@MortimerGoro MortimerGoro WebVR API Implementation, r=larsbergstrom
c5705bf
@MortimerGoro
Contributor

@jdm Ok, no problem. Rebase done and fixed proc_macro attribute no longer needed warnings with the latest rust compiler.

@emilio
Member
emilio commented Jan 9, 2017

@bors-servo r=larsbergstrom,emilio,jdm,nox,asajeffrey,cvan

@bors-servo
Contributor

📌 Commit c5705bf has been approved by larsbergstrom,emilio,jdm,nox,asajeffrey,cvan

@bors-servo
Contributor

⌛️ Testing commit c5705bf with merge 47b776f...

@bors-servo bors-servo added a commit that referenced this pull request Jan 9, 2017
@bors-servo bors-servo Auto merge of #14618 - MortimerGoro:webvr_api, r=larsbergstrom,emilio…
…,jdm,nox,asajeffrey,cvan

WebVR API Implementation

<!-- Please describe your changes on the following line: -->

WebVR API Implementation with HTC Vive support on Windows. The current implementations only enables the WebVR support on Windows. In other platforms the API is available on JavaScript but navigator.vr.getDisplays() returns an empty array. This will change when we add support for more VR providers and platforms ;)

Info about the architecture:
https://blog.mozvr.com/webvr-servo-architecture-and-latency-optimizations/
---
<!-- 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
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because _____

Proprietary openvr.dll must be copied next to servo.exe in order to test on HTC Vive (https://github.com/ValveSoftware/openvr/tree/master/bin/win64) I have added some of the official WebVR samples for testing. Switch on your headset and run:

mach run tests/html/webvr/room-scale.html

<!-- 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/14618)
<!-- Reviewable:end -->
47b776f
@bors-servo
Contributor

💔 Test failed - linux-dev

@jdm
Member
jdm commented Jan 9, 2017

@bors-servo: retry

  • linker failed; might be disk space.
@bors-servo
Contributor

⌛️ Testing commit c5705bf with merge 518ef39...

@bors-servo bors-servo added a commit that referenced this pull request Jan 9, 2017
@bors-servo bors-servo Auto merge of #14618 - MortimerGoro:webvr_api, r=larsbergstrom,emilio…
…,jdm,nox,asajeffrey,cvan

WebVR API Implementation

<!-- Please describe your changes on the following line: -->

WebVR API Implementation with HTC Vive support on Windows. The current implementations only enables the WebVR support on Windows. In other platforms the API is available on JavaScript but navigator.vr.getDisplays() returns an empty array. This will change when we add support for more VR providers and platforms ;)

Info about the architecture:
https://blog.mozvr.com/webvr-servo-architecture-and-latency-optimizations/
---
<!-- 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
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because _____

Proprietary openvr.dll must be copied next to servo.exe in order to test on HTC Vive (https://github.com/ValveSoftware/openvr/tree/master/bin/win64) I have added some of the official WebVR samples for testing. Switch on your headset and run:

mach run tests/html/webvr/room-scale.html

<!-- 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/14618)
<!-- Reviewable:end -->
518ef39
@bors-servo bors-servo merged commit c5705bf into servo:master Jan 9, 2017

2 of 3 checks passed

continuous-integration/travis-ci/pr The Travis CI build failed
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
homu Test successful
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment