From abb8c7226e5f80fc5b4d06037d4ffad790c545c3 Mon Sep 17 00:00:00 2001 From: Brandon Jones Date: Fri, 6 Jan 2017 16:19:54 -0800 Subject: [PATCH] Proposal for converting VRLayers into constructible objects Attempts to address #142, #107, #166, #156, and #125. Feedback welcome! --- index.bs | 114 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 49 deletions(-) diff --git a/index.bs b/index.bs index c54104b1..03f6bcf9 100644 --- a/index.bs +++ b/index.bs @@ -150,10 +150,8 @@ interface VRDisplay : EventTarget { /** * Begin presenting to the VRDisplay. Must be called in response to a user gesture. * Repeat calls while already presenting will update the VRLayers being displayed. - * If the number of values in the leftBounds/rightBounds arrays is not 0 or 4 for any of the passed layers the promise is rejected - * If the source of any of the layers is not present (null), the promise is rejected. */ - Promise<void> requestPresent(sequence<VRLayerInit> layers); + Promise<void> requestPresent(sequence<VRLayer> layers); /** * Stops presenting to the VRDisplay. @@ -207,13 +205,13 @@ Functionally equivalent to requestPresent() -Begins presenting the contents of the specified {{VRLayer}}s, described by {{VRLayerInit}} dictionaries, on the {{VRDisplay}} and fulfills the returned promise when presentation has begun. If {{canPresent}} is false the promise MUST be rejected. If the array contains more than {{maxLayers}} elements the promise MUST be rejected. If {{requestPresent()}} is called outside of a user gesture the promise MUST be rejected. The user agent MAY reject the promise for any other reason. If the {{VRDisplay}} is already presenting when {{requestPresent()}} is called the {{VRDisplay}} SHOULD update the {{VRLayer}} list being presented. If a call to {{requestPresent()}} is rejected while the {{VRDisplay}} is already presenting the {{VRDisplay}} MUST end presentation. +Begins presenting the contents of the specified {{VRLayer}}s on the {{VRDisplay}} and fulfills the returned promise when presentation has begun. If {{canPresent}} is false the promise MUST be rejected. If the array contains more than {{maxLayers}} elements the promise MUST be rejected. If {{requestPresent()}} is called outside of a user gesture the promise MUST be rejected. The user agent MAY reject the promise for any other reason. If the {{VRDisplay}} is already presenting when {{requestPresent()}} is called the {{VRDisplay}} SHOULD update the {{VRLayer}} list being presented. If a call to {{requestPresent()}} is rejected while the {{VRDisplay}} is already presenting the {{VRDisplay}} MUST end presentation. exitPresent() Ends presentation to the {{VRDisplay}} and fulfills the returned promise when fully exited. If the {{VRDisplay}} is not presenting the promise MUST be rejected. getLayers() -Returns an array with the {{VRLayer}} currently being presented. MUST return an empty array if the {{VRDisplay}} is not currently presenting. If the {{VRDisplay}} is presenting MUST return an array containing the {{VRLayer}}s last passed to {{requestPresent()}}. +Returns an array of the {{VRLayer}} currently being presented. MUST return an empty array if the {{VRDisplay}} is not currently presenting. If the {{VRDisplay}} is presenting MUST return an array containing copies of {{VRLayer}}s last passed to {{requestPresent()}}. submitFrame() Captures the current state of the {{VRLayer}} currently being presented and displays it on the {{VRDisplay}}. It is assumed that the frame was rendered using the {{VRPose}} and matrices provided by the last call to {{getFrameData()}}. If {{getFrameData()}} was not called prior to calling {{submitFrame()}} the user agent MAY warn the user of potentially malformed visuals or prevent the frame from being shown at all. @@ -252,7 +250,7 @@ function onVRFrame() { } // Begin presentation (must be called within a user gesture) -vrDisplay.requestPresent([{ source: canvas }]).then(function() { +vrDisplay.requestPresent([ new CanvasVRLayer(canvas) ]).then(function() { vrDisplay.requestAnimationFrame(onVRFrame); }); @@ -261,39 +259,78 @@ vrDisplay.requestPresent([{ source: canvas }]).then(function() { ## VRLayer ## {#interface-vrlayer} -The {{VRLayer}} interface is provided to a {{VRDisplay}} and presented in the HMD. +The {{VRLayer}} is the base interface for the various layer types that can be +provided to a {{requestPresent()}} call to be presented in the HMD. -
+
+interface VRLayer {};
+
+ +## CanvasVRLayer ## {#interface-canvasvrlayer} + +The {{CanvasVRLayer}} describes a layer containing head tracked stereo content +rendered to a canvas with a WebGL context. + +
 typedef (HTMLCanvasElement or
-         OffscreenCanvas) VRSource;
+         OffscreenCanvas) CanvasVRSource;
 
