8000 Add requestViewportScale/recommendedViewportScale by klausw · Pull Request #1132 · immersive-web/webxr · GitHub
[go: up one dir, main page]

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

Add requestViewportScale/recommendedViewportScale #1132

Merged
merged 5 commits into from
Sep 25, 2020
Merged
Changes from 3 commits
Commits
Show all changes
5 commits
Select commit Hold shift + click to select a range
< 10000 a href="/immersive-web/webxr/pull/1132/commits/97e0becc2646d22a838da429775c9d2ded3053a0" class="select-menu-item in-range" role="menuitem" data-commit="97e0becc2646d22a838da429775c9d2ded3053a0" >
97e0bec
Add requestViewportScale/recommendedViewportScale
klausw Sep 22, 2020
cbaae48
Address toji's review feedback
klausw Sep 24, 2020
1c3a3de
Address manishearth's requests
klausw Sep 24, 2020
27ee847
Fix view/XRView mixup
klausw Sep 24, 2020
634e8cf
Change "void" back to "undefined"
klausw Sep 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 additions & 3 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,7 @@ When an {{XRSession}} |session| receives updated [=viewer=] state for timestamp
1. Let |now| be the [=current high resolution time=].
1. Let |frame| be |session|'s [=XRSession/animation frame=].
1. Set |frame|'s [=XRFrame/time=] to |frameTime|.
1. For each |view| in [=XRSession/list of views=], set |view|'s [=view/viewport modifiable=] flag to true.
1. If the [=view/active=] flag of any [=view=] in the [=XRSession/list of views=] has changed since the last [=XR animation frame=], [=update the viewports=].
1. If the frame [=should be rendered=] for |session|:
1. Set |session|'s [=list of currently running animation frame callbacks=] to be |session|'s [=list of animation frame callbacks=].
Expand Down Expand Up @@ -1356,6 +1357,16 @@ A [=view=] has an <dfn for="view">active</dfn> flag that may change through the

Note: Many HMDs will request that content render two [=views=], one for the left eye and one for the right, while most magic window devices will only request one [=view=], but applications should never assume a specific view configuration. For example: A magic window device may request two views if it is capable of stereo output, but may revert to requesting a single view for performance reasons if the stereo output mode is turned off. Similarly, HMDs may request more than two views to facilitate a wide field of view or displays of different pixel density.

A [=view=] has an internal <dfn for="view">viewport modifiable</dfn> flag that indicates if the viewport scale can be changed by a {{XRView/requestViewportScale()}} call at this point in the session. It is set to `true` at the start of an animation frame, and set to `false` when {{XRWebGLLayer/getViewport()}} is called.

A [=view=] has an internal <dfn for="view">requested viewport scale</dfn> value that represents the requested viewport scale for this view. It is initially set to 1.0, and can be modified by the {{XRView/requestViewportScale()}} method if the system supports dynamic viewport scaling.

A [=view=] has an internal <dfn for="view">current viewport scale</dfn> value that represents the current viewport scale for this view as used internally by the system. It is initially set to 1.0. It is updated to match the [=view/requested viewport scale=] when the viewport change is successfully applied by a {{XRWebGLLayer/getViewport()}} call.

A [=view=] has an internal <dfn for="view">minimum viewport scale</dfn> value that represents the smallest supported dynamic viewport scale for this view. On a system that does not support dynamic viewport scaling, it equals 1.0. It must be a value greater than zero and less than or equal to 1.0, and does not change for the duration of a session. The minimum value MUST be large enough to ensure that the resulting viewport has nonzero width and height after scaling.

Note: Dynamic viewport scaling allows applications to render to a subset of the full-sized viewport using a scale factor that can be changed every animation frame. This is intended to be efficiently modifiable on a per-frame basis without reallocation. For correct rendering, it's essential that the XR system and application agree on the active viewport. An application can call {{XRView/requestViewportScale()}} for an {{XRView}} multiple times within a single animation frame, but the requested scale does not take effect until the application calls {{XRWebGLLayer/getViewport()}} for that view. The first `getViewport` call in an animation frame applies the change (taking effect immediately for the current animation frame), locks in the view's current scaled viewport for the remainder of this animation frame, and sets the scale as the new default for future animation frames. Optionally, the system can provide a suggested value through the {{XRView/recommendedViewportScale}} attribute based on internal performance heuristics and target framerates.

<pre class="idl">
enum XREye {
"none",
Expand All @@ -1367,6 +1378,9 @@ enum XREye {
readonly attribute XREye eye;
readonly attribute Float32Array projectionMatrix;
[SameObject] readonly attribute XRRigidTransform transform;
readonly attribute double? recommendedViewportScale;

void requestViewportScale(double? scale);
};
</pre>

Expand All @@ -1376,6 +1390,8 @@ The <dfn attribute for="XRView">projectionMatrix</dfn> attribute is the [=view/p

The <dfn attribute for="XRView">transform</dfn> attribute is the {{XRRigidTransform}} of the viewpoint. It represents the position and orientation of the viewpoint in the {{XRReferenceSpace}} provided in {{XRFrame/getViewerPose()}}.

The optional <dfn attribute for="XRView">recommendedViewportScale</dfn> attribute contains a UA-recommended viewport scale value that the application can use for a {{XRView/requestViewportScale()}} call to configure dynamic viewport scaling. It is `null` if the system does not implement a heuristic or method for determining a recommended scale. If not null, the value MUST be a numeric value between the [=view/minimum viewport scale=] and 1.0.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why we shouldn't report 1.0 instead of null on systems without any heuristics? Seems like a safer fall back, especially if developers start their development on a system that always provides a value and never test on one that doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would be unhelpful. Always reporting 1.0 would make it impossible to distinguish "this system doesn't provide a heuristic" from "the heuristic currently recommends scale 1.0". That would block applications from doing their own viewport scale calculations on systems that don't provide the heuristic.

Going back a step, if we want requestViewportScale(view.recommendedViewportScale) to work without errors on all systems, I could change requestViewportScale() to silently ignore non-numeric values such as null or undefined. Would that be preferable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be better, yes. Or have it treat non-numerics as an implicit "revert to default (1.0)". Either way prevents crashes of apps that haven't tested across a wide hardware range.


Each {{XRView}} has an associated <dfn for="XRView">session</dfn> which is the {{XRSession}} that produced it.

Each {{XRView}} has an associated <dfn for="XRView">frame time</dfn> which is the [=XRFrame/time=] of the {{XRFrame}} that produced it.
Expand All @@ -1386,6 +1402,20 @@ Each {{XRView}} has an associated <dfn for="XRView">internal projection matrix</

Note: The {{XRView/transform}} can be used to position camera objects in many rendering libraries. If a more traditional view matrix is needed by the application one can be retrieved by calling `view.transform.inverse.matrix`.

<div class="algorithm" data-algorithm="request-viewport-scale">

The <dfn method for="XRView">requestViewportScale(|scale|)</dfn> method requests that the user agent should set the [=view/requested viewport scale=] for this viewport to the requested value.

When this method is invoked on an {{XRView}} |view|, the user agent MUST run the following steps:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you still need to talk about |view|'s [=XRView/view=] below, since the min/requested viewport scale are properties of the [=view=]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, I had missed that distinction. I'm now using separate |xrview| and |view| variables to differentiate them.


1. If |scale| is null or undefined, abort these steps.
1. If |scale| is greater than 1.0, set |scale| to 1.0.
1. If |scale| is smaller than |view|'s [=view/minimum viewport scale=], set it to the [=view/minimum viewport scale=].
1. Set the |view|'s [=view/requested viewport scale=] value to |scale|.

Note: The method ignores null or undefined scale values so that applications can safely use `view.requestViewportScale(view.recommendedViewportScale)` even on systems that don't provide a recommended scale.

</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the idea that some non-primary views may want to scale separately from the primary views, and though it's a little weird to me that the primary views could scale their viewports separately it's something that appears to be well supported in the various backends.

Let's theorize about a backend that must have some/all of it's views using the same viewport size for whatever reason, though. The only compatibility path I see for that scenario is to have it force the minimum viewport scale to 1.0 across the board, thus effectively disabling this feature. (Though frankly if your backend is limited like that maybe it couldn't have supported this feature anyway?)

Does anyone think that's not sufficient? Any we might want a non-normative note that says something about how that case can be handled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion on this one, but IIRC there was a desire to support per-viewport scaling. I think the suggested workaround of disabling viewport scaling in the unlikely case that it's not supportable on a platform seems acceptable, but please speak up if this is concerning.


<div class=algorithm data-algorithm="obtain-xrview-projection">

Expand All @@ -1406,13 +1436,30 @@ When the [=view/active=] flag of any [=view=] in the [=XRSession/list of views=]
1. If |layer| is `null` abort these steps.
1. Set |layer|'s [=list of viewport objects=] to the empty [=/list=].
1. For each [=view/active=] [=view=] |view| in [=XRSession/list of views=]:
1. Let |glViewport| be the [=WebGL viewport=] from the [=list of viewports=] associated with |view|.
1. Let |viewport| be the {{XRViewport}} result of [=obtain a scaled viewport|obtaining a scaled viewport=] from the [=list of full-sized viewports=] associated with |view| for |session|.
1. [=list/Append=] |viewport| to |layer|'s [=list of viewport objects=].

</div>

<div class=algorithm data-algorithm="obtain-scaled-viewport">

To <dfn for="XRView">obtain a scaled viewport</dfn> for a given {{XRView}} |view| for an {{XRSession}} |session|

1. Let |glFullSizedViewport| be the [=WebGL viewport=] from the [=list of full-sized viewports=] associated with |view|.
1. Let |scale| be the |view|'s [=view/current viewport scale=].
1. Set |glViewport|'s `width` to an integer value less than or equal to |glFullSizedViewport|'s `width` multiplied by |scale|.
1. Set |glViewport|'s `height` to an integer value less than or equal to |glFullSizedViewport|'s `height` multiplied by |scale|.
1. Let |unusedFraction| be one minus |scale|.
1. Set |glViewport|'s `x` component to an integer value between |glFullSizedViewport|'s `x` component (inclusive) and |glFullSizedViewport|'s `x` component plus |unusedFraction| times |glFullSizedViewport|'s `width` (inclusive).
1. Set |glViewport|'s `y` component to a integer value between |glFullSizedViewport|'s `y` component (inclusive) and |glFullSizedViewport|'s `y` component plus |unusedFraction| times |glFullSizedViewport|'s `height` (inclusive).
1. Let |viewport| be a [=new=] {{XRViewport}} in the [=relevant realm=] of |session|.
1. Initialize |viewport|'s {{XRViewport/x}} to |glViewport|'s `x` component.
1. Initialize |viewport|'s {{XRViewport/y}} to |glViewport|'s `y` component.
1. Initialize |viewport|'s {{XRViewport/width}} to |glViewport|'s `width`.
1. Initialize |viewport|'s {{XRViewport/height}} to |glViewport|'s `height`.
1. [=list/Append=] |viewport| to |layer|'s [=list of viewport objects=].
1. Return |viewport|.

Note: The specific integer value calculation is intentionally left to the UA's discretion. The straightforward method of rounding down the width/height and using the `x` and `y` offsets as-is is valid, but the UA MAY also choose a slightly adjusted value within the specified constraints, for example to align the viewport to a pixel grid for efficiency. The scaled viewport MUST be completely contained within the full-sized viewport, but MAY be placed at any location within the full-sized viewport at the UA's discretion. The size and position calculation MUST be deterministic and return a consistent result for identical input values within a session.

</div>

Expand Down Expand Up @@ -2017,7 +2064,7 @@ Depth values stored in the buffer are expected to be between `0.0` and `1.0`, wi

Note: Making the scene's depth buffer available to the compositor allows some platforms to provide quality and comfort improvements such as improved reprojection.

Each {{XRWebGLLayer}} MUST have a <dfn>list of viewports</dfn> which is a [=/list=] containing one [=WebGL viewport=] for each [=view=] the {{XRSession}} may expose, including [=secondary views=] that are not currently [=view/active=] but may become [=view/active=] for the current session. The viewports MUST have a {{XRViewport/width}} and {{XRViewport/height}} greater than `0` and MUST describe a rectangle that does not exceed the bounds of the [=target framebuffer=]. The viewports MUST NOT be overlapping. If [=XRWebGLLayer/composition disabled=] is `true`, the [=list of viewports=] MUST contain a single [=WebGL viewport=] that covers the [=XRWebGLLayer/context=]'s entire default framebuffer.
Each {{XRWebGLLayer}} MUST have a <dfn>list of full-sized viewports</dfn> which is a [=/list=] containing one [=WebGL viewport=] for each [=view=] the {{XRSession}} may expose, including [=secondary views=] that are not currently [=view/active=] but may become [=view/active=] for the current session. The viewports MUST have a {{XRViewport/width}} and {{XRViewport/height}} greater than `0` and MUST describe a rectangle that does not exceed the bounds of the [=target framebuffer=]. The viewports MUST NOT be overlapping. If [=XRWebGLLayer/composition disabled=] is `true`, the [=list of full-sized viewports=] MUST contain a single [=WebGL viewport=] that covers the [=XRWebGLLayer/context=]'s entire default framebuffer.

Each {{XRWebGLLayer}} MUST have a <dfn>list of viewport objects</dfn> which is a [=/list=] containing one {{XRViewport}} for each [=view/active=] [=view=] the {{XRSession}} currently exposes.

Expand All @@ -2032,9 +2079,15 @@ The <dfn method for="XRWebGLLayer">getViewport(|view|)</dfn> method, when invoke
1. If |session| is not equal to |layer|'s [=XRWebGLLayer/session=], throw an {{InvalidStateError}} and abort these steps.
1. If |frame|'s [=XRFrame/active=] boolean is `false`, throw an {{InvalidStateError}} and abort these steps.
1. If |view|'s [=XRView/frame time=] is not equal to |frame|'s [=XRFrame/time=], throw an {{InvalidStateError}} and abort these steps.
1. If the [=view/viewport modifiable=] flag is `true` and |view|'s [=view/requested viewport scale=] is not equal to [=view/current viewport scale=]:
1. Set [=view/current viewport scale=] to [=view/requested viewport scale=].
1. In the [=list of viewport objects=], set the {{XRViewport}} associated with |view| to the {{XRViewport}} result of [=obtain a scaled viewport|obtaining a scaled viewport=] from the [=list of full-sized viewports=] associated with |view| for |session|.
1. Set the |view|'s [=view/viewport modifiable=] flag to false.
1. Let |viewport| be the {{XRViewport}} from the [=list of viewport objects=] associated with |view|.
1. Return |viewport|.

Note: The [=view/viewport modifiable=] flag is intentionally set to false even if there was no change to the [=view/current viewport scale=]. This ensures that `getViewport(view)` calls always return consistent results within an animation frame for that view, so the first retrieved value is locked in for the remainder of the frame. If an application calls {{XRView/requestViewportScale()}} after `getViewport()`, the requested value is only applied later when `getViewport()` is called again in a future frame.

</div>

Each {{XRSession}} MUST identify a <dfn>native WebGL framebuffer resolution</dfn>, which is the pixel resolution of a WebGL framebuffer required to match the physical pixel resolution of the [=XRSession/XR device=].
Expand Down
0