XR_EXT_spatial_container_self_rendering

名称字符串

XR_EXT_spatial_container_self_rendering

扩展程序类型

实例扩展程序

已注册的扩展程序编号

814

修订版本

1

批准状态

未批准

扩展程序和版本依赖项

XR_EXT_spatial_container

上次修改日期

2026-08-06

IP 状态

没有已知的 IP 权利主张。

创作贡献者

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

概览

此扩展程序为空间容器定义了“自渲染”。自渲染空间容器的像素全部由应用生成,并使用标准 OpenXR 交换链和合成层提交到运行时。由于空间容器并不总是占用屏幕的所有像素,因此运行时会告知应用每个空间容器的推荐分辨率和视图配置,以便应用更高效地进行渲染。

此扩展程序与 XR_EXT_spatial_container 结合使用,旨在提供比传统 OpenXR 1.x 核心合成层渲染更强大的功能。支持图形渲染和此扩展程序的 OpenXR 1.x 运行时必须将 XrSystemSpatialContainerPropertiesEXT :: supportsImmersive 设置为 true,以表明它们支持沉浸式空间容器。

空间容器渲染生命周期

如需对空间容器使用自行渲染,应用会在调用 xrCreateSpatialContainerEXT 时将 XrSpatialContainerCreateInfoEXT :: graphicsPresentation 设置为 XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT。然后,该应用可以通过调用 xrBeginSpatialContainerRenderingEXT 和 xrEndSpatialContainerRenderingEXT 函数来管理空间容器是否正在主动渲染。这些函数会替换 xrBeginSession 和 xrEndSession。它们不是逐帧操作,例如 xrBeginFrame 和 xrEndFrame。

在每一帧中,应用都会执行以下步骤:

销毁 XrSpatialContainerEXT 句柄会隐式将其从活跃渲染集中移除。即使应用成功调用了 xrBeginSpatialContainerRenderingEXT,但未与 xrEndSpatialContainerRenderingEXT 的相应调用相关联,应用可以销毁空间容器。如果 xrDestroySpatialContainerEXT 成功返回,则运行时必须表现得像是在 xrDestroySpatialContainerEXT 实现的早期阶段隐式调用了 xrEndSpatialContainerRenderingEXT(如果需要)。

当应用收到针对活跃渲染集中的容器的 XrEventDataSpatialContainerClosedEXT 事件时,该应用应调用 xrEndSpatialContainerRenderingEXT 并销毁 XrSpatialContainerEXT 句柄。

xrBeginSpatialContainerRenderingEXT 函数会将空间容器添加到每个会话的活跃渲染集中,从而允许应用将空间容器的合成层提交到 xrEndFrame。

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

参数说明

应用通常会在响应可见性更改事件时调用此函数,其中 XrEventDataSpatialContainerVisibleChangedEXT :: visible 设置为 true。

如果在调用 xrCreateSpatialContainerEXT 时未使用 XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT 创建空间容器,则运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_MISSING_EXT。

如果空间容器已在活跃渲染集中,则运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_IS_RENDERING_EXT。

如果在应用无法调用 xrWaitFrame 的时间(即在 xrBeginFrame 和 xrEndFrame 窗口内)调用 xrBeginSpatialContainerRenderingEXT,则运行时必须返回 XR_ERROR_CALL_ORDER_INVALID。

有效用法(隐式)

线程安全

返回代码

成功

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

失败

  • 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

XrSpatialContainerBeginInfoEXT 结构用于指定开始渲染空间容器的参数。

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

成员说明

primaryViewConfigurationType 是应用在渲染时打算使用的视图配置。出于优化目的,运行时可能会将视图配置降级为视图较少的类型(例如,容器距离非常远且非常小,以至于无法感知立体效果)。运行时会从对 xrLocateSpatialContainerViewsEXT 的调用中返回有关衰减视图配置的详细信息。

有效用法(隐式)

xrEndSpatialContainerRenderingEXT 函数用于从活跃渲染集中移除空间容器。

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

参数说明

应用通常会在响应可见性更改事件时调用此函数,其中 XrEventDataSpatialContainerVisibleChangedEXT :: visible 设置为 false。