-[Constructor(optional VRLayerInit layer)]
-interface VRLayer {
-  readonly attribute VRSource? source;
+[Constructor(optional CanvasVRSource source)]
+interface CanvasVRLayer : VRLayer {
+  attribute CanvasVRSource source;
 
-  readonly attribute sequence<float> leftBounds;
-  readonly attribute sequence<float> rightBounds;
-};
+  attribute float renderScale;
+  attribute boolean resizeSourceOnPresent;
+
+  readonly attribute unsigned long renderWidth;
+  readonly attribute unsigned long renderHeight;
 
-dictionary VRLayerInit {
-  VRSource? source = null;
+  void setLeftBounds(float left, float bottom, float right, float top);
+  sequence<float> getLeftBounds();
 
-  sequence<float> leftBounds = [ ];
-  sequence<float> rightBounds = [ ];
+  void setRightBounds(float left, float bottom, float right, float top);
+  sequence<float> getRightBounds();
 };
 
-### Attributes ### {#vrlayer-attributes} +### Attributes ### {#canvasvrlayer-attributes} + +source +The {{CanvasVRLayer/source}} attribute defines the canvas whose contents will be presented for this layer by the {{VRDisplay}} when {{VRDisplay}}.{{submitFrame()}} is called. Upon being passed into {{requestPresent()}} the current backbuffer of the source's context MAY be lost, even if the context was created with the preserveDrawingBuffer context creation attribute set to true. Calls to {{requestPresent()}} MUST be rejected if the {{CanvasVRLayer/source}} of any {{CanvasVRLayer}} provided does not represent a canvas with a webgl or webgl2 context. + +renderScale +Specifies the ratio of the number of canvas pixels to the {{VRDisplay}}'s physical pixels at the center of the output image. Note that a value of 1.0 will generally result in a larger canvas resolution than the {{VRDisplay}}'s physical screen in order to allow for corrective distiorion. Specifying a lower value can improve performance. A value of 0.0 instructs the user agent to select a ratio that provides a good balance between quality and performance for most applications on the current {{VRDisplay}}. Calls to {{requestPresent()}} MUST be rejected if the renderScale of any {{CanvasVRLayer}} provided is less than 0.0. + +If this value was set to 0.0 when the {{CanvasVRLayer}} is passed to {{requestPresent()}} the {{renderScale}} of the corresponding {{CanvasVRLayer}} returned from {{getLayers()}} should be set to the value selected by the user agent. + +The default value for {{renderScale}} MUST be 0.0. + +resizeSourceOnPresent +If true the {{CanvasVRLayer/source}} of any {{CanvasVRLayer}} provided to {{requestPresent()}} will be resized to the width and height computed from the {{renderScale}} before the promise resolves or the vrdisplaypresentchange events are fired. If {{requestPresent()}} is rejected the {{CanvasVRLayer/source}} dimensions will not be changed. + +The default value for {{resizeSourceOnPresent}} MUST be true. + +renderWidth +When a {{CanvasVRLayer}} is returned from {{getLayers()}} this attribute MUST be set to the width computed from the {{renderScale}} when the {{CanvasVRLayer}} was passed to {{requestPresent()}}. -source -The {{VRLayer/source}} attribute defines the canvas whose contents will be presented by the {{VRDisplay}} when {{VRDisplay}}.{{submitFrame()}} is called. Upon being passed into {{requestPresent()}} the current backbuffer of the source's context MAY be lost, even if the context was created with the preserveDrawingBuffer context creation attribute set to true. +renderHeight +When a {{CanvasVRLayer}} is returned from {{getLayers()}} this attribute MUST be set to the height computed from the {{renderScale}} when the {{CanvasVRLayer}} was passed to {{requestPresent()}}. -leftBounds -The {{VRLayer/leftBounds}} attribute contains four values defining the texture bounds within the {{VRLayer/source}} canvas to present to the eye in UV space: [0] left offset of the bounds (0.0 - 1.0); [1] top offset of the bounds (0.0 - 1.0); [2] width of the bounds (0.0 - 1.0); [3] height of the bounds (0.0 - 1.0). The {{VRLayer/leftBounds}} MUST default to [0.0, 0.0, 0.5, 1.0]. +setLeftBounds() +Sets the portion of the canvas to present to the left eye. Bounds are specified as texture coordinates in UV space (0.0 - 1.0), where (0, 0) represents the bottom left corner of the canvas and (1, 1) represents the top right corner. Calls to {{requestPresent()}} MUST be rejected if the left bounds of any {{CanvasVRLayer}} provided specifies bounds where right is less than left or top is less than bottom. -rightBounds -The {{VRLayer/rightBounds}} attribute contains four values defining the texture bounds rectangle within the {{VRLayer/source}} canvas to present to the eye in UV space: [0] left offset of the bounds (0.0 - 1.0); [1] top offset of the bounds (0.0 - 1.0); [2] width of the bounds (0.0 - 1.0); [3] height of the bounds (0.0 - 1.0). The {{VRLayer/rightBounds}} MUST default to [0.5, 0.0, 0.5, 1.0]. +The default left eye bounds MUST be (0.0, 0.0, 0.5, 1.0). +getLeftBounds() +Returns the current texture bounds for the left eye as a sequence of floats in the following order: [left, bottom, right, top] + +setRightBounds() +Sets the portion of the canvas to present to the right eye. Bounds are specified as texture coordinates in UV space (0.0 - 1.0), where (0, 0) represents the bottom left corner of the canvas and (1, 1) represents the top right corner. Calls to {{requestPresent()}} MUST be rejected if the right bounds of any {{CanvasVRLayer}} provided specifies bounds where right is less than left or top is less than bottom. + +The default right eye bounds MUST be (0.5, 0.0, 1.0, 1.0). + +getRightBounds() +Returns the current texture bounds for the right eye as a sequence of floats in the following order: [left, bottom, right, top] ## VRDisplayCapabilities ## {#interface-vrdisplaycapabilities} @@ -515,9 +552,6 @@ interface VREyeParameters { readonly attribute Float32Array offset; [SameObject] readonly attribute VRFieldOfView fieldOfView; - - readonly attribute unsigned long renderWidth; - readonly attribute unsigned long renderHeight; };
@@ -529,24 +563,6 @@ A three component vector describing the offset from the center point between the fieldOfView (Deprecated) The current field of view for the eye. SHOULD conservatively cover the entire viewable frustum of the eye. The application should not use these values to construct a projection matrix, as it may not take into account all aspects of the {{VRDisplay}} optics. Prefer using the projection matrices provided in {{VRFrameData}} instead. -renderWidth -Describes the recommended render target width of each eye viewport, in pixels. If multiple eyes are rendered in a single render target, then the render target should be made large enough to fit both viewports. The {{renderWidth}} for the left eye and right eye MUST NOT overlap, and the {{renderWidth}} for the right eye MUST be to the right of the {{renderWidth}} for the left eye. - -renderHeight -Describes the recommended render target height of each eye viewport, in pixels. If multiple eyes are rendered in a single render target, then the render target should be made large enough to fit both viewports. The {{renderWidth}} for the left eye and right eye MUST NOT overlap, and the {{renderWidth}} for the right eye MUST be to the right of the {{renderWidth}} for the left eye. - -
-Many HMDs will distort the rendered image to counteract undesired effects introduced by the headset optics. Because of this the optimal resolution of the canvas will often be larger than the HMD's physical resolution to ensure that the final image presented to the user has a 1:1 pixel ratio at the center of the user's view. The optimal canvas resolution can be calculated from the {{renderWidth}} and {{renderHeight}} for both eyes as follows: - -
-var leftEye = vrDisplay.getEyeParameters("left");
-var rightEye = vrDisplay.getEyeParameters("right");
-
-canvas.width = Math.max(leftEye.renderWidth, rightEye.renderWidth) * 2;
-canvas.height = Math.max(leftEye.renderHeight, rightEye.renderHeight);
-
-
- ## VRStageParameters ## {#interface-vrstageparameters} @@ -736,13 +752,13 @@ Return the {{VRDisplay/displayId}} of the {{VRDisplay}} this {{Gamepad}} is asso While not directly affecting the API interface and Web IDL, WebVR implementations should maintain the user's expectations of privacy, security, and comfort on the Web by adhering to the following guidelines: -* Trusted UI must be drawn by an independent rendering context who's state is isolated from that of the WebGL contexts provided as a {{VRLayer}} source to a {{VRDisplay}}. +* Trusted UI must be drawn by an independent rendering context who's state is isolated from that of the WebGL contexts provided as a {{CanvasVRLayer/source}} to a {{VRDisplay}}. * A "VR Compositor" runs asynchronously from content, responsible for compositing the trusted and untrusted content. If content is not performant or does not submit frames, or terminates unexpectedly the browser should be able to continue presenting a responsive front-end. * Users should always be provided an action, such as pressing a reserved hardware button or performing a gesture, that escapes out of WebVR content and displays the browsers trusted UI. * When navigating between pages in VR the browser should display trusted UI elements informing the user of the security information of the site they are navigating to which is normally presented by the 2D UI, such as the URL and encryption status. * The {{VRDisplay}} pose and other VR inputs are only updated for the focused page. * The Gamepad API will be updated such that the gamepad inputs are only updated for the focused page. * Non-focused tabs are allowed to enumerate {{Gamepad}}s and {{VRDisplay}}s but will see last received state or default values. -* To prevent CORS-related vulnerabilities, each page will see a new instance of objects returned by the WebVR API, such as {{VRDisplay}}. Attributes such as the {{VRLayer}}.{{VRLayer/source}} set by one page must not be able to be read by another. +* To prevent CORS-related vulnerabilities, each page will see a new instance of objects returned by the WebVR API, such as {{VRDisplay}}. Attributes such as the {{CanvasVRLayer/source}} set by one page must not be able to be read by another. # Acknowledgements # {#ack}