-
Notifications
You must be signed in to change notification settings - Fork 48
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
Need to spec liveness of Gamepad objects #8
Comments
@toji what is the status of this issue? It's come up in Intent to Ship: WebXR Gamepad Module on blink-dev. If this isn't interoperable as this issue suggests it isn't, then resolving it in the spec seems fairly high priority. |
FWIW, given the "The same object MUST be returned until the user agent needs to return different values (or values in a different order)" wording for |
To clarify, I'm no longer an editor on this spec. Speaking from the perspective of an editor within the Immersive Web Working Group, however, I understand the concern that our use of gamepad differs from how this spec text implies they should be used. It is worth noting that every Gamepad implementation I'm familiar with does update the gamepad objects in-place upon calling getGamepads(), which seems to contradict the spec text. I'm skeptical that the UA implementations will change to match the spec text any time soon, given that I believe it's already been previously identified by Microsoft as a content compatibility issue. As such, I would rather that WebXR be consistent with the observed behavior of gamepads rather than the single sentence in the Gamepad spec that states they should behave otherwise. That said, I also don't think the spec and implementations should disagree. It would be best if either the spec updated to reflect the currently observed behavior accurately or we get agreement from all UAs that they will update |
There's a couple issues here, right? Does |
There's some relevant discussion in Intent to Ship: WebXR Gamepad Module on blink-dev. It sounds like because the number of buttons don't change, the If |
As long as each get does not return a new array that is fine (same principle as the other getters). My main concern here is not so much compatibility by the way, but violating run-to-completion semantics. |
From what I can tell this can be made sensible in terms of not changing state while script is running and I think any remaining warts after this might be with |
@nondebug (Matt Reynolds) can you describe roughly the changes you'd like to make here? |
Sure, please take a look:
Each non-null item in the array returned by
The The The
The Live objects ( |
That's not great, either store the JavaScript array object on the object and replace it when needed, or always create a new array based on the internal data.
Must, right? (Same for other shoulds.)
This will have to be defined as updating internal state on the |
If both Firefox and Chrome's implementations do not match the spec text then I think the spec text should be changed to match the implementations. Someone should test Safari and Edge as well. I was under the impression that the Chrome implementation was snapshots, but perhaps that changed over time? Regardless, last I knew the Firefox implementation actually implemented updates to Gamepad objects by doing the internal equivalent of queuing microtasks to update the state, so using some spec language like "Updates to Gamepad objects should be queued as microtasks in the current event loop" should both be sensible and not contradict the Firefox implementation. Note that there have been requests to add support for the Gamepad API to Web Workers so ideally the spec text could be written to not make it more difficult to add that in the future. There are some existing manual web platform tests, it should be possible to write new tests for this behavior. |
@nondebug thanks, that look great! On the various "new array" questions, this is my best understanding:
For when updates happen, I think there are three choices:
Between the last two, I think "top of the event loop" is probably fine? But just matching what is or will be implemented is more important than what I think :) |
FWIW in immersive-web/webxr-gamepads-module#18 (PR at immersive-web/webxr-gamepads-module#22 ) WebXR seems to be settling on making them live, but updated every XR frame. We already have a reliable frame tick which we can piggyback on for this (so we don't need to create new tasks). |
If Gamepad is a live object then the array returned by getGamepads() should only need to change when gamepads are added or removed from the list.
Yes, apologies, I'm new to spec language.
Let's go with "updates should be queued as microtasks in the current event loop" to match the Firefox implementation.
@luser, can you elaborate on this? (Is there an issue with tying the update to microtasks or the event queue in a Web Worker?) |
The objects in the arrays may be the same, but each time |
I just wanted to call that out to make sure that we don't wind up with spec text that ties the update rate to |
Originally filed as https://www.w3.org/Bugs/Public/show_bug.cgi?id=21434:
Ted Mielczarek [:ted] 2013-03-29 13:34:49 UTC
The discussion in bug 17309 made it clear that Scott and I have implemented two different things in regards to the "liveness" of Gamepad objects. Scott's Chrome implementation returns a static snapshot of each Gamepad when you getGamepads(), whereas the Gamepad object attached to the "ongamepadconnected" event in my Firefox implementation is live: you can get the latest device state out of it at any time.
We need to decide what the correct behavior here is and spec it.
Obviously I'm biased in favor of my implementation, but I think it does have one large benefit: we're expecting users to poll Gamepad status in a requestAnimationFrame loop, so if you're generating new Gamepad objects for every call to getGamepads() you're going to be generating a lot of garbage.
[reply] [−] Comment 1 Scott Graham 2013-03-29 17:26:04 UTC
(In reply to comment #0)
(I'm honestly not sure precisely what I implemented.)
I agree on the reducing garbage concern, and so returning/modifying the same objects sounds right to me.
The part I don't like as much is that the data unexpectedly changes, and it's unclear when, and with what frequency it's updated. That's why I used getGamepads() as the sync point for "Please update the data in my gamepad objects to the most recent state".
[reply] [−] Comment 2 Ted Mielczarek [:ted] 2013-04-17 13:14:21 UTC
The HTML WebApp spec has concepts we can use to define this in a straightforward way:
http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#concept-task
It basically specs thread event queues, so that you can say that the Gamepad state will be updated after the current script has finished executing.
[reply] [−] Comment 3 Ted Mielczarek [:ted] 2014-06-23 18:41:43 UTC
I chatted with another implementer and they were curious as to whether (assuming that Gamepad objects are snapshots):
navigator.getGamepads()[0] == navigator.getGamepads()[0]
ought to be true. It looks like it currently is in Chrome, but not in the IE developer preview.
We agreed that sensible spec language would allow that to be true, and in fact the spec should allow implementations to return the same snapshot for a Gamepad until the data changes (the gamepad has been interacted with in some way).
[reply] [−] Comment 4 Philip Jägenstedt 2014-12-18 15:25:35 UTC
Live Array-like objects like NodeList are more work to implement and test, we should avoid making more of them.
That navigator.getGamepads()[0] === navigator.getGamepads()[0] should always hold true seems like a given, otherwise you'll necessarily have multiple Gamepad objects representing the same underlying hardware, and the mess of updating all of them. Unless the idea is that a Gamepad object is a snapshot and you have to call getGamepads() every time you want to check for changes? Easy to implement, but a bit odd...
Finally, navigator.getGamepads() === navigator.getGamepads()? Returning a new array every time seems easier bindings-wise at least in Blink. It does require having a separate array internally to keep track of the Gamepad objects so that you don't create new one. Spec'ing and testing how the return value of getGamepads() is reused seems like an unnecessary burden to me.
[reply] [−] Comment 5 Ted Mielczarek [:ted] 2014-12-18 15:50:16 UTC
(In reply to Philip Jägenstedt from comment #4)
Yeah, I don't think anyone is arguing for that, thankfully.
This is the entire crux of this bug: currently the Gamepad objects in Firefox always represent the most-recent-available state of the controller, whereas in Chrome and IE they represent a snapshot of the state as of the time you called navigator.getGamepads().
There was a long thread about this on public-webapps earlier this year:
http://lists.w3.org/Archives/Public/public-webapps/2014AprJun/0238.html
It didn't really reach any strong conclusions, there are good arguments to be made both ways. One compelling argument that was made is that if we do spec some sort of data change events in the future we will want snapshots for that:
http://lists.w3.org/Archives/Public/public-webapps/2014AprJun/0257.html
There's also a pretty good argument (that didn't come up on-list) that having snapshots makes it easier to compare gamepad state in the polling model, since you can hang on to the previous snapshot and compare vs. the next snapshot (using .timestamp, for one, and checking whether button states match etc).
[reply] [−] Comment 6 Philip Jägenstedt 2015-02-09 11:29:14 UTC
I'm trying to implement the Navigator.getGamepads() change in https://codereview.chromium.org/808643005/ but quickly realized that "Retrieve a snapshot of the data for the the currently connected and interacted-with gamepads." isn't a certainly until this spec bug is resolved.
Having getGamepads() return a new array of completly new Gamepad object every time is simple enough to implement, but it seems to make the GamepadEvent nonsensical, as the GamepadEvent.gamepad member will necessarily be a Gamepad object constructed only for that event and that will never appear in any array returned by Navigator.getGamepads().
What does Firefox do?
[reply] [−] Comment 7 Philip Jägenstedt 2015-02-09 13:32:22 UTC
(In reply to Philip Jägenstedt from comment #6)
I don't have a gamepad to test with here, but judging by the code in gecko-dev and minor testing, it looks like Firefox always returns a new array, but the internal Gamepad objects are just allocated once. One point of doubt is |Gamepad::Clone|, is that called as part of getGamepads() so that a new object is seen by script every time?
The text was updated successfully, but these errors were encountered: