Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upWebVR API Implementation #14618
WebVR API Implementation #14618
Conversation
highfive
commented
Dec 16, 2016
|
Heads up! This PR modifies the following files:
|
highfive
commented
Dec 16, 2016
|
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? |
| @@ -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) { | |||
This comment has been minimized.
This comment has been minimized.
| sender | ||
| } | ||
|
|
||
| #[allow(unsafe_code)] |
This comment has been minimized.
This comment has been minimized.
|
|
||
| fn poll_events(&mut self, sender: IpcSender<bool>) { | ||
| let events = self.service.poll_events(); | ||
| if events.len() > 0 { |
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| fn schedule_poll_events(&mut self) { | ||
| if self.service.is_initialized() && !self.polling_events { |
This comment has been minimized.
This comment has been minimized.
emilio
Dec 16, 2016
Member
nit: Probably nicer to read as:
if !self.service.is_initialized() || self.polling_events {
return;
}
// ...| // WebVR Thread asked to unschedule this thread | ||
| break; | ||
| } | ||
| thread::sleep(time::Duration::from_millis(500)); |
This comment has been minimized.
This comment has been minimized.
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.
| // 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); |
This comment has been minimized.
This comment has been minimized.
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.
This comment has been minimized.
This comment has been minimized.
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.
| (*compositor.0).sync_poses(); | ||
| (*compositor.0).synced_frame_data(near, far).to_bytes() | ||
| }; | ||
| let _result = sender.send(Ok(pose)); |
This comment has been minimized.
This comment has been minimized.
emilio
Dec 16, 2016
Member
The patterns let _ = sender.send(..) or drop(sender.send()) seem more used across the codebase.
| impl WebVRCompositorHandler { | ||
| #[allow(unsafe_code)] | ||
| fn create_compositor(&mut self, device_id: webrender_traits::VRCompositorId) { | ||
| if let Some(ref sender) = self.webvr_thread_sender { |
This comment has been minimized.
This comment has been minimized.
emilio
Dec 16, 2016
Member
Probably worth:
let sender = match self.webvr_thread_sender {
Some(ref s) => s,
None => return,
};| } | ||
| } | ||
|
|
||
| pub fn set_webvr_thread_sender(&mut self, sender: IpcSender<WebVRMsg>) { |
This comment has been minimized.
This comment has been minimized.
emilio
Dec 16, 2016
Member
Worth pointing here with a comment that this is done only a per-platform basis on initialization.
| @@ -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>>, | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
MortimerGoro
Dec 16, 2016
•
Author
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.
|
@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. |
|
@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. |
|
Seems like this should all be behind a pref. |
|
@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. |
|
@MortimerGoro It belongs in resources/prefs.json and annotated in the WebIDL files using [Pref=] instead of a command-line option. |
|
@jdm Thanks for the info, never had played with that json before ;) |
|
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; | |||
This comment has been minimized.
This comment has been minimized.
| @@ -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 | |||
This comment has been minimized.
This comment has been minimized.
|
|
||
| impl Navigator { | ||
| pub fn handle_webvr_event(&self, event: WebVREventMsg) { | ||
| if let Some(vr) = self.vr.get() { |
This comment has been minimized.
This comment has been minimized.
| return promise; | ||
| } | ||
|
|
||
| if let Some(wevbr_sender) = self.webvr_thread() { |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
|
|
||
| impl VRDisplayEvent { | ||
| fn new_inherited(display: &VRDisplay, | ||
| reason: &Option<VRDisplayEventReason>) |
This comment has been minimized.
This comment has been minimized.
| display: &VRDisplay, | ||
| event: &webvr::VRDisplayEvent) | ||
| -> Root<VRDisplayEvent> { | ||
| let (name, reason) = match event { |
This comment has been minimized.
This comment has been minimized.
| fov: JS<VRFieldOfView>, | ||
| } | ||
|
|
||
| // Wrappers required to include WebVR structs in a DOM struct |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
MortimerGoro
Dec 19, 2016
Author
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 ;)
| let (name, reason) = match event { | ||
| &webvr::VRDisplayEvent::Connect(_) => ("displayconnect", None), | ||
| &webvr::VRDisplayEvent::Disconnect(_) => ("displaydisconnect", None), | ||
| &webvr::VRDisplayEvent::Activate(_, ref reason) => ("activate", Some(reason)), |
This comment has been minimized.
This comment has been minimized.
|
|
||
| impl VREyeParameters { | ||
| #[allow(unrooted_must_root)] | ||
| fn new_inherited(parameters: &webvr::VREyeParameters, global: &GlobalScope) -> VREyeParameters { |
This comment has been minimized.
This comment has been minimized.
cvan left a comment
|
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"); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
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."); | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
MortimerGoro
Dec 19, 2016
•
Author
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 ;)
| @@ -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", | |||
This comment has been minimized.
This comment has been minimized.
| @@ -495,6 +495,13 @@ macro_rules! window_event_handlers( | |||
| event_handler!(unhandledrejection, GetOnunhandledrejection, | |||
| SetOnunhandledrejection); | |||
| event_handler!(unload, GetOnunload, SetOnunload); | |||
| event_handler!(vrdisplayconnect, GetOnvrdisplayconnect, SetOnvrdisplayconnect); | |||
This comment has been minimized.
This comment has been minimized.
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 immersive-web/webxr#152.)
This comment has been minimized.
This comment has been minimized.
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.
This comment has been minimized.
This comment has been minimized.
MortimerGoro
Dec 19, 2016
Author
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)
| self.displays.borrow() | ||
| .iter() | ||
| .find(|d| d.get_display_id() == display_id) | ||
| .map(|d| Root::from_ref(&**d)) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| serde_derive = "0.8" | ||
| rust-webvr = {version = "0.1", features = ["serde-serialization"] } | ||
|
|
||
|
|
This comment has been minimized.
This comment has been minimized.
| msg = {path = "../msg"} | ||
| serde = "0.8" | ||
| serde_derive = "0.8" | ||
| rust-webvr = {version = "0.1", features = ["serde-serialization"] } |
This comment has been minimized.
This comment has been minimized.
|
|
||
| pub type WebVRResult<T> = Result<T, String>; | ||
|
|
||
| // Messages from Script Thread to WebVR thread. |
This comment has been minimized.
This comment has been minimized.
|
|
||
| if let Some(wevbr_sender) = self.webvr_thread() { | ||
| let (sender, receiver) = ipc::channel().unwrap(); | ||
| wevbr_sender.send(WebVRMsg::GetVRDisplays(sender)).unwrap(); |
This comment has been minimized.
This comment has been minimized.
| RegisterContext(PipelineId), | ||
| UnregisterContext(PipelineId), | ||
| PollEvents(IpcSender<bool>), | ||
| GetVRDisplays(IpcSender<WebVRResult<Vec<VRDisplayData>>>), |
This comment has been minimized.
This comment has been minimized.
|
I took a closer look, here are a few comments :) |
| @@ -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"), | |||
This comment has been minimized.
This comment has been minimized.
| WebGLPaintTaskData::WebRender(ref api, id) => { | ||
| api.send_vr_compositor_command(id, message); | ||
| } | ||
| WebGLPaintTaskData::Readback(_, _, _) => { |
This comment has been minimized.
This comment has been minimized.
| @@ -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) | |||
This comment has been minimized.
This comment has been minimized.
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"); |
This comment has been minimized.
This comment has been minimized.
emilio
Dec 19, 2016
Member
Also, if we go this spell-checking mode, you may want to capitalize WebVR :P
| fn Vr(&self) -> Root<VR> { | ||
| self.vr.or_init(|| VR::new(&self.global())) | ||
| } | ||
|
|
This comment has been minimized.
This comment has been minimized.
|
|
||
| pub fn new(global: &GlobalScope, pose: &webvr::VRPose) -> Root<VRPose> { | ||
| let root = reflect_dom_object(box VRPose::new_inherited(), | ||
| global, |
This comment has been minimized.
This comment has been minimized.
|
|
||
| // https://w3c.github.io/webvr/#interface-vrlayer | ||
|
|
||
| //typedef (HTMLCanvasElement or OffscreenCanvas) VRSource; |
This comment has been minimized.
This comment has been minimized.
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.
| default_features = false | ||
| features = ["serde_derive"] | ||
|
|
||
|
|
This comment has been minimized.
This comment has been minimized.
| use vr_traits::webvr::*; | ||
| use webrender_traits; | ||
|
|
||
| // Defines the polling interval time in ms for VR Events such as VRDevice connected, disconnected, etc. |
This comment has been minimized.
This comment has been minimized.
| // * 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. | ||
|
|
This comment has been minimized.
This comment has been minimized.
|
@nox @jdm @emilio @larsbergstrom @cvan @Ms2ger Thanks for the feedback. I have updated the PR:
|
566ecdc
to
ac3df0f
| @@ -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"), | |||
This comment has been minimized.
This comment has been minimized.
| #[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) |
This comment has been minimized.
This comment has been minimized.
| @@ -473,6 +475,8 @@ pub struct ScriptThread { | |||
| content_process_shutdown_chan: IpcSender<()>, | |||
|
|
|||
| promise_job_queue: PromiseJobQueue, | |||
| /// A handle to the webvr thread, if available | |||
This comment has been minimized.
This comment has been minimized.
larsbergstrom
Dec 21, 2016
Contributor
nit: blank line before this comment (they are roughly separated by "area")
| @@ -284,7 +289,8 @@ impl fmt::Debug for ConstellationControlMsg { | |||
| DispatchStorageEvent(..) => "DispatchStorageEvent", | |||
| FramedContentChanged(..) => "FramedContentChanged", | |||
| ReportCSSError(..) => "ReportCSSError", | |||
| Reload(..) => "Reload" | |||
| Reload(..) => "Reload", | |||
| WebVREvent(..) => "WebVREvent" | |||
This comment has been minimized.
This comment has been minimized.
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 :-)
| @@ -7,6 +7,7 @@ | |||
| "dom.serviceworker.timeout_seconds": 60, | |||
| "dom.testable_crash.enabled": false, | |||
| "dom.testbinding.enabled": false, | |||
| "dom.webvr.enabled": true, | |||
This comment has been minimized.
This comment has been minimized.
| 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); |
This comment has been minimized.
This comment has been minimized.
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?
|
|
||
| fn poll_events(&mut self, sender: IpcSender<bool>) { | ||
| let events = self.service.poll_events(); | ||
| if !events.is_empty() { |
This comment has been minimized.
This comment has been minimized.
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.
| /// 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. |
This comment has been minimized.
This comment has been minimized.
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 :-)
This comment has been minimized.
This comment has been minimized.
|
|
||
| /// 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. |
This comment has been minimized.
This comment has been minimized.
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.
| 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; |
This comment has been minimized.
This comment has been minimized.
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?
This comment has been minimized.
This comment has been minimized.
MortimerGoro
Dec 22, 2016
Author
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.
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| fn register(&self) { | ||
| if let Some(wevbr_sender) = self.webvr_thread() { |
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| fn unregister(&self) { | ||
| if let Some(wevbr_sender) = self.webvr_thread() { |
This comment has been minimized.
This comment has been minimized.
|
@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. |
00b1897
to
85ce70c
|
@larsbergstrom @emilio PR updated! |
|
|
|
|
|
Sorry, that was #14323. We can keep working on merging it after a rebase. |
|
@jdm Ok, no problem. Rebase done and fixed proc_macro attribute no longer needed warnings with the latest rust compiler. |
|
@bors-servo r=larsbergstrom,emilio,jdm,nox,asajeffrey,cvan |
|
|
…,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 -->
|
|
|
@bors-servo: retry
|
…,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 -->
|
|
MortimerGoro commentedDec 16, 2016
•
edited by larsbergstrom
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 -ddoes not report any errors./mach test-tidydoes not report any errorsProprietary 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