In the interests of a speedier resolution, do you have a quick Godot test app that demonstrates the issue?
(If not, I can certainly also write one, but I fear that's gonna end up pushing this further down the task list until there's enough time free to do so.)
On reading this a little more closely, I realized something: you're comparing OpenVR's stationary reference space to the explicitly-meant-to-be-moved OpenXR stage.
By definition, the stage should be centered on wherever the user recenters to. What you are probably actually looking for is not the movable XR_REFERENCE_SPACE_TYPE_STAGE but rather XR_REFERENCE_SPACE_TYPE_STATIONARY_EXT, which should give you the same location you're seeing from OpenVR's GetPlayAreaRect.
Let me know if that fixes it for you?
Thanks for looking into it! I'm definitely not the expert here, but I've read all the OpenXR documents that I've found (and are not always easy to interpret) and I believe LOCAL_FLOOR is the standing reference space that is meant to be moved, while STAGE is meant to be stationary.
For example this Khronos blog post says:
STAGE space is typically set up and calibrated by the user once corresponding to their real world room and then typically stays fixed until the user manually starts a new calibration procedure.
and
This is where LOCAL_FLOOR space was born: A space that can typically be recentered to the current position at the press of a button without a whole calibration procedure
Doesn't this support my interpretation, that Valve's implementation of STAGE is actually how LOCAL_FLOOR should behave?
I think the the OpenXR specification doesn't spell it out as clearly, but it does say:
The STAGE reference space is a runtime-defined flat, rectangular space that is empty and can be walked around on.
And I would argue that Valve's implementation is violating this requirement. By moving the bounds to be centered on the headset, the rectangle can and likely will include furniture and physical walls. Or, I think it would be technically correct to say that the STAGE->VIEW offset is zeroed when recentering the HMD, since the bounds never move relative to the STAGE origin?
I hadn't seen XR_REFERENCE_SPACE_TYPE_STATIONARY_EXT before (and in my defense, kagi finds exactly 1 match when searching for it :)) So the OpenXR 1.1 spec says the following about it:
Unlike STAGE space, the origin of the STATIONARY space is not impacted by the geometry of the user-defined room boundary, if there is any. The origin of STATIONARY space must not move only because the user redefines the room boundary.
But when the user redefines the room boundary (i.e. runs room setup in Steam), the origin in OpenVR also moves to the center of the new chaperone bounds, right? I think this should also be the behaviour of OpenXR STAGE space. My guess is that STATIONARY space is for more complex use cases, such as multiple rooms/play areas sharing the same origin.
I'll make a copy of my Godot test scene, remove the irrelevant stuff and upload it!
STAGE space is typically set up and calibrated by the user once corresponding to their real world room and then typically stays fixed until the user manually starts a new calibration procedure.
Hm. FWIW, what's getting done right now is LOCAL is the seated zero pose, STAGE is the standing zero pose, and STATIONARY is the reference play space. However, this specific piece of code is what I would politely term "a bit old and dusty" and it's quite possible it (and the comments in the code) were written to a draft spec of OpenXR and never touched after that.
Regardless, this passes the OpenXR compliance test suite so hasn't been looked at, but I just looked at the tests and... they just check if getting the space returns XR_SUCCESS, so that is... kind of a low bar here.
I'll bring this up to the team, but looking at the current spec it sounds like what we maybe should be doing is that LOCAL should be the repositioned standing zero pose, and both STAGE and STATIONARY should return the playspace. It would mean OpenXR would have no way to get the seated zero pose, but... OpenXR doesn't seem to distinguish between seated and standing zero anyway.
So I went and talked to the co-worker responsible for this, and got some background.
He pointed out that STAGE is not explicitly defined as motionless, just as the 'you can move around in this' versus 'this is your local reference space.' SteamVR allows you to reposition both your seated reference (e.g. your local origin) and your standing reference (the flat area in which you play).
We actually had changed it at one point so that STAGE did not move, and got a lot of Unhappy emails from people who were like "I cannot recenter my room-scale experience any longer?! Why did you break recenter?!" So it got reverted, which is why this behavior is back in place.
According to the co-worker, REFERENCE_SPACE_TYPE_STATIONARY_EXT was proposed and implemented in OpenXR specifically in response to this issue, because STAGE had a fuzzy definition -- it just defines 'this is a space in which you can play,' not whether or not the space moves -- so STATIONARY is actually meant to be the explicit "this is the immobile version of the stage"
As such, the recommended path (both from the co-worker, and technically with OpenXR 1.1 anyway) is to check if STATIONARY is supported if you need an immobile version of the stage, and use that if so. And if not, fall back to STAGE.
Does that help?
Thanks again for looking into this, despite not being directly Linux related!
When discussing the issue on Godot discord it was also theorized that it might be impossible to change it now, even if we reached consensus that it ought to be, since existing games are likely built around the current behaviour. I think using STATIONARY is could be a good solution, I'll try to take it up with Baastian Olij who is the main OpenXR dev for Godot.
My interpretation of OpenXR spaces is:
But that is only after reading that Khronos blog post, I couldn't get anything definite by just reading the OpenXR spec.
Nothing extracted yet.
Describe the bug
After running room setup, STAGE space is aligned with my chaperone bounds. But if I recenter the HMD using the Steam overlay, the STAGE space moves and the bounds I get from OpenXR are no longer correct.
Unfortunately I only have a Godot program which exhibits this issue, i.e. no clean MRP, but I've looked into the Godot Engine source code and talked to the developer who wrote it, and we think Godot uses OpenXR correctly. My interpretation of the OpenXR spec is that Steam's runtime behaves as if reference space type was LOCAL_FLOOR even when it is STAGE.
To Reproduce
Steps to reproduce the behavior:
xrLocateSpace(view_space, stage_space, ...)andxrGetReferenceSpaceBoundsRect(..., XR_REFERENCE_SPACE_TYPE_STAGE, ...)to render the stage space boundary as a polygon.IVRChaperone::GetPlayAreaRect(...)to render a second polygon.Expected behavior
Boundaries for OpenXR space STAGE always matching calibrated physical room
System Information (please complete the following information):
Please use the latest Steam beta client and SteamVR beta for your bug reports!
Screenshots
I don't think they add any more information than what I wrote above, some polygons that overlap in one case and don't in the other.
Additional context
I'm making a game in Godot and the whole point is that it takes place entirely within the physical bounds of the room. My current workaround is to use both OpenVR (for room bounds) and OpenXR (for everything else) in parallel.