如果空间容器不在活跃渲染集中,运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT。

如果在应用无法调用 xrWaitFrame 的时间(即 xrBeginFrame 和 xrEndFrame 调用之间的时段)调用 xrEndSpatialContainerRenderingEXT,则运行时必须返回 XR_ERROR_CALL_ORDER_INVALID。

有效用法(隐式)

线程安全

返回代码

成功

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

失败

  • 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

XrSpatialContainerEndInfoEXT 结构用于指定结束渲染空间容器的参数。

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

成员说明

有效用法(隐式)

通过 xrLocateSpatialContainerViewsEXT 定位视图

xrLocateSpatialContainerViewsEXT 函数用于定位一组空间容器的视图。

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

参数说明

此函数取代了 xrLocateViews,使应用能够同时查询多个空间容器的视图。

所有空间容器的视图都会在单个 views 数组中返回。视图的排序依据是空间容器在 XrSpatialContainerViewsLocateInfoEXT :: viewLocateInfos 数组中的索引,然后是该容器的视图索引。

例如,如果应用在 XrSpatialContainerViewsLocateInfoEXT :: viewLocateInfos 中提供了三个空间容器:

  • 索引 0:XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO(1 次观看)
  • 索引 1:XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO(2 个视图)
  • 索引 2:XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET(4 个数据视图)

然后,运行时必须使用 7 个视图填充 views 数组:

  • views [0] 是容器 0 的视图。
  • views [1] 是容器 1 的左眼视图。
  • views [2] 是容器 1 的右眼视图。
  • views [3] 是容器 2 的左眼视图。
  • views [4] 是容器 2 的右眼视图。
  • views [5] 是容器 2 的左眼插图视图。
  • views [6] 是容器 2 的右眼插图视图。

如果 viewCount 不等于每个空间容器请求的视图配置类型的观看次数之和,则运行时必须返回 XR_ERROR_VALIDATION_FAILURE。

出于优化目的,运行时可能会将视图配置降级为视图较少的类型(例如,容器距离非常远且非常小,以至于无法感知立体效果)。如果运行时衰减视图配置,则运行时必须仍返回该容器的原始视图数量,并使用衰减视图的副本填充额外视图。例如,如果 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO 衰减为 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO,则索引 0 和索引 1 都将包含单个单声道视图。因此,views 数组中返回的视图总数与运行时是否决定衰减任何视图配置无关。

运行时应调整返回的每个 XrView :: fov,使视锥体尽可能小,同时仍包含相应空间容器的整个边界。运行时可能会返回非对称视野,例如,当空间容器与用户的视角呈倾斜角度,或被其他对象部分遮挡时。运行时可以自由定位空间容器的目标渲染表面。由于多应用渲染的特性(空间容器与用户分离),运行时可能会返回不适合像素渲染的 XrView 值,例如,如果空间容器在屏幕上不可见,则没有有效值可用于设置 XrView :: fov。在这种情况下,运行时必须将 XrSpatialContainerViewStateEXT :: shouldSubmitLayers 设置为 false。

运行时会在 viewStates 数组中返回每个空间容器的其他渲染状态信息。如果 viewStateCount 不等于 XrSpatialContainerViewsLocateInfoEXT :: viewLocateInfoCount,则运行时必须返回 XR_ERROR_VALIDATION_FAILURE。

在运行时为空间容器将 XrEventDataSpatialContainerClosedEXT 事件加入队列后,此函数必须在 XrSpatialContainerViewStateEXT :: shouldSubmitLayers 中返回 XR_FALSE,并在 XrSpatialContainerViewStateEXT :: viewStateFlags 中返回 0,以对应相应的空间容器。

对于在现实世界中位置不断变化的空间容器,运行时应在填充 views 时考虑容器的运动,以最大限度地减少重投影伪影。

如果针对当前不在活跃渲染集中的一个或多个空间容器调用 xrLocateSpatialContainerViewsEXT,则运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT。

有效用法(隐式)

返回代码

成功

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

失败

  • 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

XrSpatialContainerViewsLocateInfoEXT 结构包含用于在一组空间容器中定位视图的参数。

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

成员说明

  • type 是此结构的 XrStructureType。
  • next 是 NULL 或指向结构链中下一个结构的指针。
  • displayTime 是要在其中查找视图的 XrTime。
  • viewLocateInfoCount 是 viewLocateInfos 数组中的条目数。
  • viewLocateInfos 是一个指向 XrSpatialContainerViewLocateInfoEXT 结构数组的指针。

有效用法(隐式)

XrSpatialContainerViewLocateInfoEXT 结构包含用于定位视图的单个空间容器的参数。

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

成员说明

应用可以在 space 中提供任何有效的 XrSpace,但建议为 XrSpatialContainerViewLocateInfoEXT 结构中指定的相应 XrSpatialContainerEXT 提供空间容器空间。

有效用法(隐式)

XrSpatialContainerViewStateEXT 结构通过调用 xrLocateSpatialContainerViewsEXT 来填充,其中包含每个空间容器的渲染适用性信息。

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

成员说明

  • type 是此结构的 XrStructureType。
  • next 是 NULL 或指向结构链中下一个结构的指针。
  • viewStateFlags 是 XrViewStateFlags 的位掩码。这些标志与核心 XrViewState 结构中的 viewStateFlags 具有相同的用途。
  • viewConfigurationType 是一个 XrViewConfigurationType,用于指示运行时为空间容器使用的视图配置类型,运行时可能会将其降级为视图数量少于请求数量的视图配置。
  • shouldSubmitLayers 是运行时返回的 XrBool32。如果为 XR_FALSE,应用应避免为此空间容器提交合成,因为它当前不可见或处于不需要渲染的状态。
  • recommendedImageExtent 是一个 XrExtent2Di 结构,表示空间容器的推荐分辨率。

与核心 OpenXR 视图位置一样,应用必须检查 XrSpatialContainerViewStateEXT :: viewStateFlags,以确保在使用视图数据进行渲染之前,位置和方向有效且已被跟踪。

应用应检查 shouldSubmitLayers 和 viewConfigurationType,以避免渲染超出运行时建议的内容。当 shouldSubmitLayers 为 false 时,应用会按如下方式更改其渲染行为:

  • 应用不得读取相应的 XrView :: fov。
  • 应用不得读取 recommendedImageExtent。
  • 应用应避免渲染到交换链,并避免为相应的空间容器提交合成层。
  • 应用应将 XrSpatialContainerLayerEXT :: retainPreviousSubmission 设置为 true。如果应用选择无论如何都提交图层,则由运行时决定如何处理这些图层(例如,运行时可能选择丢弃这些图层)。

recommendedImageExtent 专门用于根据容器位置或用户视图进行更新的图层。在更新内容取决于空间容器位置或用户视图(例如普通 3D 内容)的图层时,应用应渲染到此较小的视口,而不是默认的推荐视图大小。使用不依赖于空间容器位置或用户视图的内容(例如静态四边形图层)更新图层时,应用可以安全地忽略这些值。对于依赖于视图的层,recommendedImageExtent 是运行时向应用提供的建议,旨在最大限度地减少锯齿并提高渲染性能。如果应用愿意,可以提交完整分辨率的图片。

recommendedImageExtent 的 XrExtent2Di :: width 和 XrExtent2Di :: height 必须始终是小于或等于空间容器主视图配置的 XrViewConfigurationView 的 XrViewConfigurationView :: recommendedImageRectWidth 和 XrViewConfigurationView :: recommendedImageRectHeight 的正整数。应用不应仅仅因为 recommendedImageExtent 发生变化而重新创建交换链,因为这是对应用的动态提示,用于指定要在其提交的任何全分辨率合成层的 XrSwapchainSubImage 中使用的 XrSwapchainSubImage :: imageRect,并且可以在每一帧中发生变化。

有效用法(隐式)

查看参考空间

视图参考空间保持不变,因为它位于 OpenXR 核心中。与核心库一样,它不适用于渲染。

通过 xrEndFrame 提交图层

为了渲染空间容器,应用必须向 xrEndFrame 提供与其各自空间容器关联的图层列表。这是通过 XrSpatialContainerLayerFrameEndInfoEXT 结构实现的。

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

成员说明

  • type 是此结构的 XrStructureType。
  • next 是 NULL 或指向结构链中下一个结构的指针。
  • containerLayerCount 是 containerLayers 数组中的元素数量。
  • containerLayers 是一个指向 XrSpatialContainerLayerEXT 结构数组的指针。

containerLayers 数组包含一个 XrSpatialContainerLayerEXT,对应于活跃渲染集中的每个空间容器(由 xrBeginSpatialContainerRenderingEXT 和 xrEndSpatialContainerRenderingEXT 按会话管理的容器)。每个 XrSpatialContainerLayerEXT 都包含一个与给定空间容器关联的 XrCompositionLayerBaseHeader 指针列表。运行时可以按照其选择的任何顺序(与 containerLayers 的顺序无关)来组合容器。例如,运行时可以根据容器相对于用户的深度来合成容器,让空间容器充当背景层,或“固定”空间容器,使其始终位于顶部,而不管其深度如何。在有界模式或沉浸模式下,运行时可以使用不同的策略为空间容器合成图层。例如,运行时可以将每个有界空间容器的合成层预合成为一个立体四边形层,然后在第二个合成步骤中与其他所有容器合并。

XrFrameEndInfo :: displayTime 和 XrFrameEndInfo :: environmentBlendMode 将应用于 XrSpatialContainerLayerFrameEndInfoEXT 中提供的所有空间容器。

当此结构通过链接到 XrFrameEndInfo 传递给 xrEndFrame 时,系统会根据活跃渲染集添加以下其他错误情况:

  • 如果一个空间容器被 containerLayers 的多个元素引用,则运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_DUPLICATE_EXT。
  • 如果 containerLayers 的元素未引用有效渲染集中的空间容器,则运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_MISSING_EXT。
  • 如果 containerLayers 引用的空间容器不是活跃渲染集的成员,则运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT。

此外,如果活跃渲染集中有任何空间容器,并且此结构未链接到 XrFrameEndInfo,则运行时必须返回 XR_ERROR_SPATIAL_CONTAINER_MISSING_EXT。

在上述任何错误情况下,运行时不得为任何空间容器渲染任何内容。

如果活跃渲染集中没有空间容器,只要应用不提交任何合成层,可以调用 xrEndFrame。

请注意,单个交换链可以通过有效结构中的图层提交到多个容器,例如,通过单个交换链渲染的四边形图层提交到多个空间容器。这种情况的处理方式与其他渲染方式一致:运行时必须在所有此类空间容器中渲染四边形。

有效用法(隐式)

XrSpatialContainerLayerEXT 结构会对标准合成层进行分组,并将其与特定空间容器相关联。

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

成员说明

  • type 是此结构的 XrStructureType。
  • next 是 NULL 或指向结构链中下一个结构的指针。
  • spatialContainer 是标识目标容器的 XrSpatialContainerEXT。
  • retainPreviousSubmission 是 XrBool32。如果为 XR_TRUE,运行时将保留此空间容器最后提交的帧。
  • layerCount 是 layers 数组中的合成层数。
  • layers 是一个指向 XrCompositionLayerBaseHeader 指针数组的指针。

当 retainPreviousSubmission 为 XR_TRUE 时,运行时必须重复使用在 retainPreviousSubmission 为 XR_FALSE 的最近一次 xrEndFrame 调用期间锁存的合成层和交换链图片内容。对保留层所用交换链的任何后续修改不得反映在渲染输出中。

对于为单个空间容器提交的图层(容器内渲染),运行时必须遵循已指定的图层合成顺序规则。也就是说,容器内的图层必须按照指定的顺序绘制,并通过画家算法进行合成。

“清除”空间容器是指从空间容器中移除所有内容,从而导致在当前帧中不为该容器渲染任何内容。运行时必须将此视为应用为容器内的每个像素提交透明黑色(所有位均为 0)。“保留”空间容器是指运行时继续显示为该容器提交的最新一组图层。

对于每个空间容器,应用可以指定是提交新图层、保留之前提交的内容,还是清空空间容器:

  • 如需提交新层,应用会提供一个非零 layerCount 和一个有效的 layers 指针。
  • 如需清除空间容器,其条目的 layerCount 必须为 0,retainPreviousSubmission 必须为 XR_FALSE。
  • 如需保留上次提交的图层和交换链映像(保留),其条目的 layerCount 必须为 0,retainPreviousSubmission 必须为 XR_TRUE。

这种针对每个容器的显式控制可确保应用的意图在每一帧中都得到清晰传达。

如果 retainPreviousSubmission 为 XR_TRUE 且 layerCount 不为 0,则运行时必须返回 XR_ERROR_VALIDATION_FAILURE。

如果 XrSpatialContainerCompositionLayerViewConfigurationEXT 未链接到属于空间容器的 XrCompositionLayerProjection,则运行时必须返回 XR_ERROR_VALIDATION_FAILURE。此结构仅适用于 XrCompositionLayerProjection 图层。如果它与其他层类型(例如四边形层或圆柱体层)链接,则运行时必须返回 XR_ERROR_VALIDATION_FAILURE。

有效用法(隐式)

为投影层指定视图配置

XrSpatialContainerCompositionLayerViewConfigurationEXT 结构用于指定用于为空间容器渲染投影层的视图配置。

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

成员说明

为空间容器提交 XrCompositionLayerProjection 时,应用必须指定用于渲染该图层的视图配置。这是必要的,因为应用可以选择是使用空间容器的主视图配置还是运行时建议的衰减视图配置(如果有)来渲染每一帧。

当 XrSpatialContainerCompositionLayerViewConfigurationEXT 链接时,应用必须确保 XrCompositionLayerProjection :: viewCount 是链接结构中指定的 XrViewConfigurationType 的有效视图计数。链式结构中指定的 viewConfigurationType 必须是容器启动时指定的 XrSpatialContainerBeginInfoEXT :: primaryViewConfigurationType,或运行时在 XrSpatialContainerViewStateEXT 中返回的衰减视图配置类型,或通过扩展程序启用的其他值。

有效用法(隐式)

像素裁剪行为

应用应在提交合成层时通过链接 XrSpatialContainerLayerVolumeClippingEXT,告知运行时它们是否在空间容器边界之外渲染像素。

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

成员说明

此结构可作为提示,指示应用是否在空间容器边界之外渲染像素。当空间容器处于沉浸模式时,由于边界是无限的,因此此提示没有意义。

对于使用自渲染的有界空间容器,运行时需要同时考虑 3D 体积边界和 2D 像素边界。3D 体积边界由 xrGetSpatialContainerBoundsEXT 返回。2D 像素边界是 3D 体积边界在系统每个显示屏上的投影。

运行时必须舍弃交换链图片中超出 2D 像素边界的像素,但不得舍弃位于 2D 像素边界内但超出 3D 体积边界的像素。可以实现立体效果,让内容看起来超出容器的 3D 体积边界或位于其前方。应用可以舍弃有界空间容器 3D 体积边界之外的像素,以减少深度悖论,并且应根据其舍弃方式设置 volumeClipping。深度悖论通常会导致眼睛疲劳和用户不适。

运行时可以使用 volumeClipping 来调整特定空间容器的显示方式。例如,如果设置为 XR_SPATIAL_CONTAINER_VOLUME_CLIPPING_STRICT_EXT,运行时可能会渲染 3D 体积边界的背面,如果空间容器在容器的 3D 体积边界后面绘制像素,则会导致深度悖论。

如果提交了深度信息,未来的扩展程序可能会允许根据 3D 体积边界进行剪裁。

有效用法(隐式)

新增命令

新结构

新枚举常量

  • XR_EXT_SPATIAL_CONTAINER_SELF_RENDERING_EXTENSION_NAME
  • XR_EXT_spatial_container_self_rendering_SPEC_VERSION
  • 扩展 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
  • 扩展 XrSpatialContainerGraphicsPresentationEXT:

    • XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT
  • 扩展 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

代码示例

// 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));

版本历史记录

  • 修订版 1,2026 年 8 月 6 日 (Lachlan Ford)

    • 初始扩展程序说明