XR_EXT_spatial_container_self_rendering

Name String

XR_EXT_spatial_container_self_rendering

Extension Type

Instance extension

Registered Extension Number

814

Revision

1

Ratification Status

Not ratified

Extension and Version Dependencies

XR_EXT_spatial_container

Last Modified Date

2026-08-06

IP Status

No known IP claims.

Contributors

Lachlan Ford, Google
Andreas Loeve Selvik, Meta Platforms
Adam Gousetis, Meta Platforms
Bjorn Swenson, LÖVR
Cass Everitt, Meta Platforms
Christian Kruse, Meta Platforms
Denny Rönngren, Varjo Technologies
Fredia Huya-Kouadio, Godot Engine
Jakob Bornecrantz, NVIDIA
Jared Finder, Google
Jonathan Wright, Meta Platforms
Jian Zhang, ByteDance
Kurtis Eveleigh, Qualcomm
Mauricio Narvaez, Meta Platforms
Nick Klingensmith, Qualcomm
Nikita Lutsenko, Meta Platforms
Praveen Babu J D, ByteDance
Rylie Pavlik, Collabora
Salar Khan, Google
Shuai Liu, ByteDance
Walter Luh, Meta Platforms
Yin Li, Microsoft

Overview

This extension defines "self rendering" for spatial containers. A self rendered spatial container’s pixels are all generated by an app and submitted to the runtime using standard OpenXR swapchains and composition layers. Because spatial containers do not always take up all of a screen’s pixels, the runtime informs apps of each spatial container’s recommended resolution and view configuration to enable apps to render more efficiently.

This extension combined with XR_EXT_spatial_container is intended to offer a superset of the capabilities of conventional OpenXR 1.x core composition layer rendering. OpenXR 1.x runtimes that support graphical rendering and this extension must set XrSystemSpatialContainerPropertiesEXT :: supportsImmersive to true to indicate they support immersive spatial containers.

Spatial Container Rendering Lifecycle

To use self rendering for a spatial container, an app sets the XrSpatialContainerCreateInfoEXT :: graphicsPresentation to XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT when calling xrCreateSpatialContainerEXT . Then that app can manage whether a spatial container is actively rendering by calling the xrBeginSpatialContainerRenderingEXT and xrEndSpatialContainerRenderingEXT functions. These functions replace xrBeginSession and xrEndSession . They are not per-frame operations, such as xrBeginFrame and xrEndFrame .

Every frame, an app goes through the following steps:

  • Call xrWaitFrame and xrBeginFrame . These functions have no container-specific structures and function as they do in the core spec.
  • Retrieve view poses, projection parameters, and rendering recommendations for all spatial containers actively rendering with xrLocateSpatialContainerViewsEXT . For each spatial container, the runtime reports whether its composition layers should be rendered and the recommended way to render the layers by filling out XrSpatialContainerViewStateEXT .
  • Render pixels into composition layers normally. These operations have no container-specific structures.
  • Submit composition layers for all spatial containers actively rendering by calling xrEndFrame and chaining XrSpatialContainerLayerFrameEndInfoEXT .

Destroying an XrSpatialContainerEXT handle implicitly removes it from the active render set. An app may destroy a spatial container even if it made a successful call to xrBeginSpatialContainerRenderingEXT not associated with a corresponding call to xrEndSpatialContainerRenderingEXT . The runtime must behave as if xrEndSpatialContainerRenderingEXT is implicitly called if needed early in the implementation of xrDestroySpatialContainerEXT if it returns successfully.

When an app receives an XrEventDataSpatialContainerClosedEXT event for a container in the active render set, that app should call xrEndSpatialContainerRenderingEXT and destroy the XrSpatialContainerEXT handle.

The xrBeginSpatialContainerRenderingEXT function adds the spatial container to the per-session active render set, allowing an app to submit composition layers for the spatial container to xrEndFrame .

XrResult xrBeginSpatialContainerRenderingEXT(
    XrSession                                   session,
    const XrSpatialContainerBeginInfoEXT*       beginInfo);

Parameter Descriptions

Apps typically call this in response to a visible-changed event with XrEventDataSpatialContainerVisibleChangedEXT :: visible set to true.

If the spatial container was not created with XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT when calling xrCreateSpatialContainerEXT , the runtime must return XR_ERROR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_MISSING_EXT .

