1. Introduction
This specification describes a mechanism for rendering WebXR content using WebGPU, instead of WebGL.
It adds support for creation of XRCompositionLayers, as described in the WebXR Layers API, which are rendered using the WebGPU API.
WebGPU is an API for utilizing the graphics and compute capabilities of a device’s GPU more efficiently than WebGL allows, with an API that better matches both GPU hardware architecture and the modern native APIs that interface with them, such as Vulkan, Direct3D 12, and Metal.
1.1. Terminology
This specification uses the terms XR device, XR Compositor, XRSession, XRFrame, XRView, and feature descriptor as defined in the WebXR Device API specification.
It uses the terms XRCompositionLayer, XRProjectionLayer, XRQuadLayer, XRCylinderLayer, XREquirectLayer, XRCubeLayer, XRSubImage, and XRWebGLBinding as defined in the WebXR Layers API specification.
It uses the terms GPUDevice, GPUAdapter, GPUQueue, GPUTexture, GPUTextureViewDescriptor, GPUTextureFormat, and GPUTextureUsageFlags as defined in the WebGPU specification.
1.2. Application flow
If an author wants to use WebGPU to render content for a WebXR Session, they must perform the following steps:
In no particular order
-
Create a
GPUDevicefrom anGPUAdapterwhich was requested with thexrCompatibleoption set totrue.
Then
-
Create an
XRGPUBindingwith both the XR-compatibleGPUDeviceand WebGPU-compatible session. -
Create one or more
XRCompositionLayers with theXRGPUBinding -
Add the layers to
XRRenderStateInitand callupdateRenderState(). -
During
requestAnimationFrame()for each WebGPU layer:-
For each
XRGPUSubImageexposed by the layer:-
Draw the contents of the subimage using the
GPUDevicetheXRGPUBindingwas created with.
-
-
2. Initialization
2.1. Feature Descriptor
The string "webgpu" is introduced by this module as a new valid feature descriptor for the WebXR/WebGPU Binding feature.
If a user agent wants to use WebGPU for rendering during a session, the session MUST be requested with the webgpu feature descriptor. XRSessions created with this feature are referred to as WebGPU-compatible sessions.
A WebGPU-compatible session MUST have the following behavioral differences from a WebGL-compatible session:
-
"inline"sessions are NOT supported. -
XRWebGLBindingandXRWebGLLayerinstances MUST NOT be created with the session. -
XRGPUBindinginstances CAN be created for the session. -
baseLayerMUST NOT be set inupdateRenderState().layersMUST be used instead. -
The projection matrix returned by
XRViewfor this session MUST use a clip-space depth range of [0, 1] instead of the WebGL default [-1, 1].
const session= await navigator. xr. requestSession( 'immersive-vr' , { requiredFeatures: [ 'webgpu' ] });
NOTE: The webgpu feature may be passed to either requiredFeatures or optionalFeatures. If passed to optionalFeatures, the author MUST check enabledFeatures after the session is created and use either WebGPU or WebGL to render the session’s content depending on whether webgpu is present.
2.2. GPUAdapter Integration
To create a GPUDevice that is compatible with an XR device, the GPUAdapter used to create it must have been requested with the xrCompatible option set to true.
partial dictionary GPURequestAdapterOptions {boolean xrCompatible =false ; };
The xrCompatible option, when set to true, indicates that the returned GPUAdapter MUST be compatible with the XR device selected by the user agent. If no GPUAdapter can satisfy this constraint, the request MUST return null.
NOTE: There is no WebGPU equivalent to the WebGLRenderingContextBase.makeXRCompatible() method. If a user agent needs to ensure XR compatibility, the GPUAdapter MUST be requested with xrCompatible set to true from the start.
An XR-compatible adapter is a GPUAdapter that was successfully returned from a requestAdapter() call with xrCompatible set to true.
An XR-compatible device is a GPUDevice that was created from an XR-compatible adapter.
3. Layer Types
XRCompositionLayer, XRProjectionLayer, XRQuadLayer, XRCylinderLayer,
XREquirectLayer, and XRCubeLayer are defined by the WebXR Layers API.
An XRCompositionLayer created by an XRGPUBinding is a WebGPU-backed layer. Each
WebGPU-backed layer has an associated WebGPU device, which
is the device of the XRGPUBinding that created it.
4. Rendering
4.1. XRGPUSubImage
An XRGPUSubImage represents a view into a WebGPU-backed composition layer’s textures. It provides the GPUTextures to render into and a GPUTextureViewDescriptor that describes which portion of the texture corresponds to the requested view.
The viewport describes the region of the colorTexture that corresponds to the requested view. When present, the depthStencilTexture has the same corresponding region, except when the motionVectorTexture is not null. In that case, the viewport does not describe the region of the depth/stencil or motion vector texture.
Each XRGPUSubImage has an associated non-negative integer array layer
index, which identifies the first array layer belonging to the subimage.
Each WebGPU-backed layer has an associated current color texture, an optional
current depth-stencil texture, a current texture set, and a
current texture frame, all initially null. Each WebGPU-backed XRProjectionLayer
also has an optional current motion vector texture, initially null. The current
textures are GPUTexture objects that expose the backing resources of the layer’s
current texture set during its current texture frame.
The user agent MUST return the same current GPUTexture objects for every subimage of a layer
within an XR animation frame. After that frame’s animation frame callbacks have completed, the
user agent MUST release the WebGPU textures for the layer. A retained XRGPUSubImage will
therefore return destroyed textures after the frame in which it was obtained.
Calling destroy() on a current color texture, current depth-stencil texture,
or current motion vector texture MUST terminate write access through that GPUTexture, but
MUST NOT destroy or alter the WebGPU layer texture resource that backs it. The layer continues
to reference the same destroyed GPUTexture until its current textures are expired. Consequently,
subsequent subimage requests for the same layer in the same frame return the same destroyed texture.
[Exposed =(Window ),SecureContext ]interface :XRGPUSubImage XRSubImage { [SameObject ]readonly attribute GPUTexture colorTexture ; [SameObject ]readonly attribute GPUTexture ?depthStencilTexture ; [SameObject ]readonly attribute GPUTexture ?motionVectorTexture ;GPUTextureViewDescriptor getViewDescriptor (); };
4.1.1. Attributes
The colorTexture attribute returns the GPUTexture to be used as the color attachment when rendering this sub image. Its backing resource is allocated and managed by the user agent. The same GPUTexture object is returned for all subimages of the same layer within a single frame. Use the result of getViewDescriptor() to determine which array layer of the texture to render to.
The returned texture has the following properties:
-
dimension: The
dimensionof the layer’s color texture descriptor. -
format: The
formatof the layer’s color texture descriptor. -
width: The width component of the
sizeof the layer’s color texture descriptor. -
height: The height component of the
sizeof the layer’s color texture descriptor. -
depthOrArrayLayers: The depth or array layer count component of the
sizeof the layer’s color texture descriptor. -
usage: The
usageof the layer’s color texture descriptor. -
mipLevelCount: The
mipLevelCountof the layer’s color texture descriptor. -
sampleCount:
1.
The depthStencilTexture attribute returns the GPUTexture containing the depth/stencil data for this sub image, or null if no depth/stencil format was specified when creating the layer. When this texture is present and the motionVectorTexture is null, it has the same dimensions as the colorTexture and can be used as the depth/stencil attachment in the render pass that renders to the color texture. When the motion vector texture is not null, the depth/stencil texture is also present, has the same dimensions as the motion vector texture, may have different dimensions than the color texture, and is rendered separately as described in § 6 Space Warp. When provided, the user agent MAY use the depth information to improve composition quality (for example, for reprojection).
When present, the returned texture has the following properties:
-
dimension: The
dimensionof the layer’s depth-stencil texture descriptor. -
format: The
formatof the layer’s depth-stencil texture descriptor. -
width: The width component of the
sizeof the layer’s depth-stencil texture descriptor. -
height: The height component of the
sizeof the layer’s depth-stencil texture descriptor. -
depthOrArrayLayers: The depth or array layer count component of the
sizeof the layer’s depth-stencil texture descriptor. -
usage: The
usageof the layer’s depth-stencil texture descriptor. -
mipLevelCount: The
mipLevelCountof the layer’s depth-stencil texture descriptor. -
sampleCount:
1.
NOTE: If a depthStencilFormat other than "stencil8" was provided during layer creation, it is implied that the author will populate it with an accurate representation of the scene’s depth. If the depth information is not representative of the rendered scene, the user agent SHOULD allocate its own depth/stencil textures rather than use the layer-provided one.
The motionVectorTexture attribute returns the GPUTexture to be used as the motion vector attachment when rendering this sub image. It returns null unless the layer is an XRProjectionLayer, the space-warp feature descriptor was enabled when the session was created, and the layer was created with an depthStencilFormat that is present and is not "stencil8".
When present, the returned texture has the following properties:
-
dimension: The
dimensionof the layer’s motion vector texture descriptor. -
format: The
formatof the layer’s motion vector texture descriptor. -
width: The width component of the
sizeof the layer’s motion vector texture descriptor. -
height: The height component of the
sizeof the layer’s motion vector texture descriptor. -
depthOrArrayLayers: The depth or array layer count component of the
sizeof the layer’s motion vector texture descriptor. -
usage: The
usageof the layer’s motion vector texture descriptor. -
mipLevelCount: The
mipLevelCountof the layer’s motion vector texture descriptor. -
sampleCount:
1.
4.1.2. getViewDescriptor
The getViewDescriptor() method returns a GPUTextureViewDescriptor configured for creating a texture view of this sub image’s portion of the layer’s textures. The returned descriptor can be passed to GPUTexture.createView() on the colorTexture, depthStencilTexture, and motionVectorTexture.
When invoked, the user agent MUST run the following steps:
-
Let descriptor be a new
GPUTextureViewDescriptor. -
Set descriptor’s
dimensionto"2d". -
Set descriptor’s
mipLevelCountto 1. -
Set descriptor’s
arrayLayerCountto 1. -
Set descriptor’s
baseArrayLayerto thisXRGPUSubImage’s array layer index. -
Return descriptor.
NOTE: The returned descriptor selects a single 2D slice from the texture array via baseArrayLayer paired with an arrayLayerCount of 1. The viewport still needs to be applied via setViewport() when rendering to the color texture and, when the motionVectorTexture is null, when rendering to the depth/stencil texture. Depth/stencil and motion vector textures used for space warp are rendered to their full extent as described in § 6 Space Warp.
NOTE: For an XRCubeLayer, the returned descriptor selects the first face for the requested eye.
Authors can render the remaining faces by creating descriptors with baseArrayLayer increased by
1 through 5, following the face order defined for createCubeLayer().
const subImage= binding. getViewSubImage( layer, view); const viewDesc= subImage. getViewDescriptor(); const colorView= subImage. colorTexture. createView( viewDesc); const depthView= subImage. depthStencilTexture. createView( viewDesc);
5. GPU Layer and View Creation
This section defines the binding, initialization dictionaries, and algorithms used to create WebGPU-backed layers and access their per-frame texture views.
5.1. Texture allocation
Each WebGPU-backed layer has an internal color texture descriptor and an optional
depth-stencil texture descriptor. Each WebGPU-backed XRProjectionLayer also has an
optional motion vector texture descriptor. These GPUTextureDescriptors describe the
textures used to render the layer.
A WebGPU layer texture resource is GPU texture storage allocated by the user agent for a
WebGPU-backed layer using the layer’s WebGPU device. It is not itself a
Web-exposed GPUTexture, but it can be exposed through a GPUTexture whose underlying storage
points to the resource. Its allocation MUST be attributed to the GPUDevice and subject to the
same resource limits as a texture created with createTexture().
A WebGPU layer texture set is an internal tuple containing one color
WebGPU layer texture resource, an optional depth-stencil resource, and an optional motion vector
resource. Each set has an available boolean. Each
WebGPU-backed layer has an internal list of texture sets.
Until destroy() is invoked, the list’s length is implementation-defined but
MUST be greater than zero. The resources in every set MUST have the properties specified by their
corresponding texture descriptors.
If a WebGPU-backed layer has two or more mipLevels, the author SHOULD
populate every mip level. The user agent MUST NOT generate the mip levels on the author’s behalf.
To create an XR GPU texture descriptor with an XRGPUBinding binding, positive
integers width, height, arrayLayerCount, and mipLevelCount, a GPUTextureFormat format,
and GPUTextureUsageFlags usage, the user agent MUST run the following steps:
-
Let descriptor be a new
GPUTextureDescriptorwith the following members:-
sizeset to aGPUExtent3Dwhose width is width, height is height, and depth or array layer count is arrayLayerCount. -
mipLevelCountset to mipLevelCount. -
sampleCountset to1. -
formatset to format. -
usageset to usage. -
viewFormatsset to an empty sequence.
-
-
If descriptor would not satisfy the validation requirements for
createTexture()when invoked on binding’s device, throw aNotSupportedErrorDOMException. -
Return descriptor.
To allocate WebGPU layer texture sets with an XRGPUBinding binding, a
GPUTextureDescriptor colorDescriptor, a nullable GPUTextureDescriptor
depthStencilDescriptor, and a nullable GPUTextureDescriptor motionVectorDescriptor, the user
agent MUST run the following steps:
-
Let textureSets be a new empty list.
-
Let textureSetCount be an implementation-defined positive integer sufficient to allow the application and the XR Compositor to use different texture sets concurrently.
-
Repeat textureSetCount times:
-
Let textureSet be a new WebGPU layer texture set.
-
Allocate textureSet’s color resource using binding’s device with the storage properties described by colorDescriptor.
-
If depthStencilDescriptor is not
null, allocate textureSet’s depth-stencil resource using binding’s device with the storage properties described by depthStencilDescriptor. Otherwise, set its depth-stencil resource tonull. -
If motionVectorDescriptor is not
null, allocate textureSet’s motion vector resource using binding’s device with the storage properties described by motionVectorDescriptor. Otherwise, set its motion vector resource tonull. -
If the user agent was unable to allocate any resource for textureSet, release every resource allocated by these steps and throw an
OperationErrorDOMException. -
Set textureSet’s available boolean to
true. -
Append textureSet to textureSets.
-
-
Return textureSets.
To acquire WebGPU textures for a WebGPU-backed layer layer and an XRFrame
frame, the user agent MUST run the following steps:
-
Let device be layer’s WebGPU device.
-
If device has been destroyed, throw an
InvalidStateErrorDOMException. -
If layer’s list of texture sets is empty, throw an
InvalidStateErrorDOMException. -
If layer’s current texture set is not
null:-
Assert that layer’s current texture frame is frame.
-
Return.
-
-
Let textureSet be an entry in layer’s list of texture sets whose available boolean is
true. -
Assert that textureSet exists. The user agent MUST ensure that a texture set is available before invoking the animation frame callbacks for frame.
-
Set textureSet’s available boolean to
false. -
Set layer’s current texture set to textureSet and its current texture frame to frame.
-
Set layer’s current color texture to the result of calling device.
createTexture()with layer’s color texture descriptor, except with theGPUTexture’s underlying storage pointing to textureSet’s color resource. -
If layer’s depth-stencil texture descriptor is not
null, set layer’s current depth-stencil texture to the result of calling device.createTexture()with that descriptor, except with theGPUTexture’s underlying storage pointing to textureSet’s depth-stencil resource. -
If layer is an
XRProjectionLayerand its motion vector texture descriptor is notnull, set layer’s current motion vector texture to the result of calling device.createTexture()with that descriptor, except with theGPUTexture’s underlying storage pointing to textureSet’s motion vector resource. -
Clear every color and motion vector resource in textureSet to zero. Clear every depth component in its depth-stencil resource to
1.0and every stencil component to0.
To expire the WebGPU textures for a WebGPU-backed layer layer, the user agent MUST run the following steps:
-
If layer’s current color texture is not
null, calldestroy()on it (without destroying the underlying storage) to terminate write access to the image. -
If layer’s current depth-stencil texture is not
null, calldestroy()on it (without destroying the underlying storage) to terminate write access to the image. -
If layer is an
XRProjectionLayerand its current motion vector texture is notnull, calldestroy()on it (without destroying the underlying storage) to terminate write access to the image. -
Set layer’s current color texture and current depth-stencil texture to
null. -
If layer is an
XRProjectionLayer, set its current motion vector texture tonull. -
Set layer’s current texture set and current texture frame to
null.
To release the WebGPU textures for a WebGPU-backed layer layer and an XRFrame
frame, the user agent MUST run the following steps:
-
If layer’s current texture frame is not frame, return.
-
Let textureSet be layer’s current texture set.
-
Before the XR Compositor consumes textureSet, ensure that all WebGPU work submitted during the processing of frame that writes to the current textures has completed, or synchronize the compositor with that work using an equivalent GPU-side dependency. If multiple
submit()calls write to a current texture during frame, this synchronization MUST include the last such submission. -
Make textureSet’s backing resources available to the XR Compositor for frame.
-
Run expire the WebGPU textures for layer.
-
Set layer’s
needsRedrawtofalse. -
Once the XR Compositor no longer accesses textureSet’s backing resources, set textureSet’s available boolean to
true.
When destroy() is invoked on a WebGPU-backed layer layer, the user
agent MUST run the following steps instead of the WebXR Layers API’s steps for calling destroy()
on a layer:
-
Let textureSets be layer’s list of texture sets.
-
Run expire the WebGPU textures for layer.
-
Set layer’s list of texture sets to an empty list.
-
For each textureSet of textureSets, once neither previously submitted WebGPU work nor the XR Compositor accesses its backing resources, release each non-null backing resource in textureSet.
The XR animation frame algorithm is extended as follows: immediately before setting its
active boolean to false, the user agent MUST release the WebGPU textures for every
WebGPU-backed layer whose current texture frame is that XRFrame.
5.2. Supported Texture Formats
The supported color formats for XRGPUBinding layer creation are:
-
"rgba8unorm" -
"bgra8unorm" -
"rgba16float"
The supported depth/stencil formats for XRGPUBinding layer creation are:
-
"stencil8" -
"depth16unorm" -
"depth24plus" -
"depth24plus-stencil8" -
"depth32float" -
"depth32float-stencil8"
The formats listed above are the only formats that can be used for layer creation. User agents MUST NOT accept formats outside of these lists. A listed format can still be rejected when it does not satisfy the WebGPU feature or usage requirements of the device.
5.3. XRGPUProjectionLayerInit
The XRGPUProjectionLayerInit dictionary is used to configure projection layers created with createProjectionLayer().
dictionary {XRGPUProjectionLayerInit required GPUTextureFormat colorFormat ;GPUTextureFormat ?depthStencilFormat ;GPUTextureUsageFlags textureUsage = 0x10; // GPUTextureUsage.RENDER_ATTACHMENTdouble scaleFactor = 1.0; };
const layer= binding. createProjectionLayer({ colorFormat: binding. getPreferredColorFormat(), depthStencilFormat: 'depth24plus-stencil8' , });
The colorFormat member specifies the GPUTextureFormat for the layer’s color textures. This MUST be a supported color format.
The depthStencilFormat member, when present, specifies the GPUTextureFormat for the layer’s depth/stencil textures. This MUST be a supported depth-stencil format. When not present, no depth/stencil texture is allocated. A motion vector texture is allocated only when the space-warp feature descriptor is enabled and this member is present and is not "stencil8".
The textureUsage member specifies the GPUTextureUsageFlags to be set on the allocated textures. The default value is GPUTextureUsage.RENDER_ATTACHMENT. If overriding this value, developers MUST explicitly include RENDER_ATTACHMENT if they intend to use the textures as render attachments.
The scaleFactor member specifies a scale factor to apply to the recommended WebGPU texture resolution. A value of 1.0 uses the recommended resolution; values less than 1.0 reduce quality for improved performance; values greater than 1.0 increase quality at the cost of performance. The value is clamped to the range [0.2, max(nativeProjectionScaleFactor, 1.0)].
5.4. XRGPULayerInit
The XRGPULayerInit dictionary is the base dictionary for configuring non-projection composition layers. Non-projection layers require the "layers" feature descriptor to be enabled for the session.
dictionary {XRGPULayerInit required GPUTextureFormat colorFormat ;GPUTextureFormat ?depthStencilFormat ;GPUTextureUsageFlags textureUsage = 0x10; // GPUTextureUsage.RENDER_ATTACHMENTrequired XRSpace space ;unsigned long mipLevels = 1;required unsigned long viewPixelWidth ;required unsigned long viewPixelHeight ;XRLayerLayout layout = "mono";boolean isStatic =false ; };
The colorFormat member specifies the GPUTextureFormat for the layer’s color textures. This MUST be a supported color format.
The depthStencilFormat member, when present, specifies the GPUTextureFormat for the layer’s depth/stencil textures. This MUST be a supported depth-stencil format. When not present, no depth/stencil texture is allocated.
The textureUsage member specifies the GPUTextureUsageFlags for the allocated textures. The default value is GPUTextureUsage.RENDER_ATTACHMENT.
The space member specifies the XRSpace in which the layer is positioned.
The mipLevels member specifies the desired number of mip levels for the layer’s textures. The actual number is returned by mipLevels and can be lower than the requested value.
The viewPixelWidth member specifies the width, in pixels, of each view’s texture.
The viewPixelHeight member specifies the height, in pixels, of each view’s texture.
The layout member specifies the XRLayerLayout of the layer. "default" is not valid for non-projection layers created by an XRGPUBinding.
The isStatic member, when set to true, indicates that the author will only draw to the layer when needsRedraw is true. This allows the user agent to optimize for this scenario.
5.5. XRGPUQuadLayerInit
dictionary :XRGPUQuadLayerInit XRGPULayerInit {XRRigidTransform ?transform ;float width = 1.0;float height = 1.0; };
The transform member specifies the initial position and orientation of the quad layer relative to the space.
The width member specifies the width of the quad in meters.
The height member specifies the height of the quad in meters.
5.6. XRGPUCylinderLayerInit
dictionary :XRGPUCylinderLayerInit XRGPULayerInit {XRRigidTransform ?transform ;float radius = 2.0;float centralAngle = 0.78539;float aspectRatio = 2.0; };
The transform member specifies the initial position and orientation of the cylinder layer.
The radius member specifies the radius of the cylinder in meters.
The centralAngle member specifies the central angle of the cylinder in radians. The default value of 0.78539 corresponds to approximately 45 degrees.
The aspectRatio member specifies the aspect ratio (width / height) of the visible portion of the cylinder.
5.7. XRGPUEquirectLayerInit
dictionary :XRGPUEquirectLayerInit XRGPULayerInit {XRRigidTransform ?transform ;float radius = 0;float centralHorizontalAngle = 6.28318;float upperVerticalAngle = 1.570795;float lowerVerticalAngle = -1.570795; };
The transform member specifies the initial position and orientation of the equirect layer.
The radius member specifies the radius of the sphere in meters. A value of 0 indicates an infinite sphere (the equirect is rendered as a skybox).
The centralHorizontalAngle member specifies the horizontal angular extent of the sphere in radians. The default value of 6.28318 corresponds to a full 360 degrees.
The upperVerticalAngle member specifies the upper vertical angle of the visible portion in radians, measured from the horizon.
The lowerVerticalAngle member specifies the lower vertical angle of the visible portion in radians, measured from the horizon.
5.8. XRGPUCubeLayerInit
dictionary :XRGPUCubeLayerInit XRGPULayerInit {DOMPointReadOnly ?orientation ; };
The orientation member specifies the initial orientation of the cube layer as a quaternion.
For cube layers, viewPixelWidth and viewPixelHeight specify the
dimensions of each cube face and MUST be equal.
5.9. XRGPUBinding
The XRGPUBinding interface is the entry point for using WebGPU with a WebGPU-compatible session. It provides methods for creating WebGPU-backed XRCompositionLayers and obtaining XRGPUSubImages for rendering.
[Exposed =(Window ),SecureContext ]interface {XRGPUBinding constructor (XRSession ,session GPUDevice );device readonly attribute double nativeProjectionScaleFactor ;readonly attribute boolean usesDepthValues ;XRProjectionLayer createProjectionLayer (optional XRGPUProjectionLayerInit = {});init XRQuadLayer createQuadLayer (optional XRGPUQuadLayerInit = {});init XRCylinderLayer createCylinderLayer (optional XRGPUCylinderLayerInit = {});init XREquirectLayer createEquirectLayer (optional XRGPUEquirectLayerInit = {});init XRCubeLayer createCubeLayer (optional XRGPUCubeLayerInit = {});init XRGPUSubImage getSubImage (XRCompositionLayer ,layer XRFrame ,frame optional XREye = "none");eye XRGPUSubImage getViewSubImage (XRProjectionLayer ,layer XRView );view GPUTextureFormat getPreferredColorFormat (); };
Each XRGPUBinding has an associated session which is the XRSession it was created with, and an associated device which is the GPUDevice it was created with.
NOTE: A WebGPU-backed layer can be used with any XRGPUBinding that has the same
session and device as the binding that created the layer.
The XRGPUBinding(session, device) constructor MUST perform the following steps when invoked:
-
If session’s ended value is
true, throw anInvalidStateErrorDOMException. -
If session is NOT a WebGPU-compatible session, throw an
InvalidStateErrorDOMException. -
If device has been destroyed, throw an
InvalidStateErrorDOMException. -
If device was NOT created from an XR-compatible adapter, throw an
InvalidStateErrorDOMException. -
Let binding be a new
XRGPUBinding. -
Set binding’s session to session.
-
Set binding’s device to device.
-
Return binding.
XRGPUBinding:
const adapter= await navigator. gpu. requestAdapter({ xrCompatible: true }); const device= await adapter. requestDevice(); const binding= new XRGPUBinding( session, device);
The nativeProjectionScaleFactor attribute returns the scale factor that, when applied to the recommended WebGPU texture resolution, would result in a 1:1 texel-to-pixel ratio at the center of the user’s view. This value MAY change over the lifetime of the session.
The usesDepthValues attribute, if false, indicates that the XR Compositor MUST NOT make use of values in a depth/stencil texture. When the attribute is true, it indicates that the contents of the depth/stencil texture will be used by the XR Compositor and are expected to be representative of the scene rendered into the layer. If the space-warp feature descriptor is enabled, this attribute MUST return true.
Each XR device has a recommended WebGPU texture resolution, which represents the per-view dimensions that the user agent considers a good balance between rendering quality and performance for that device. The recommended resolution is determined by taking the maximum width and height across all of the session’s views, scaled by a user agent-defined default scale factor.
NOTE: Unlike the recommended WebGL framebuffer resolution defined in the WebXR spec, which concatenates views side-by-side into a single framebuffer, the recommended WebGPU texture resolution describes the size of a single view. When creating projection layers, the user agent allocates a texture array where each layer corresponds to one view, with each layer having the recommended resolution.
Each XR device has a recommended WebGPU depth-stencil texture resolution, which represents the per-view dimensions that the user agent considers sufficient for depth/stencil textures used by the XR Compositor. Unless the space-warp feature descriptor is enabled, this resolution MUST be equal to the recommended WebGPU texture resolution.
If the space-warp feature descriptor is enabled, each XR device MUST have a recommended WebGPU motion vector texture resolution, which represents the per-view dimensions that the user agent considers sufficient for motion vector textures used by space warp. When the space-warp feature descriptor is enabled, the recommended WebGPU depth-stencil texture resolution MUST be equal to the recommended WebGPU motion vector texture resolution.
The nativeProjectionScaleFactor attribute can be used to determine the scale factor needed to achieve the native 1:1 resolution. A scaleFactor of 1.0 in createProjectionLayer() uses the recommended resolution directly.
The getPreferredColorFormat() method returns the GPUTextureFormat that the user agent recommends for the color attachments of layers created with this binding.
When invoked, the user agent MUST return the preferred GPUTextureFormat for the session’s XR device.
NOTE: The preferred color format is typically "rgba8unorm" or "bgra8unorm" depending on the platform. The preferred format for WebXR may differ from the format reported by navigator.gpu.getPreferredCanvasFormat(). Authors SHOULD use this method rather than getPreferredCanvasFormat() to determine the format for their XR projection layers.
To scale an XR GPU texture size recommendedSize by a double scaleFactor for a
GPUDevice device, the user agent MUST run the following steps:
-
Let width be max(1, floor(recommendedSize’s width multiplied by scaleFactor)).
-
Let height be max(1, floor(recommendedSize’s height multiplied by scaleFactor)).
-
Let maxDimension be device’s
limits.maxTextureDimension2D. -
If width or height is greater than maxDimension:
-
Let limitScale be min(maxDimension divided by width, maxDimension divided by height).
-
Set width to max(1, floor(width multiplied by limitScale)).
-
Set height to max(1, floor(height multiplied by limitScale)).
-
-
Return (width, height).
The createProjectionLayer(init) method creates a new
XRProjectionLayer backed by WebGPU textures.
When this method is invoked on an XRGPUBinding binding, the user agent MUST run the following
steps:
-
Let session be binding’s session.
-
Let device be binding’s device.
-
If session has ended, throw an
InvalidStateErrorDOMException. -
If device has been destroyed, throw an
InvalidStateErrorDOMException. -
If init’s
colorFormatis not a supported color format, throw aNotSupportedErrorDOMException. -
If init’s
depthStencilFormatis present and is not a supported depth-stencil format, throw aNotSupportedErrorDOMException. -
Let hasDepthAspect be
trueif init’sdepthStencilFormatis present and is not"stencil8"; otherwisefalse. -
Let useSpaceWarp be
trueif hasDepthAspect istrueand session was created with the space-warp feature descriptor; otherwisefalse. -
Let scaleFactor be init’s
scaleFactor, clamped to the range [0.2, max(binding’snativeProjectionScaleFactor, 1.0)]. -
Let (colorWidth, colorHeight) be the result of scaling an XR GPU texture size of the recommended WebGPU texture resolution by scaleFactor for device.
-
Initialize depthWidth to colorWidth and depthHeight to colorHeight.
-
If useSpaceWarp is
true:-
Let (spaceWarpWidth, spaceWarpHeight) be the result of scaling an XR GPU texture size of the recommended WebGPU depth-stencil texture resolution by scaleFactor for device.
-
Set depthWidth to spaceWarpWidth and depthHeight to spaceWarpHeight.
-
-
Let arrayLayerCount be the number of entries in session’s list of views.
-
Let colorDescriptor be the result of creating an XR GPU texture descriptor with binding, colorWidth, colorHeight, arrayLayerCount,
1, init’scolorFormat, and init’stextureUsage. -
Initialize depthStencilDescriptor to
null. -
If init’s
depthStencilFormatis present, set depthStencilDescriptor to the result of creating an XR GPU texture descriptor with binding, depthWidth, depthHeight, arrayLayerCount,1, init’sdepthStencilFormat, and init’stextureUsage. -
Initialize motionVectorDescriptor to
null. -
If useSpaceWarp is
true:-
Set motionVectorDescriptor to the result of creating an XR GPU texture descriptor with binding, depthWidth, depthHeight, arrayLayerCount,
1,"rgba16float", and init’stextureUsage.
-
-
Let layer be a new
XRProjectionLayerin the relevant realm of binding. -
Run initialize a composition layer on layer with session.
-
Set layer’s WebGPU device to device.
-
Set layer’s isStatic to
false. -
Set layer’s
needsRedrawtotrue. -
Set layer’s
mipLevelsto1. -
Set layer’s
textureWidthto colorWidth. -
Set layer’s
textureHeightto colorHeight. -
Set layer’s
textureArrayLengthto arrayLayerCount. -
If motionVectorDescriptor is not
null, set layer’signoreDepthValuestofalse. -
Otherwise, if hasDepthAspect is
trueand binding’susesDepthValuesistrue, set layer’signoreDepthValuestofalse. -
Otherwise, set layer’s
ignoreDepthValuestotrue. -
If fixed foveation is supported, set layer’s
fixedFoveationto0. Otherwise, set it tonull. -
Set layer’s
deltaPosetonull. -
Set layer’s color texture descriptor to colorDescriptor.
-
Set layer’s depth-stencil texture descriptor to depthStencilDescriptor.
-
Set layer’s motion vector texture descriptor to motionVectorDescriptor.
-
Set layer’s list of texture sets to the result of allocating WebGPU layer texture sets with binding, colorDescriptor, depthStencilDescriptor, and motionVectorDescriptor.
-
Set layer’s current color texture, current depth-stencil texture, current motion vector texture, current texture set, and current texture frame to
null. -
Return layer.
const layer= binding. createProjectionLayer({ colorFormat: binding. getPreferredColorFormat(), depthStencilFormat: 'depth24plus' , }); session. updateRenderState({ layers: [ layer] });
To validate WebGPU layer creation with an XRGPUBinding binding, the user agent MUST
run the following steps:
-
If binding’s session has ended, throw an
InvalidStateErrorDOMException. -
If binding’s device has been destroyed, throw an
InvalidStateErrorDOMException. -
If the layers feature descriptor is not enabled for binding’s session, throw a
NotSupportedErrorDOMException.
To determine a WebGPU layer texture layout from an XRGPULayerInit init, the user
agent MUST run the following steps:
-
Let width be init’s
viewPixelWidth. -
Let height be init’s
viewPixelHeight. -
Let arrayLayerCount be
1. -
Switch on init’s
layout:"mono"- Do nothing.
"stereo"- Set arrayLayerCount to
2. "stereo-left-right"- Set width to width multiplied by
2. "stereo-top-bottom"- Set height to height multiplied by
2. "default"- Throw a
TypeError.
-
Return (width, height, arrayLayerCount).
To initialize a WebGPU-backed layer layer with an XRGPUBinding binding, an
XRGPULayerInit init, non-negative integers width and height, and a positive integer
arrayLayerCount, the user agent MUST run the following steps:
-
Let session be binding’s session.
-
If width or height is
0, throw aTypeError. -
If init’s
colorFormatis not a supported color format, throw aNotSupportedErrorDOMException. -
If init’s
depthStencilFormatis present and is not a supported depth-stencil format, throw aNotSupportedErrorDOMException. -
If init’s
mipLevelsis less than1, throw anInvalidStateErrorDOMException. -
Let maximumMipLevelCount be floor(log2(max(width, height))) + 1.
-
Let mipLevelCount be min(init’s
mipLevels, maximumMipLevelCount). The user agent MAY reduce mipLevelCount further if it cannot support the requested number of mip levels, but it MUST NOT reduce it below1. -
Let colorDescriptor be the result of creating an XR GPU texture descriptor with binding, width, height, arrayLayerCount, mipLevelCount, init’s
colorFormat, and init’stextureUsage. -
Initialize depthStencilDescriptor to
null. -
If init’s
depthStencilFormatis present, set depthStencilDescriptor to the result of creating an XR GPU texture descriptor with binding, width, height, arrayLayerCount, mipLevelCount, init’sdepthStencilFormat, and init’stextureUsage. -
Run initialize a composition layer on layer with session.
-
Run setting the space on a layer with init’s
spaceand layer. -
Set layer’s WebGPU device to binding’s device.
-
Set layer’s
mipLevelsto mipLevelCount. -
Set layer’s
needsRedrawtotrue. -
Set layer’s color texture descriptor to colorDescriptor.
-
Set layer’s depth-stencil texture descriptor to depthStencilDescriptor.
-
Set layer’s list of texture sets to the result of allocating WebGPU layer texture sets with binding, colorDescriptor, depthStencilDescriptor, and
null. -
Set layer’s current color texture, current depth-stencil texture, current texture set, and current texture frame to
null.
The createQuadLayer(init) method creates a new
XRQuadLayer backed by WebGPU textures.
When this method is invoked on an XRGPUBinding binding, the user agent MUST run the following
steps:
-
Run validate WebGPU layer creation with binding.
-
Let (width, height, arrayLayerCount) be the result of determining a WebGPU layer texture layout from init.
-
Let layer be a new
XRQuadLayerin the relevant realm of binding. -
Run initialize a WebGPU-backed layer with layer, binding, init, width, height, and arrayLayerCount.
-
If init’s
transformis present, set layer’stransformto a newXRRigidTransformin layer’s relevant realm, initialized with the position and orientation of init’stransform. Otherwise, set it to a new identityXRRigidTransformin layer’s relevant realm. -
Return layer.
The createCylinderLayer(init) method creates a new
XRCylinderLayer backed by WebGPU textures.
When this method is invoked on an XRGPUBinding binding, the user agent MUST run the following
steps:
-
Run validate WebGPU layer creation with binding.
-
Let (width, height, arrayLayerCount) be the result of determining a WebGPU layer texture layout from init.
-
Let layer be a new
XRCylinderLayerin the relevant realm of binding. -
Run initialize a WebGPU-backed layer with layer, binding, init, width, height, and arrayLayerCount.
-
If init’s
transformis present, set layer’stransformto a newXRRigidTransformin layer’s relevant realm, initialized with the position and orientation of init’stransform. Otherwise, set it to a new identityXRRigidTransformin layer’s relevant realm. -
Set layer’s
centralAngleto init’scentralAngle. -
Set layer’s
aspectRatioto init’saspectRatio. -
Return layer.
NOTE: The WebXR Layers space
requirements allow XRQuadLayer and XRCylinderLayer to use any XRSpace, including
"viewer" for head-locked content. XREquirectLayer and XRCubeLayer
are restricted to XRReferenceSpace values other than "viewer" so that
environment content, such as 360-degree media and skyboxes, preserves rotational reprojection.
The createEquirectLayer(init) method creates a new
XREquirectLayer backed by WebGPU textures.
When this method is invoked on an XRGPUBinding binding, the user agent MUST run the following
steps:
-
Run validate WebGPU layer creation with binding.
-
If init’s
spaceis not anXRReferenceSpace, throw aTypeError. -
Let (width, height, arrayLayerCount) be the result of determining a WebGPU layer texture layout from init.
-
Let layer be a new
XREquirectLayerin the relevant realm of binding. -
Run initialize a WebGPU-backed layer with layer, binding, init, width, height, and arrayLayerCount.
-
If init’s
transformis present, set layer’stransformto a newXRRigidTransformin layer’s relevant realm, initialized with the position and orientation of init’stransform. Otherwise, set it to a new identityXRRigidTransformin layer’s relevant realm. -
Set layer’s
centralHorizontalAngleto init’scentralHorizontalAngle. -
Set layer’s
upperVerticalAngleto init’supperVerticalAngle. -
Set layer’s
lowerVerticalAngleto init’slowerVerticalAngle. -
Return layer.
The createCubeLayer(init) method creates a new
XRCubeLayer backed by WebGPU textures.
When this method is invoked on an XRGPUBinding binding, the user agent MUST run the following
steps:
-
Run validate WebGPU layer creation with binding.
-
If init’s
spaceis not anXRReferenceSpace, throw aTypeError. -
If init’s
layoutis not"mono"or"stereo", throw aTypeError. -
If init’s
viewPixelWidthis not equal to init’sviewPixelHeight, throw aTypeError. -
Let arrayLayerCount be
6if init’slayoutis"mono", and12otherwise. -
Let layer be a new
XRCubeLayerin the relevant realm of binding. -
Run initialize a WebGPU-backed layer with layer, binding, init, init’s
viewPixelWidth, init’sviewPixelHeight, and arrayLayerCount. -
If init’s
orientationis present, set layer’sorientationto the result of runningfromPoint()with init’sorientation. Otherwise, set it to the result of runningfromPoint()with{ x: 0, y: 0, z: 0, w: 1 }. -
Return layer.
Cube layer textures use six consecutive array layers for each eye, in the order +X, -X, +Y,
-Y, +Z, -Z. For a stereo cube layer, the left eye’s faces begin at array layer 0 and the
right eye’s faces begin at array layer 6.
To validate WebGPU subimage creation with an XRGPUBinding binding, an
XRCompositionLayer layer, and an XRFrame frame, the user agent MUST run the following
steps:
-
If layer is not a WebGPU-backed layer, throw an
InvalidStateErrorDOMException. -
Let session be binding’s session.
-
If layer’s session is not session, throw an
InvalidStateErrorDOMException. -
If layer’s WebGPU device is not binding’s device, throw an
InvalidStateErrorDOMException. -
If binding’s device has been destroyed, throw an
InvalidStateErrorDOMException. -
If frame’s session is not session, throw an
InvalidStateErrorDOMException. -
If frame is not an active XR animation frame, throw an
InvalidStateErrorDOMException. -
If layer’s list of texture sets is empty, throw an
InvalidStateErrorDOMException. -
If layer is not contained in session’s
renderState.layers, throw aTypeError. -
If layer’s isStatic is
trueand itsneedsRedrawisfalse, throw anInvalidStateErrorDOMException.
The getSubImage(layer, frame, eye) method returns an XRGPUSubImage for non-projection layers.
When invoked, the user agent MUST run the following steps:
-
If the
"layers"feature descriptor is not enabled for the session, throw aNotSupportedErrorDOMException. -
Run validate WebGPU subimage creation with this
XRGPUBinding, layer, and frame. -
If layer is an
XRProjectionLayer, throw aTypeError. -
Let eyeIndex be
1if eye is"right", and0otherwise. -
If layer’s
layoutis not"mono"and eye is"none", throw aTypeError. -
Run acquire WebGPU textures for layer and frame.
-
Let subImage be a new
XRGPUSubImage. -
Set subImage’s
colorTextureto the layer’s current color texture. -
Set subImage’s
depthStencilTextureto the layer’s current depth-stencil texture, ornullif no depth/stencil format was specified during layer creation. -
Set subImage’s
motionVectorTexturetonull. -
Set subImage’s
viewportto the full width and height of subImage’scolorTexture, with an x and y offset of0. -
If layer’s
layoutis"stereo-left-right", divide subImage’sviewportwidth by2and set its x offset to that width multiplied by eyeIndex. -
If layer’s
layoutis"stereo-top-bottom", divide subImage’sviewportheight by2and set its y offset to that height multiplied by eyeIndex. -
Set subImage’s array layer index as follows:
- If layer is an
XRCubeLayerand itslayoutis"stereo" - Set it to eyeIndex multiplied by
6. - If layer’s
layoutis"stereo" - Set it to eyeIndex.
- Otherwise
- Set it to
0.
- If layer is an
-
Return subImage.
The getViewSubImage(layer, view) method returns an XRGPUSubImage for a specific view of a projection layer.
When invoked, the user agent MUST run the following steps:
-
If view’s session is not the session, throw an
InvalidStateErrorDOMException. -
Let frame be view’s frame.
-
Run validate WebGPU subimage creation with this
XRGPUBinding, layer, and frame. -
If view’s active flag is
false, throw anInvalidStateErrorDOMException. -
Run acquire WebGPU textures for layer and frame.
-
Let subImage be a new
XRGPUSubImage. -
Set subImage’s
colorTextureto the layer’s current color texture. -
Set subImage’s
depthStencilTextureto the layer’s current depth-stencil texture, ornullif no depth/stencil format was specified during layer creation. -
Set subImage’s
motionVectorTextureto the layer’s current motion vector texture, ornullif the layer’s motion vector texture descriptor isnull. -
Set subImage’s array layer index to the view’s index.
-
Set subImage’s viewport to the region of the
colorTexturecorresponding to view, adjusted by the current viewport scale. -
Return subImage.
function onXRFrame( time, frame) { session. requestAnimationFrame( onXRFrame); const pose= frame. getViewerPose( refSpace); if ( ! pose) return ; const commandEncoder= device. createCommandEncoder(); for ( const viewof pose. views) { const subImage= binding. getViewSubImage( layer, view); const viewDesc= subImage. getViewDescriptor(); const passEncoder= commandEncoder. beginRenderPass({ colorAttachments: [{ view: subImage. colorTexture. createView( viewDesc), loadOp: 'clear' , storeOp: 'store' , clearValue: { r: 0 , g: 0 , b: 0 , a: 1 }, }], depthStencilAttachment: { view: subImage. depthStencilTexture. createView( viewDesc), depthLoadOp: 'clear' , depthClearValue: 1.0 , depthStoreOp: 'store' , }, }); const vp= subImage. viewport; passEncoder. setViewport( vp. x, vp. y, vp. width, vp. height, 0.0 , 1.0 ); // Render scene from the viewpoint of view... passEncoder. end(); } device. queue. submit([ commandEncoder. finish()]); }
6. Space Warp
Space warp is a technology that improves the XR Compositor’s reprojection.
By submitting a motionVectorTexture along with a depthStencilTexture, the XR Compositor can do high quality frame extrapolation and reprojection which allows the user agent to run at a reduced framerate but still provide a smooth experience to users. The rate at which requestAnimationFrame() callbacks are delivered may be lower than the display’s native refresh rate. The XR Compositor will synthesize the missing frames using the motion vectors and depth information provided by the experience.
To enable space warp, the XRSession MUST be created with the space-warp feature descriptor.
If the space-warp feature descriptor is enabled, usesDepthValues MUST be set to true.
For an XRProjectionLayer whose motion vector texture descriptor is not null, the XR Compositor MUST make use of depth values and ignoreDepthValues MUST be set to false.
An XRProjectionLayer participates in space warp only when its motion vector texture descriptor is not null. Such a descriptor is allocated only when the space-warp feature descriptor is enabled and the layer was created with an depthStencilFormat that is present and is not "stencil8". For each XRGPUSubImage obtained from such a layer, the experience SHOULD render representative depth values into the depthStencilTexture.
When the motionVectorTexture is not null, the depthStencilTexture MUST also not be null, and the two textures MUST have the same dimensions, which MAY be different from the dimensions of the colorTexture.
For such a subimage, authors SHOULD render depth/stencil and motion vector information to the full extent of the texture view selected by getViewDescriptor(), rather than to the viewport.
NOTE: Since the depth/stencil and motion vector attachments can have different dimensions than the color attachment, they are not intended to be attached to the render pass used to render into the colorTexture.
Authors SHOULD render depth/stencil and motion vector information separately at their attachment dimensions.
When the motionVectorTexture is not null, it MUST be in "rgba16float" format. For such a texture, the author SHOULD fill in the RG components with the 2D screen-space motion vector for that area, expressed as a delta in Normalized Device Coordinates between the current frame and the previous frame. The motion vector is computed as (currentClipPos / currentW) - (prevClipPos / prevW), where currentClipPos and prevClipPos are the clip space positions of the fragment in the current and previous frames, respectively. The red channel corresponds to the delta in X, and the green channel corresponds to the delta in Y. The blue channel MAY contain the delta in Z depth, and the alpha channel is unused.
If the motionVectorTexture or depthStencilTexture were not submitted during the processing of the XRFrame, the XR Compositor MUST process the XRFrame as if space warp was not enabled.
7. Security and Privacy Considerations
This specification does not introduce any new security or privacy considerations beyond those described in the WebXR Device API, WebXR Layers API, and WebGPU specifications.
The textures provided by XRGPUSubImage are allocated by the user agent and do not expose any additional information about the user’s environment beyond what the underlying XR session already provides. The user agent MUST ensure that textures returned by the binding do not contain data from previous frames or other origins.
The xrCompatible flag does not expose any new fingerprinting surface beyond what is already available through the requestAdapter() API, as the returned adapter capabilities are the same regardless of whether XR compatibility is requested.
8. Conformance
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 when, and only when, they appear in all capitals, as shown here.