From 765ed49c5c4c40fd86c360867c9c11560964f201 Mon Sep 17 00:00:00 2001 From: Brandon Jones Date: Wed, 31 May 2017 14:35:46 -0700 Subject: [PATCH 1/3] Allow multiple active sessions at once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change removes any concepts that assume a device may only have a single active session at a time. This allows multiple non-exclusive VRSessions to be active at once, which will be important for enabling various interesting magic window scenarios in the future. With this change starting an exclusive session suspends the non-exclusive ones, but doesn’t end them. IDL Changes: - Remove VRDevice.activeSession - VRDevice.onsessionchange -> VRSession.onended --- explainer.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/explainer.md b/explainer.md index 03db16bc..f974675c 100644 --- a/explainer.md +++ b/explainer.md @@ -103,7 +103,7 @@ Clicking that button will attempt to initiate a [`VRSession`](https://w3c.github The page may also want to create a session that doesn't need exclusive access to the device for tracking purposes. Since the `VRSession` is also what provides access to the device's position and orientation data requesting a non-exclusive session enables what's referred to as "Magic Window" use cases, where the scene is rendered on the page normally but is responsive to device movement. This is especially useful for mobile devices, where moving the device can be used to look around a scene. Devices with Tango tracking capabilities may also expose 6DoF tracking this way, even when the device itself is not capable of stereo presentation. -Requesting a new type of session will end any previously active ones. +Starting an exclusive session will suspend all non-exclusive sessions for the device (firing the session `blur` event) until the exclusive session is ended. ```js function BeginVRSession(isExclusive) { @@ -255,29 +255,26 @@ To stop presenting to the `VRDevice`, the page calls [`VRSession.endSession`](ht ```js function EndVRSession() { // Do we have an active session? - if (vrDevice.activeSession) { + if (vrSession) { // End VR mode now. - vrDevice.activeSession.endSession().then(OnSessionEnded); + vrSession.endSession().then(OnSessionEnded); } } // Restore the page to normal after exclusive access has been released. function OnSessionEnded() { + vrSession = null; + // Ending the session stops executing callbacks passed to requestFrame(). // To continue rendering, use the window's AnimationFrame callback window.requestAnimationFrame(onDrawFrame); } ``` -In addition to the application ending the session manually, the UA may force the session to end at any time for a variety of reasons. Well behaved applications should monitor the `sessionchange` event on the `VRDevice` to detect when that happens. +In addition to the application ending a session manually, the UA may force sessions to end at any time for a variety of reasons. Well behaved applications should monitor the `ended` event on the `VRSession` to detect when that happens. ```js -vrDevice.addEventListener('sessionchange', vrDeviceEvent => { - // Check to see if the vrDevice no longer has an active session. - if (!vrDevice.activeSession) { - OnSessionEnded(); - } -}); +vrSession.addEventListener('ended', OnSessionEnded); ``` @@ -445,8 +442,8 @@ Similarly, the `deactivate` event can be used to detect when the user removes th ```js vrDevice.addEventListener('deactivate', vrDeviceEvent => { - if (vrDevice.activeSession) { - vrDevice.activeSession.endSession().then(OnSessionEnded); + if (vrSession) { + vrSession.endSession().then(OnSessionEnded); } }); ``` @@ -562,9 +559,6 @@ interface VRDevice : EventTarget { readonly attribute DOMString deviceName; readonly attribute boolean isExternal; - attribute VRSession? activeSession; - - attribute EventHandler onsessionchange; attribute EventHandler onactivate; attribute EventHandler ondeactivate; @@ -596,6 +590,7 @@ interface VRSession : EventTarget { attribute EventHandler onblur; attribute EventHandler onfocus; attribute EventHandler onresetpose; + attribute EventHandler onended; Promise createFrameOfReference(VRFrameOfReferenceType type); From f113613a6099fdf62372fbf9a75dfd3054b81bc0 Mon Sep 17 00:00:00 2001 From: Brandon Jones Date: Wed, 31 May 2017 15:45:05 -0700 Subject: [PATCH 2/3] Clarify the difference between Exclusive and Non-Exclusive modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wanted to explicitly state the differences between the two modes and why you’d want to use one or the other. --- explainer.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/explainer.md b/explainer.md index f974675c..c46fb94c 100644 --- a/explainer.md +++ b/explainer.md @@ -76,9 +76,15 @@ navigator.vr.getDevices().then(devices => { ``` ### Detecting and advertising VR mode -If a VRDevice is available and has the appropriate capabilities the page will usually want to add some UI to trigger activation of "VR Presentation Mode", where the page can begin sending imagery to the device. Testing to see if the device supports the capabilities the page needs is done via the `supportsSession` call, which takes a dictionary of the desired functionality and returns whether or not the device can create a session supporting them. Querying for support this way is necessary because it allows the application to detect what VR features are available without actually engaging the sensors or beginning presentation, which can incur significant power or performance overhead on some systems and may have side effects such as launching a VR status tray or storefront. +If a `VRDevice` is available and has the appropriate capabilities the page will usually want to add some UI to trigger activation of "VR Presentation Mode", where the page can begin sending imagery to the device. Testing to see if the device supports the capabilities the page needs is done via the `supportsSession` call, which takes a dictionary of the desired functionality and returns whether or not the device can create a session supporting them. Querying for support this way is necessary because it allows the application to detect what VR features are available without actually engaging the sensors or beginning presentation, which can incur significant power or performance overhead on some systems and may have side effects such as launching a VR status tray or storefront. -In this case, we only ask if the ability to have `exclusive` access (Which includes the ability to display imagery on the headset). Note that `exclusive: true` is actually the dictionary default, and so does not need to be specified here. It’s made explicit in this example for clarity. +Sessions can be created with two levels of access: + +**Exclusive Access**: The default mode, but can be explicitly requested with the `exclusive: true` dictionary argument. Exclusive sessions present content directly to the `VRDevice`, enabling immersive VR presentation. Only one exclusive session is allowed at a time across the entire UA. Exclusive sessions must be created within a user gesture event. + +**Non-Exclusive Access**: Requested with the `exclusive: false` dictionary argument. Non-exclusive sessions do not have the ability to display anything on the `VRDevice`, but are able to access device tracking information and use it to render content on the page. This technique, where a scene rendered to the page is responsive to device movement is sometimes referred to as "Magic Window" mode. It's especially useful for mobile devices, where moving the device can be used to look around a scene. Devices like Tango phones and tablets with 6DoF tracking capabilities may expose them via non-exclusive sessions even if the hardware is not capable of immersive, stereo presentation. Multiple non-exclusive sessions can be active at once, but all non-exclusive sessions are suspended when an exclusive session is active. Non-exclusive sessions are not required to be created within a user gesture event. + +In this case, we ask if the `VRDevice` supports sessions with `exclusive` access, since we want the ability to display imagery on the headset. ```js async function OnVRAvailable() { @@ -91,7 +97,7 @@ async function OnVRAvailable() { if (exclusiveMode) { var enterVrBtn = document.createElement("button"); enterVrBtn.innerHTML = "Enter VR"; - enterVrBtn.addEventListener("click", BeginVRSession()); + enterVrBtn.addEventListener("click", BeginVRSession); document.body.appendChild(enterVrBtn); } } @@ -101,10 +107,6 @@ async function OnVRAvailable() { Clicking that button will attempt to initiate a [`VRSession`](https://w3c.github.io/webvr/#interface-vrsession), which manages input and output for the display. When creating a session with `VRDevice.requestSession` the capabilities that the returned session must have are passed in via a dictionary, exactly like the `supportsSession` call. If `supportsSession` returned true for a given dictionary then calling `requestSession` with the same dictionary values should be reasonably expected to succeed, barring external factors (such as `requestSession` not being called in a user gesture for an exclusive session or another page currently having an exclusive session for the same device.) -The page may also want to create a session that doesn't need exclusive access to the device for tracking purposes. Since the `VRSession` is also what provides access to the device's position and orientation data requesting a non-exclusive session enables what's referred to as "Magic Window" use cases, where the scene is rendered on the page normally but is responsive to device movement. This is especially useful for mobile devices, where moving the device can be used to look around a scene. Devices with Tango tracking capabilities may also expose 6DoF tracking this way, even when the device itself is not capable of stereo presentation. - -Starting an exclusive session will suspend all non-exclusive sessions for the device (firing the session `blur` event) until the exclusive session is ended. - ```js function BeginVRSession(isExclusive) { // VRDevice.requestSession must be called within a user gesture event @@ -450,9 +452,11 @@ vrDevice.addEventListener('deactivate', vrDeviceEvent => { ### Responding to a suspended session -The UA may temporarily suspend a session if allowing the page to continue reading the headset position represents a security risk (like when the user is entering a password or URL with a virtual keyboard, in which case the head motion may infer the user's input), or if other content is obscuring the page's output. While suspended the page may either refresh the vr device at a slower rate or not at all, and poses queried from the headset may be less accurate. The UA is expected to present a tracked environment to the user when the page is being throttled to prevent user discomfort. +The UA may temporarily suspend a session if allowing the page to continue reading the headset position represents a security risk (like when the user is entering a password or URL with a virtual keyboard, in which case the head motion may infer the user's input), or if other content is obscuring the page's output. Additionally, non-exclusive sessions are suspended while an exclusive session is active. + +While suspended the page may either refresh the vr device at a slower rate or not at all, and poses queried from the device may be less accurate. If the user is wearing a headset the UA is expected to present a tracked environment when the page is being throttled to prevent user discomfort. -In general the page should continue drawing frames in response to `VRSession.requestFrame()` callbacks. The UA may use these frames as part of it's tracked environment, though they may be partially occluded, blurred, or otherwise manipulated. Still, some applications may wish to respond to this suspension by halting game logic, purposefully obscuring content, or pausing media. To do so, the application should listen for the `blur` and `focus` events from the `VRSession`. For example, a 360 media player would do this to pause the video/audio whenever the UA has obscured it. +In general the page should continue drawing frames in response to `VRSession.requestFrame()` callbacks. The UA may use these frames as part of it's tracked environment or page composition, though they may be partially occluded, blurred, or otherwise manipulated. Still, some applications may wish to respond to this suspension by halting game logic, purposefully obscuring content, or pausing media. To do so, the application should listen for the `blur` and `focus` events from the `VRSession`. For example, a 360 media player would do this to pause the video/audio whenever the UA has obscured it. ```js vrSession.addEventListener('blur', vrSessionEvent => { From fa8dc4581ed043739084cc3c0524f3f549c2f05d Mon Sep 17 00:00:00 2001 From: Brandon Jones Date: Thu, 22 Jun 2017 16:46:26 -0700 Subject: [PATCH 3/3] First stab at updating the explainer's navigation section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompted by @kearwood’s comments on #236 --- explainer.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/explainer.md b/explainer.md index c46fb94c..641f3cb9 100644 --- a/explainer.md +++ b/explainer.md @@ -486,22 +486,18 @@ vrSession.addEventListener('resetpose', vrSessionEvent => { ### Page navigation -WebVR applications can, like any web page, link to other pages. In the context of a VR scene this is handled by setting `window.location` to the desired URL when the user performs some action. If the page being linked to is not VR-capable the user will either have to remove the VR device to view it (which the UA should explicitly instruct them to do) or the page could be shown as a 2D page in a VR browser. +WebVR applications can, like any web page, link to other pages. In the context of an exclusive `VRSession` this is handled by setting `window.location` to the desired URL when the user performs some action. If the page being linked to is not VR-capable the user will either have to remove the VR device to view it (which the UA should explicitly instruct them to do) or the page could be shown as a 2D page in a VR browser. -If the page being navigated to is VR capable, however, it's frequently desirable to allow the user to immediately begin using a VR session for that page as well, so that the user feels as though they are navigating through a single continuous VR experience. To achieve this the page can handle the `navigate` event, fired on the `navigator.vr` object. This event provides a `VRSession` for the `VRDevice` that the previous page was presenting to. +If the page being navigated to is VR capable, however, it's frequently desirable to allow the user to immediately create an exclusive `VRSession` for that page as well, so that the user feels as though they are navigating through a single continuous VR experience. To achieve this the page can query `navigator.vr.getNavigationDevice()` which returns a promise. If the page was navigated to from an exclusive `VRSession` the promise will resolve with the `VRDevice` that the previous session was created with. If not, the promise immediately rejects. -The `VRDevice` and `VRSession` must not retain any state set by the previous page, and need not make any guarantees about consistency of pose data between pages. They should maintain the same general implementation between pages for basic usage consistency. For example: The user agent should not switch users from the Oculus SDK to OpenVR between pages, or from Daydream to Cardboard, even though in both cases the users device could technically be used with either. - -To indicate to indicate that you wish to continue presenting VR content on this page the handler must call `event.preventDefault()`. +If `getNavigationDevice()` resolves to a `VRDevice` the page is allowed to create an exclusive session with the same device in the promise resolution callback without being subject to the user gesture requirement. This is only allowed once, and subsequent exclusive `VRSession`s must be requested within a user gesture. Additionally, the session must be requested within a User Agent-defined period after the page load or the ability to create an exclusive session without a user gesture expires. ```js -navigator.vr.addEventListener('navigate', vrSessionEvent => { - vrSessionEvent.preventDefault(); - vrSession = vrSessionEvent.session; - vrDevice = vrSession.device; - - // Ensure content is loaded and begin drawing. - onDrawFrame(); +// Check to see if the page was navigated to from an exclusive session, and if +// so begin our own to provide a more continuous VR experience to the user. +navigator.vr.getNavigationDevice().then(device => { + vrDevice = device; + BeginVRSession(); }); ``` @@ -547,9 +543,10 @@ partial interface Navigator { interface VR : EventTarget { attribute EventHandler ondeviceconnect; attribute EventHandler ondevicedisconnect; - attribute EventHandler onnavigate; Promise> getDevices(); + + Promise getNavigationDevice(); }; //