If the spatial container is already in the active render set, the runtime must return XR_ERROR_SPATIAL_CONTAINER_IS_RENDERING_EXT .

If xrBeginSpatialContainerRenderingEXT is called at a time when the app could not call xrWaitFrame (i.e. inside the xrBeginFrame and xrEndFrame window), the runtime must return XR_ERROR_CALL_ORDER_INVALID .

Valid Usage (Implicit)

Thread Safety

Return Codes

Success

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

Failure

  • XR_ERROR_CALL_ORDER_INVALID
  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_MISSING_EXT
  • XR_ERROR_SPATIAL_CONTAINER_IS_RENDERING_EXT
  • XR_ERROR_VALIDATION_FAILURE
  • XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED

The XrSpatialContainerBeginInfoEXT structure is used to specify the parameters for beginning rendering of a spatial container.

typedef struct XrSpatialContainerBeginInfoEXT {
    XrStructureType            type;
    const void*                next;
    XrSpatialContainerEXT      spatialContainer;
    XrViewConfigurationType    primaryViewConfigurationType;
} XrSpatialContainerBeginInfoEXT;

Member Description

primaryViewConfigurationType is the view configuration that the app intends to use when rendering. The runtime may decay the view configuration to a type with fewer views for optimization purposes (e.g. a container is very far away and small such that stereo is imperceptible). The runtime returns details about the decayed view configuration from calls to xrLocateSpatialContainerViewsEXT .

Valid Usage (Implicit)

The xrEndSpatialContainerRenderingEXT function removes the spatial container from the active render set.

XrResult xrEndSpatialContainerRenderingEXT(
    XrSession                                   session,
    const XrSpatialContainerEndInfoEXT*         endInfo);

Parameter Descriptions

Apps typically call this in response to a visible-changed event with XrEventDataSpatialContainerVisibleChangedEXT :: visible set to false.

If the spatial container is not in the active render set, the runtime must return XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT .

If xrEndSpatialContainerRenderingEXT is called at a time when the app could not call xrWaitFrame (i.e. during the period between xrBeginFrame and xrEndFrame calls), the runtime must return XR_ERROR_CALL_ORDER_INVALID .

Valid Usage (Implicit)

Thread Safety

Return Codes

Success

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

Failure

  • XR_ERROR_CALL_ORDER_INVALID
  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT
  • XR_ERROR_VALIDATION_FAILURE

The XrSpatialContainerEndInfoEXT structure is used to specify the parameters for ending rendering of a spatial container.

typedef struct XrSpatialContainerEndInfoEXT {
    XrStructureType          type;
    const void*              next;
    XrSpatialContainerEXT    spatialContainer;
} XrSpatialContainerEndInfoEXT;

Member Description

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain.
  • spatialContainer is the XrSpatialContainerEXT to end rendering.

Valid Usage (Implicit)

Locating views via xrLocateSpatialContainerViewsEXT

The xrLocateSpatialContainerViewsEXT function locates views for a set of spatial containers.

XrResult xrLocateSpatialContainerViewsEXT(
    XrSession                                   session,
    const XrSpatialContainerViewsLocateInfoEXT* locateInfo,
    uint32_t                                    viewStateCount,
    XrSpatialContainerViewStateEXT*             viewStates,
    uint32_t                                    viewCount,
    XrView*                                     views);

Parameter Descriptions

This function replaces xrLocateViews , allowing an app to query views for multiple spatial containers simultaneously.

Views for all spatial containers are returned in the single views array. The views are ordered based on the index of the spatial container in the XrSpatialContainerViewsLocateInfoEXT :: viewLocateInfos array, then the view index for that container.

For example, if an app provides three spatial containers in XrSpatialContainerViewsLocateInfoEXT :: viewLocateInfos :

  • Index 0: XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO (1 view)
  • Index 1: XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO (2 views)
  • Index 2: XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET (4 views)

Then the runtime must fill out the views array with 7 views:

  • views [0] is the view for container 0.
  • views [1] is the left eye view for container 1.
  • views [2] is the right eye view for container 1.
  • views [3] is the left eye view for container 2.
  • views [4] is the right eye view for container 2.
  • views [5] is the left eye inset view for container 2.
  • views [6] is the right eye inset view for container 2.

