Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation in 2.0 #194

Closed
toji opened this issue Feb 27, 2017 · 9 comments
Closed

Navigation in 2.0 #194

toji opened this issue Feb 27, 2017 · 9 comments
Labels
feature request Request for a new feature
Milestone

Comments

@toji
Copy link
Member

toji commented Feb 27, 2017

Closely related to #193 (and #30), which should inform this discussion, but I wanted to talk specifically about API shape in 2.0.

In general I think the timing outline Kip provided (firing after the load event) is reasonable, and the restrictions on when it fires discussed in that thread should also carry over. But in the context of 2.0 it would no longer be tied to the activate event, and instead be promoted to it's own navigate event that fires off the navigator.vrobject. My suggestion is that we fire a VREvent (or VRSessionEvent) that includes the VRDisplay that was presenting on the previous page as well as a VRSession to present with. This actually negates the need to make the navigate event a user gesture, since we'd be giving the page a everything it needs to present with.

I'd also suggest that we perhaps make it a requirement that pages call event.preventDefault() if they're actually going to present in response to the event. This is mostly so that we have a clear point where we can say "Nope, they're not gonna do anything with it, so shut it down." Otherwise we're relying on the garbage collector to clean up the display/session before we know that we can revert back to a 2D version of the page. That's particularly important for responsiveness in VR browsers that mode switch between VR content and traditional 2D content.

@toji toji added the feature request Request for a new feature label Feb 27, 2017
@toji toji added this to the 2.0 milestone Feb 27, 2017
@AlbertoElias
Copy link

The only thing I'm not sure about is requiring event.preventDefault(), are there APIs who depend on it in a similar way? It seems to me like a misuse of it. Can't the browser know if presentation has occured due to a handler on the navigate event, or if the call stack for that handler has emptied (not sure how to express it) without presenting?

@cvan
Copy link
Contributor

cvan commented Mar 3, 2017

The Web App Manifest API does:

https://developers.google.com/web/fundamentals/engage-and-retain/app-install-banners/#deferring_or_cancelling_the_prompt

@toji
Copy link
Member Author

toji commented Mar 3, 2017

WebGL's context loss handling also uses preventDefault in a similar fashion.

The catch with trying to detect "presentation" in a navigate handler is that in 2.0 the proposed model is that requestion an exclusive access session from a display is the mechanism for starting presentation. As long as the session is active the page is considered to be presenting. In this proposal, we'd be giving the page the session to present with automatically (as if it was carried over from the previous page), which implies that presentation is already started.

So that means that the action that needs to be taken in the navigate handler is to decide whether to continue presenting or to end it. I see a couple of ways of handling this:

  1. If the page handles the event but doesn't want to present (maybe they were just doing feature detection, metrics gathering, or determined that they need to show non-VR content first) it must explicitly call session.endSession().
  2. Make the page explicitly confirm that it intends to continue using the session provided (the suggestion here being that preventDefault() acts as the confirmation method)
  3. Only provide the display in the navigate event and make the page request a new session from it if it intends to keep presenting. Here we'd probably need a way to communicate the properties of the session being previously used so that an identical one could be created. That won't really be an issue now since there's only one bit to flip, but it could be more problematic down the road.

I don't like option 1 because it makes it easy to erroneously get into a bad state (page is "presenting" but not actually producing frames) unless the developer follows best practices. Options 2 and 3 require clear intent on the part of the page, which is a nice property to have.

Option 2 gives the impression that we're keeping a single session alive across multiple navigates (regardless of what's actually happening behind the scenes), which is a nice mental model at the very least and may allow for some cleaner implementations to boot, so I tend to prefer that.

@AlbertoElias
Copy link

Why would option 3 be more problematic down the road?

My concern with preventDefault is that it feels weird to me for it to be a part of the WebVR API, when it's a more general Web API. Seeing that other APIs are starting to give it a similar use, if this seems like a way the Web is heading, it definitely does seem comfortable to use and I can definitely see the logic behind it.

@toji
Copy link
Member Author

toji commented Mar 14, 2017

Follow up to this that I've been meaning to post for a little while but still had to work out some of the quirks in my head: A colleague of mine, Michael Thiessen, pointed out that at least on Daydream there's some potential issues with the event-based approach for both navigate and activate. The basic problem is this:

  • Page registers for activate event.
  • When the phone is placed in a Daydream View, we recognize the presence of an activate listener and prevent the home screen from launching, handing control over to the browser instead.
  • Once we're in the right state to do so we fire the activate event.
  • Page does not call requestSession in response.
  • Browser has to detect that no session was requested by the event handler and manually launches home screen (or 2D page view in VR) instead. Loading has now taken several times longer than necessary.

Even if the page DOES call requestPresent in response to the event it means we're now kicking off and waiting for yet another asynchronous call fairly late in the transition, which again slows things down and makes it appear a bit janky to the users. navigate suffers from the same problems, just between pages instead of transitioning in and out of the headset, which makes delays and jank even more noticeable. To be honest, Daydream is probably a worst-case scenarion here, but I do think this is something that would manifest on most hardware to some degree.

So, here's our proposal to make this more predictable and less janky: Rather than (or along with) having pages register for events have them instead describe the scenarios under which they want a new session to be created, and when those situations occur provide a session to the page directly. There's a few models for how this could happen, so I'm just going to pick a couple and run with them. Feel free to suggest alternate interfaces.

Event listener style

navigator.vr.addSessionListener("navigate", session => { ... });
navigator.vr.addSessionListener("activate", session => { ... });

Provides a familiar model, though it's maybe a bit verbose. Raises questions about whether or not the same interface should exist on the VR singleton (for navigate) and the Device (for activate) or only the singleton. (It could work, but then you're forced to listen for activates from all devices.) Alternately you could have explicit functions for each scenario:

navigator.vr.requestSessionOnNavigate(session => { ... });
vrDisplay.requestSessionOnActivate(session => { ... });

Keep in mind that deactivate is probably still a normal event. Also, is it necessary to specify session creation args in there somehow? Seems like a good idea.

RequestSession args

vrDisplay.requestSession({ waitFor: ["navigate", "activate"] }).then(session => { ... });

This model would nicely consolidate everything under requestSession, but has a few downsides: For one, you'd need to enumerate all devices and add the pending request to each of them, which may not be practical from a timing perspective for navigate. It's use of promises also makes it a one-shot deal, and if you want to respond to future events you'd have to re-register another listener every time one fires. That feels very awkward to me.

Or, you know, maybe this isn't something worth worrying about at all because nobody else is running into issues with the event-based model? Let me know what you think!

@toji
Copy link
Member Author

toji commented May 31, 2017

Update: We had a vote on features that should be in scope for WebVR 2.0 today and WebVR-to-WebVR page navigation was voted in. Let's continue iterating on the feature design to work out the issues that have been brought up thus far.

@toji
Copy link
Member Author

toji commented May 25, 2018

Consolidating issues: There is related conversation that happened in #30, which is closed now simply to allow this issue to be the top-level bug.

@toji
Copy link
Member Author

toji commented Jul 24, 2018

The latest explainer and proposal covering this topic are at #382 and #383 respectively.

@NellWaliczek
Copy link
Member

Closing in favor of #382 and #383

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Request for a new feature
Projects
None yet
Development

No branches or pull requests

5 participants