If viewCount is not equal to the sum of the view counts for each spatial container’s requested view configuration type, the runtime must return XR_ERROR_VALIDATION_FAILURE .

The runtime may decay the view configuration to a type with fewer views for optimization purposes (e.g. a container is very far away and small such that stereo is imperceptible). If the runtime decays the view configuration, the runtime must still return the original number of views for that container, populating the extra views with duplicates of the decayed views. For example, if XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO is decayed to XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO , both index 0 and index 1 will contain the single mono view. Consequently, the total number of views returned in the views array is independent of if the runtime decided to decay any view configurations or not.

The runtime should adapt each XrView :: fov returned such that the frustum is as small as possible while still containing the entire bounds of the corresponding spatial container. Runtimes may return non-symmetrical FOVs, for example if a spatial container is at an oblique angle to the user’s view, or is partially occluded by another object. Runtimes may freely position the target rendering surface for the spatial container. Due to the nature of multi-app rendering, where spatial containers are detached from the user, the runtime may return XrView values not suitable for pixel rendering, for example if the spatial container is not visible on screen there are no valid values to set XrView :: fov to. In such cases, runtimes must set XrSpatialContainerViewStateEXT :: shouldSubmitLayers to false.

The runtime returns additional per-spatial container rendering state information in the viewStates array. If viewStateCount is not equal to XrSpatialContainerViewsLocateInfoEXT :: viewLocateInfoCount , the runtime must return XR_ERROR_VALIDATION_FAILURE .

After the runtime queues an XrEventDataSpatialContainerClosedEXT event for a spatial container, this function must return XR_FALSE in XrSpatialContainerViewStateEXT :: shouldSubmitLayers and 0 in XrSpatialContainerViewStateEXT :: viewStateFlags for the corresponding spatial container.

For spatial containers with actively changing locations in the real world, the runtime should take the container’s motion into account when filling out views to minimize reprojection artifacts.

If xrLocateSpatialContainerViewsEXT is called for one or more spatial containers not currently in the active render set, the runtime must return XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT .

Valid Usage (Implicit)

Return Codes

Success

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

Failure

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT
  • XR_ERROR_TIME_INVALID
  • XR_ERROR_VALIDATION_FAILURE
  • XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED

The XrSpatialContainerViewsLocateInfoEXT structure contains the parameters for locating views across a collection of spatial containers.

typedef struct XrSpatialContainerViewsLocateInfoEXT {
    XrStructureType                               type;
    const void*                                   next;
    XrTime                                        displayTime;
    uint32_t                                      viewLocateInfoCount;
    const XrSpatialContainerViewLocateInfoEXT*    viewLocateInfos;
} XrSpatialContainerViewsLocateInfoEXT;

Member Description

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain.
  • displayTime is the XrTime at which to locate the views.
  • viewLocateInfoCount is the number of entries in the viewLocateInfos array.
  • viewLocateInfos is a pointer to an array of XrSpatialContainerViewLocateInfoEXT structures.

Valid Usage (Implicit)

The XrSpatialContainerViewLocateInfoEXT structure contains the parameters for an individual spatial container to locate views for.

typedef struct XrSpatialContainerViewLocateInfoEXT {
    XrStructureType            type;
    const void*                next;
    XrViewConfigurationType    viewConfigurationType;
    XrSpace                    space;
    XrSpatialContainerEXT      spatialContainer;
} XrSpatialContainerViewLocateInfoEXT;

Member Description

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain.
  • viewConfigurationType is an XrViewConfigurationType to be used for the spatial container.
  • space is the XrSpace in which the spatial container’s views are located.
  • spatialContainer is the XrSpatialContainerEXT handle.

The app can provide any valid XrSpace in space , however it is recommended that it provide the spatial container space for the corresponding XrSpatialContainerEXT specified within the XrSpatialContainerViewLocateInfoEXT structure.

Valid Usage (Implicit)

The XrSpatialContainerViewStateEXT structure is populated by calls to xrLocateSpatialContainerViewsEXT with per-spatial container rendering suitability information.

typedef struct XrSpatialContainerViewStateEXT {
    XrStructureType            type;
    void*                      next;
    XrViewStateFlags           viewStateFlags;
    XrViewConfigurationType    viewConfigurationType;
    XrBool32                   shouldSubmitLayers;
    XrExtent2Di                recommendedImageExtent;
} XrSpatialContainerViewStateEXT;

Member Description

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain.
  • viewStateFlags is a bitmask of XrViewStateFlags . These flags have the same purpose as the viewStateFlags in the core XrViewState structure.
  • viewConfigurationType is an XrViewConfigurationType which indicates the view configuration type the runtime is using for the spatial container, which runtimes may decay to a view configuration with a lower number of views than the one requested.
  • shouldSubmitLayers is an XrBool32 returned by the runtime. If XR_FALSE , the app should avoid submitting composition for this spatial container as it is currently not visible or is in a state where rendering is not required.
  • recommendedImageExtent is an XrExtent2Di structure representing the recommended resolution for the spatial container.

As with core OpenXR view location, the app must check the XrSpatialContainerViewStateEXT :: viewStateFlags to ensure the position and orientation are valid and tracked before using the view data for rendering.

The app should check shouldSubmitLayers and viewConfigurationType to avoid rendering more than the runtime recommends. When shouldSubmitLayers is false, the app changes its rendering behavior as follows:

  • The app must not read the corresponding XrView :: fov .
  • The app must not read recommendedImageExtent .
  • The app should avoid rendering to swapchains and submitting composition layers for the corresponding spatial container.
  • The app should set XrSpatialContainerLayerEXT :: retainPreviousSubmission to true. If the app chooses to submit layers anyway, it is up to the runtime to determine what it wants to do with the layers (e.g. the runtime may choose to drop them).

recommendedImageExtent is explicitly for layers that will be updated based on the container position or user’s view. When updating layers with content that depends on the spatial container’s position or user’s view (such as normal 3D content), the app should render to this smaller viewport instead of the default recommended view size. When updating layers with content that does not depend on the spatial container’s position or user’s view (such as static quad layers), the app may safely ignore these values. For view dependent layers, recommendedImageExtent is a recommendation to the app from the runtime intended to minimize aliasing and improve rendering performance. The app is free to submit at full resolution if it wishes.

The XrExtent2Di :: width and XrExtent2Di :: height of recommendedImageExtent must always be a positive integer less than or equal to the XrViewConfigurationView :: recommendedImageRectWidth and XrViewConfigurationView :: recommendedImageRectHeight of the XrViewConfigurationView for the spatial container’s primary view configuration. An app should not recreate a swapchain solely because recommendedImageExtent changed, as it is a dynamic hint to the app specifying the XrSwapchainSubImage :: imageRect to use in the XrSwapchainSubImage of any full resolution composition layers it submits, and can change every frame.

Valid Usage (Implicit)

View reference space

The view reference space remains unchanged as it is in OpenXR core. As in core, it is not intended for rendering use.

Submitting layers via xrEndFrame

In order to render spatial containers, the app must supply a list of layers associated with their respective spatial containers to xrEndFrame . This is accomplished via the XrSpatialContainerLayerFrameEndInfoEXT structure.

typedef struct XrSpatialContainerLayerFrameEndInfoEXT {
    XrStructureType                      type;
    const void*                          next;
    uint32_t                             containerLayerCount;
    const XrSpatialContainerLayerEXT*    containerLayers;
} XrSpatialContainerLayerFrameEndInfoEXT;

Member Description

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain.
  • containerLayerCount is the count of elements in the containerLayers array.
  • containerLayers is a pointer to an array of XrSpatialContainerLayerEXT structures.

The containerLayers array contains an XrSpatialContainerLayerEXT for each spatial container in the active render set (the containers managed per-session by xrBeginSpatialContainerRenderingEXT and xrEndSpatialContainerRenderingEXT ). Each XrSpatialContainerLayerEXT contains a list of XrCompositionLayerBaseHeader pointers associated with the given spatial container. Runtimes may composite containers in any order they choose, independent of the order of the containerLayers . For example, runtimes may composite containers based on their depth relative to the user, have a spatial container act as a background layer, or 'pin' a spatial container such that it is always on top regardless of its depth. Runtimes may composite layers using different strategies for spatial containers in bounded or immersive modes. For example, runtimes may precomposite each bounded spatial container’s composition layers into a single stereo quad layer that is combined with all other containers in a second composition step.

XrFrameEndInfo :: displayTime and XrFrameEndInfo :: environmentBlendMode will be applied to all spatial containers supplied in XrSpatialContainerLayerFrameEndInfoEXT .

When this structure is passed to xrEndFrame by being chained onto XrFrameEndInfo , the following additional error cases are added based on the active render set:

  • If a spatial container is referenced by more than one element of containerLayers , runtimes must return XR_ERROR_SPATIAL_CONTAINER_DUPLICATE_EXT .
  • If a spatial container in the active render set is not referenced by an element of containerLayers , runtimes must return XR_ERROR_SPATIAL_CONTAINER_MISSING_EXT .
  • If a spatial container referenced by containerLayers is not a member of the active render set, runtimes must return XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT .

Furthermore, if there are any spatial containers in the active render set and this structure is not chained onto XrFrameEndInfo , runtimes must return XR_ERROR_SPATIAL_CONTAINER_MISSING_EXT .

In any of the above error cases, the runtime must not render any content for any spatial container.

If no spatial containers are in the active render set, an app can call xrEndFrame as long as it is not submitting any composition layers.

Note that individual swapchains may be submitted for multiple containers via layers in a valid structure, e.g. A quad layer rendered via a single swapchain submitted to multiple spatial containers. This case is handled uniformly with other rendering: the runtime must render the quad in all such spatial containers.

Valid Usage (Implicit)

The XrSpatialContainerLayerEXT structure groups standard composition layers and associates them with a specific spatial container.

typedef struct XrSpatialContainerLayerEXT {
    XrStructureType                               type;
    const void*                                   next;
    XrSpatialContainerEXT                         spatialContainer;
    XrBool32                                      retainPreviousSubmission;
    uint32_t                                      layerCount;
    const XrCompositionLayerBaseHeader* const*    layers;
} XrSpatialContainerLayerEXT;

Member Description

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain.
  • spatialContainer is the XrSpatialContainerEXT identifying the target container.
  • retainPreviousSubmission is an XrBool32 . If XR_TRUE , the runtime will retain the last submitted frame for this spatial container.
  • layerCount is the number of composition layers in the layers array.
  • layers is a pointer to an array of XrCompositionLayerBaseHeader pointers.

When retainPreviousSubmission is XR_TRUE , the runtime must reuse the composition layers and swapchain image contents as latched during the most recent xrEndFrame call where retainPreviousSubmission was XR_FALSE . Any subsequent modifications to swapchains used by retained layers must not be reflected in the rendered output.

For layers submitted for an individual spatial container (intra-container rendering), the runtime must follow the already specified rules for layer composition ordering. That is to say the layers within containers must be drawn in the same order as they are specified, composed via painter’s algorithm.

"Clearing" a spatial container refers to the removal of all content from a spatial container, resulting in nothing being rendered for that container in the current frame. Runtimes must treat this equivalent to the app submitting transparent black (all bits 0) for every pixel inside the container. "Retaining" a spatial container refers to the runtime continuing to display the most recently submitted set of layers for that container.

For each spatial container the app can specify whether to submit new layers, retain the previous submission, or clear the spatial container:

  • To submit new layers, the app provides a non-zero layerCount and a valid layers pointer.
  • To clear a spatial container, the layerCount for its entry must be 0 and retainPreviousSubmission must be XR_FALSE .
  • To retain the last submitted layers and swapchain images (retaining), the layerCount for its entry must be 0 and retainPreviousSubmission must be XR_TRUE .

This explicit per-container control ensures that the app’s intent is clearly communicated in every frame.

If retainPreviousSubmission is XR_TRUE and layerCount is not 0, the runtime must return XR_ERROR_VALIDATION_FAILURE .

If XrSpatialContainerCompositionLayerViewConfigurationEXT is not chained to an XrCompositionLayerProjection that is part of a spatial container, the runtime must return XR_ERROR_VALIDATION_FAILURE . This structure is only applicable to XrCompositionLayerProjection layers. If it is chained to other layer types (e.g. quad or cylinder layers), the runtime must return XR_ERROR_VALIDATION_FAILURE .

Valid Usage (Implicit)

Specifying View Configuration for Projection Layers

The XrSpatialContainerCompositionLayerViewConfigurationEXT structure is used to specify the view configuration used to render a projection layer for a spatial container.

typedef struct XrSpatialContainerCompositionLayerViewConfigurationEXT {
    XrStructureType            type;
    const void*                next;
    XrViewConfigurationType    viewConfigurationType;
} XrSpatialContainerCompositionLayerViewConfigurationEXT;

Member Description

When submitting an XrCompositionLayerProjection for a spatial container, the app must specify which view configuration was used to render the layer. This is necessary because the app can choose each frame whether to render with the spatial container’s primary view configuration or the runtime’s suggested decayed view configuration, if any.

When XrSpatialContainerCompositionLayerViewConfigurationEXT is chained, the app must ensure that XrCompositionLayerProjection :: viewCount is a valid view count for the XrViewConfigurationType specified in the chained structure. The viewConfigurationType specified in the chained structure must be the XrSpatialContainerBeginInfoEXT :: primaryViewConfigurationType specified when the container was begun, or the decayed view configuration type returned by the runtime in XrSpatialContainerViewStateEXT , or additional values enabled through an extension.

Valid Usage (Implicit)

Pixel clipping behavior

Apps should inform the runtime if they render pixels outside of a spatial container’s bounds or not by chaining XrSpatialContainerLayerVolumeClippingEXT when submitting composition layers.

typedef struct XrSpatialContainerLayerVolumeClippingEXT {
    XrStructureType                        type;
    const void*                            next;
    XrSpatialContainerVolumeClippingEXT    volumeClipping;
} XrSpatialContainerLayerVolumeClippingEXT;

Member Description

This structure serves as a hint for if the app renders pixels outside the bounds of a spatial container. When a spatial container is in immersive mode, because the bounds are infinite this hint has no meaning.

For bounded spatial containers using self rendering, the runtime needs to reason about both the 3D volume bounds and the 2D pixel bounds. The 3D volume bounds is returned from xrGetSpatialContainerBoundsEXT . The 2D pixel bounds is the projection of the 3D volume bounds onto each display for the system.

Runtimes must discard pixels from swapchain images outside the 2D pixel bounds, however runtimes must not discard pixels that are inside the 2D pixel bounds but outside of the 3D volume bounds. Stereo effects where content appears to extend beyond or in front of the container’s 3D volume bounds are possible. Apps may discard pixels outside of the 3D volume bounds of a bounded spatial container to reduce depth paradoxes and should set volumeClipping based on how they do so. Depth paradoxes often cause eye strain and user discomfort.

Runtimes may use volumeClipping to adjust how they display a particular spatial container. For example, when set to XR_SPATIAL_CONTAINER_VOLUME_CLIPPING_STRICT_EXT a runtime could render rear faces of the 3D volume bounds that would cause depth paradoxes if the spatial container drew pixels behind the container’s 3D volume bounds.

Future extensions may allow for clipping based the 3D volume bounds if depth information is submitted.

Valid Usage (Implicit)

New Commands

New Structures

New Enum Constants

  • XR_EXT_SPATIAL_CONTAINER_SELF_RENDERING_EXTENSION_NAME
  • XR_EXT_spatial_container_self_rendering_SPEC_VERSION
  • Extending XrResult :

    • XR_ERROR_SPATIAL_CONTAINER_DUPLICATE_EXT
    • XR_ERROR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_MISSING_EXT
    • XR_ERROR_SPATIAL_CONTAINER_IS_RENDERING_EXT
    • XR_ERROR_SPATIAL_CONTAINER_MISSING_EXT
    • XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT
  • Extending XrSpatialContainerGraphicsPresentationEXT :

    • XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT
  • Extending XrStructureType :

    • XR_TYPE_SPATIAL_CONTAINER_BEGIN_INFO_EXT
    • XR_TYPE_SPATIAL_CONTAINER_COMPOSITION_LAYER_VIEW_CONFIGURATION_EXT
    • XR_TYPE_SPATIAL_CONTAINER_END_INFO_EXT
    • XR_TYPE_SPATIAL_CONTAINER_LAYER_EXT
    • XR_TYPE_SPATIAL_CONTAINER_LAYER_FRAME_END_INFO_EXT
    • XR_TYPE_SPATIAL_CONTAINER_LAYER_VOLUME_CLIPPING_EXT
    • XR_TYPE_SPATIAL_CONTAINER_VIEWS_LOCATE_INFO_EXT
    • XR_TYPE_SPATIAL_CONTAINER_VIEW_LOCATE_INFO_EXT
    • XR_TYPE_SPATIAL_CONTAINER_VIEW_STATE_EXT

Code Samples

// Example: Complete lifecycle, view location, and frame submission for spatial containers
PFN_xrBeginSpatialContainerRenderingEXT xrBeginSpatialContainerRenderingEXT; // previously initialized
PFN_xrEndSpatialContainerRenderingEXT xrEndSpatialContainerRenderingEXT; // previously initialized
PFN_xrLocateSpatialContainerViewsEXT xrLocateSpatialContainerViewsEXT; // previously initialized

XrSession session; // previously initialized

// We want to update this container
XrSpatialContainerEXT containerA; // previously initialized with XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT
XrSpace containerASpace; // previously initialized

// We want to retain this container
XrSpatialContainerEXT containerB; // previously initialized with XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT
XrSpace containerBSpace; // previously initialized

// 1. Add to active render set when the app is ready to start submitting layers
XrSpatialContainerBeginInfoEXT beginInfoA {
  .type = XR_TYPE_SPATIAL_CONTAINER_BEGIN_INFO_EXT,
  .spatialContainer = containerA,
  .primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
};
CHK_XR(xrBeginSpatialContainerRenderingEXT(session, &beginInfoA));

XrSpatialContainerBeginInfoEXT beginInfoB {
  .type = XR_TYPE_SPATIAL_CONTAINER_BEGIN_INFO_EXT,
  .spatialContainer = containerB,
  .primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
};
CHK_XR(xrBeginSpatialContainerRenderingEXT(session, &beginInfoB));

// ... Frame Loop ...


XrTime displayTime; // returned from xrWaitFrame

const std::vector<XrSpatialContainerEXT> containers = {containerA, containerB};
const std::vector<XrSpace> containerSpaces = {containerASpace, containerBSpace};
uint32_t containerCount = static_cast<uint32_t>(containers.size());

// 2. Setup individual locate info for each container
std::vector<XrSpatialContainerViewLocateInfoEXT> containerLocateInfos(containerCount, {
  .type = XR_TYPE_SPATIAL_CONTAINER_VIEW_LOCATE_INFO_EXT,
});

for (uint32_t i = 0; i < containerCount; ++i) {
    containerLocateInfos[i].viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
    containerLocateInfos[i].space = containerSpaces[i]; // Recommended to use container's own space
    containerLocateInfos[i].spatialContainer = containers[i];
}

// Setup bulk locate info
XrSpatialContainerViewsLocateInfoEXT locateInfo {
  .type = XR_TYPE_SPATIAL_CONTAINER_VIEWS_LOCATE_INFO_EXT,
  .displayTime = displayTime,
  .viewLocateInfoCount = containerCount,
  .viewLocateInfos = containerLocateInfos.data(),
};

// Prepare states to be populated by the runtime
std::vector<XrSpatialContainerViewStateEXT> viewStates(containerCount, {
  .type = XR_TYPE_SPATIAL_CONTAINER_VIEW_STATE_EXT,
});

// Call the dedicated locate views function
// We know the required view count is 2 per container for STEREO
const uint32_t viewsPerConfig = 2;
uint32_t totalViewCount = containerCount * viewsPerConfig;
std::vector<XrView> views(totalViewCount, {.type = XR_TYPE_VIEW});

CHK_XR(xrLocateSpatialContainerViewsEXT(session, &locateInfo, containerCount, viewStates.data(), totalViewCount, views.data()));

// Process results
for (uint32_t i = 0; i < containerCount; ++i) {
    XrBool32 shouldSubmitLayers = viewStates[i].shouldSubmitLayers;
    XrExtent2Di recommendedImageExtent = viewStates[i].recommendedImageExtent;
    XrViewConfigurationType viewConfigurationType = viewStates[i].viewConfigurationType;
    bool isDecayedToMono = (viewConfigurationType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO);
    if (shouldSubmitLayers) {
        // App is expected to also verify viewStates[i].viewStateFlags has valid orientation/position bits before rendering
        XrView& leftView = views[i * viewsPerConfig];
        XrView& rightView = views[i * viewsPerConfig + 1];
        // If isDecayedToMono is true, left and right will be identical.
    } else {
        // Skip layer submission but retain previous submission when calling
        // xrEndFrame.
    }
}

// 3. Setup projection layer for container A
XrCompositionLayerProjection projectionLayer = {.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION};
// ... standard projection layer setup (space, viewCount, views, etc.) ...

// Specify which view configuration was used to render this specific layer
XrSpatialContainerCompositionLayerViewConfigurationEXT containerViewConfig {
  .type = XR_TYPE_SPATIAL_CONTAINER_COMPOSITION_LAYER_VIEW_CONFIGURATION_EXT,
  // Even if the runtime decayed to MONO, we indicate what we actually rendered
  .viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
};

// Chain it to the projection layer
projectionLayer.next = &containerViewConfig;
const XrCompositionLayerBaseHeader* layersA[1] = {reinterpret_cast<const XrCompositionLayerBaseHeader*>(&projectionLayer)};

// Note: In a real app, you are required to submit ALL active containers in the render set.

XrSpatialContainerLayerVolumeClippingEXT containerClippingA = {
  .type = XR_TYPE_SPATIAL_CONTAINER_LAYER_VOLUME_CLIPPING_EXT,
  // Set volumeClipping appropriately for the rendering done.
  .volumeClipping = XR_SPATIAL_CONTAINER_VOLUME_CLIPPING_STRICT_EXT,
};

// Setup container A (Update with new pixels)
XrSpatialContainerLayerEXT containerLayerA {
  .type = XR_TYPE_SPATIAL_CONTAINER_LAYER_EXT,
  .next = &containerClippingA,
  .spatialContainer = containerA,
  .retainPreviousSubmission = XR_FALSE,
  .layerCount = 1,
  .layers = layersA,
};

XrSpatialContainerLayerVolumeClippingEXT containerClippingB {
  .type = XR_TYPE_SPATIAL_CONTAINER_LAYER_VOLUME_CLIPPING_EXT,
  // Set volumeClipping appropriately for the rendering done.
  .volumeClipping = XR_SPATIAL_CONTAINER_VOLUME_CLIPPING_STRICT_EXT,
};

// Setup container B (Explicitly retain previous frame)
XrSpatialContainerLayerEXT containerLayerB {
  .type = XR_TYPE_SPATIAL_CONTAINER_LAYER_EXT,
  .next = &containerClippingB,
  .spatialContainer = containerB,
  .retainPreviousSubmission = XR_TRUE,
  .layerCount = 0, // Required to be 0 when retaining
  .layers = nullptr,
};

// Aggregate all container layers
XrSpatialContainerLayerEXT containerLayers[] = { containerLayerA, containerLayerB };

XrSpatialContainerLayerFrameEndInfoEXT spatialEndInfo {
  .type = XR_TYPE_SPATIAL_CONTAINER_LAYER_FRAME_END_INFO_EXT,
  .containerLayerCount = 2, // Must equal the number of active containers
  .containerLayers = containerLayers,
};

XrFrameEndInfo frameEndInfo {
  .type = XR_TYPE_FRAME_END_INFO,
  .next = &spatialEndInfo,
  .displayTime = displayTime,
  .environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND,
  .layerCount = 0, // MANDATORY: Must be 0 for self rendering extension
};

CHK_XR(xrEndFrame(session, &frameEndInfo));

// ... End Frame Loop ...

// 4. Remove from active render set when the container is destroyed or no longer needs rendering
XrSpatialContainerEndInfoEXT endInfoA {
  .type = XR_TYPE_SPATIAL_CONTAINER_END_INFO_EXT,
  .spatialContainer = containerA,
};
CHK_XR(xrEndSpatialContainerRenderingEXT(session, &endInfoA));

XrSpatialContainerEndInfoEXT endInfoB {
  .type = XR_TYPE_SPATIAL_CONTAINER_END_INFO_EXT,
  .spatialContainer = containerB,
};
CHK_XR(xrEndSpatialContainerRenderingEXT(session, &endInfoB));

Version History

  • Revision 1, 2026-08-06 (Lachlan Ford)

    • Initial extension description