WebXR 设备 API

W3C 候选推荐草案,

关于本文档的更多详情
此版本:
https://www.w3.org/TR/2026/CRD-webxr-20260609/
最新发布版本:
https://www.w3.org/TR/webxr/
编辑草案:
https://immersive-web.github.io/webxr/
先前版本:
历史:
https://www.w3.org/standards/history/webxr/
实现报告:
https://wpt.fyi/results/webxr?label=master&label=experimental&aligned
反馈:
GitHub
编辑:
Google
Google [Mozilla 至 2020 年]
Meta
前编辑:
Amazon [Microsoft 至 2018 年]
参与:
提交问题开放问题
邮件列表归档
W3C 的 #immersive-web IRC

摘要

本规范描述在 Web 上访问虚拟现实(VR)和增强现实(AR)设备的支持, 包括传感器和头戴式显示器。

本文档状态

本节描述本文档在发布时的状态。当前 W3C 出版物列表以及本 技术报告的最新修订版可在 W3C 标准与草案索引中找到。

沉浸式 Web 工作组维护着该组尚未处理的 所有错误 报告列表。本草案突出显示了一些待定问题,这些问题 仍将在工作组中讨论。对于这些问题的结果,包括它们是否有效, 尚未作出决定。 强烈鼓励为未解决问题提交带有拟议规范文本的拉取请求。

本文档由沉浸式 Web 工作组作为候选推荐草案发布,使用的是推荐标准 轨道。本文档旨在成为 W3C 推荐标准。

作为候选推荐发布并不意味着 W3C 及其成员的认可。 候选推荐草案整合了工作组打算纳入后续候选推荐快照的、来自上一份候选推荐的更改。 这是一份草案文档,可能随时被更新、替换或废弃为其他文档。 除作为进行中的工作外,不宜引用本文档。

本文档进入拟议推荐阶段的准入条件 是至少有两个独立且可互操作的用户代理 实现本规范的所有特性,这将通过 通过工作组开发的测试套件中定义的用户代理测试来确定。 工作组将准备一份实现报告来跟踪进展。

本文档由一个在 W3C 专利政策下运作的小组制作。 W3C 维护着一个与该组交付物相关的任何 专利 披露的公开列表;该页面还包含 披露专利的说明。实际知晓某项专利且该个人 认为该专利包含 必要 权利要求的个人,必须按照 W3C 专利政策第 6 节披露该信息。

本文档受 2025 年 8 月 18 日 W3C 流程文档管辖。

关于自上一份草案以来的更改, 请参见更改一节。

1. 引言

支持虚拟现实(VR)和增强现实(AR)应用的硬件现已广泛 面向消费者提供,提供了一个兼具新机遇和新挑战的沉浸式计算平台。能够直接与 沉浸式硬件交互,对于确保 Web 能够在这种环境中作为一等公民运行至关重要。

沉浸式计算引入了对高精度、低延迟通信的严格要求,以便 提供可接受的体验。它也给像 Web 这样的平台带来了独特的安全关切。 WebXR 设备 API 提供了必要的接口,使开发者能够 在 Web 上跨多种硬件形态构建引人入胜、舒适且安全的沉浸式应用。

其他 Web 接口,例如 RelativeOrientationSensorAbsoluteOrientationSensor, 可以被重新用于从某些设备暴露输入,以在有限 情况下对 WebXR 设备 API 进行 polyfill。不过,这些接口无法支持高端沉浸式体验的多项功能, 例如 6DoF 跟踪、向 头显外围设备呈现,或被跟踪的输入设备。

1.1. 术语

本文档通篇使用首字母缩略词 XR 来指代用于虚拟现实、增强现实及其他相关技术的 硬件、应用和技术谱系。示例包括但不限于:

它们之间的重要共同点是都提供某种程度的空间跟踪,用以 模拟虚拟内容的视图。

诸如“XR device”、“XR application”等术语通常被理解为适用于上述任何内容。 本文档中仅适用于这些设备某个子集的部分,会在适当位置予以说明。

术语 3DoF6DoF 在本文档通篇用于描述 XR 设备的跟踪 能力。

1.2. 应用流程

大多数使用 WebXR 设备 API 的应用都会遵循类似的使用模式:

2. 模型

2.1. XR 设备

XR 设备是 能够向用户呈现沉浸式内容的物理硬件单元。如果内容产生视觉、音频、触觉或其他感官输出, 用于模拟或增强用户环境的各个方面,则该内容被认为是“沉浸式”的。 这最常涉及跟踪用户在空间中的运动,并 生成与用户运动同步的输出。在桌面客户端上,这通常是 头显外围设备。在移动客户端上,它可以表示移动设备本身与 观看器支架的结合。它也可以表示没有立体呈现能力但具有更 高级跟踪能力的设备。

一个 XR 设备具有一个受支持模式列表 (一个由字符串组成的列表),该列表包含 XRSessionMode 中该 XR 设备支持的枚举值。

每个 XR 设备对于其 受支持模式列表中的每个 XRSessionMode 都具有一个已授予特性集合,它是一个由 特性描述符组成的 集合, 并且 MUST 初始为空集合

用户代理具有一个沉浸式 XR 设备列表(一个由 XR 设备组成的列表),该列表 MUST 初始为空 列表

用户代理具有一个沉浸式 XR 设备nullXR 设备),其初始值为 null,并 表示来自沉浸式 XR 设备列表的活动 XR 设备。此对象 MAY 位于 单独的线程上并异步更新。

用户代理 MUST 具有一个默认内联 XR 设备,它是一个 XR 设备,其 受支持模式列表 MUST 包含 "inline"默认内联 XR 设备 MUST NOT 报告任何姿态信息, 并且 MUST NOT 报告XR 输入 源或事件,但由指针事件创建的除外。

注:默认内联 XR 设备 完全是为开发者提供便利而存在,使他们能够对 内联和沉浸式内容使用相同的渲染和输入逻辑。默认内联 XR 设备不会暴露任何 开发者无法通过页面上的其他机制(例如用于输入的指针事件)获得的信息, 它只会以以 XR 为中心的格式暴露这些值。

用户代理 MUST 具有一个内联 XR 设备,它是一个 XR 设备,其 受支持模式列表 MUST 包含 "inline"。 如果内联 XR 设备能够将其受支持的 主视图作为 HTML 文档的一部分暴露, 则它 MAY 能够支持 inline-stereo 特性描述符。 如果沉浸式 XR 设备 提供的跟踪适合暴露给内联内容,则内联 XR 设备 MAY 是该沉浸式 XR 设备, 否则为默认内联 XR 设备

注:在手机上,内联 XR 设备可能报告 来源于手机内部传感器(如陀螺仪和加速度计)的姿态信息。在没有类似传感器的台式机和笔记本电脑上, 内联 XR 设备将无法报告姿态,因此 应回退到默认内联 XR 设备。如果用户代理已经 运行在XR 设备上, 内联 XR 设备将 是同一设备,并且可能支持多个视图。在提供超出 默认内联 XR 设备所暴露内容之外的任何跟踪或输入特性之前, 必须获得用户同意。

沉浸式 XR 设备列表内联 XR 设备沉浸式 XR 设备的当前值 MAY 位于单独的线程上并异步更新。这些对象 SHOULD NOT 在未 并行运行的步骤中被直接访问。

3. 初始化

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute XRSystem xr;
};

xr 属性的 getter MUST 返回与其关联的 XRSystem 对象。

3.2. XRSystem

[SecureContext, Exposed=Window] interface XRSystem : EventTarget {
  // Methods
  Promise<boolean> isSessionSupported(XRSessionMode mode);
  [NewObject] Promise<XRSession> requestSession(XRSessionMode mode, optional XRSessionInit options = {});

  // Events
  attribute EventHandler ondevicechange;
};

当创建 Navigator 对象时,用户代理 MUST 创建一个 XRSystem 对象 并将其与该对象关联。

XRSystem 对象是 API 的入口点,用于查询用户代理可用的 XR 特性,并通过创建 XRSession 来启动与 XR 硬件的通信。

用户代理 MUST 能够枚举沉浸式 XR 设备,这些设备连接到系统;此时每个可用设备都会被放入 沉浸式 XR 设备列表中。后续请求枚举的算法 MUST 重用缓存的 沉浸式 XR 设备列表。枚举设备 不应初始化设备跟踪。 在首次枚举后,用户代理 MUST 开始监视设备连接和断开连接,将已连接设备添加到 沉浸式 XR 设备列表并移除已断开连接的设备。

每当沉浸式 XR 设备列表发生变化时,用户代理 should 通过运行以下步骤来选择沉浸式 XR 设备

  1. oldDevice沉浸式 XR 设备

  2. 如果沉浸式 XR 设备列表是空 列表, 则将沉浸式 XR 设备设为 null

  3. 如果沉浸式 XR 设备列表大小为一,则将沉浸式 XR 设备设为 沉浸式 XR 设备列表[0]。

  4. 按如下方式设置沉浸式 XR 设备

    如果存在任何活动的 XRSession, 且沉浸式 XR 设备列表 包含 oldDevice

    沉浸式 XR 设备设为 oldDevice

    否则:

    沉浸式 XR 设备设为用户代理所选择的设备。

  5. 如果适当,用户代理 MAY 将内联 XR 设备更新为 沉浸式 XR 设备, 否则更新为默认内联 XR 设备

  6. 如果这是首次枚举设备,或者 oldDevice 等于 沉浸式 XR 设备,则中止这些步骤。

  7. 关闭 任何活动的 XRSession

  8. 排队一个任务,将所有 WebGLRenderingContextBase 实例的 XR compatible 布尔值设为 false

  9. 排队一个任务,在 相关 Global 对象navigatorxr触发 一个事件,其名称为 devicechange

  10. 排队一个任务,在任何因 沉浸式 XR 设备内联 XR 设备的变化而受到影响的 XRPermissionStatus 对象上触发适当的 change 事件。

注:这些步骤应始终 并行运行。

注:沉浸式 XR 设备列表包含多个设备时,用户代理可以使用其 想要的任何标准来选择沉浸式 XR 设备。 例如,用户代理可以始终选择列表中的第一项,或提供设置 UI 让 用户管理设备优先级。理想情况下,用于选择默认设备的算法是稳定的,并且会 在多个浏览会话中产生相同的设备选择结果。

用户代理可以通过运行以下步骤来确保选中了沉浸式 XR 设备

  1. 如果沉浸式 XR 设备不是 null,则返回 沉浸式 XR 设备并中止这些步骤。

  2. 枚举沉浸式 XR 设备

  3. 选择沉浸式 XR 设备

  4. 返回沉浸式 XR 设备

注:这些步骤应始终 并行运行。

ondevicechange 属性是 事件处理器 IDL 属性,对应 devicechange 事件类型。

isSessionSupported(mode) 方法 查询给定 mode 是否可能受到用户代理和设备能力的支持。

调用此方法时,它 MUST 运行以下步骤:

  1. promise 为此 XRSystem相关 realm中的 新 Promise

  2. 如果 mode"inline", 则用 true 解决 promise 并返回它。

  3. 如果 mode沉浸式会话模式,且请求文档的 不被允许使用 "xr-spatial-tracking" 权限策略,则用 "SecurityError" DOMException 拒绝 promise 并返回它。

  4. 按如下方式检查会话 mode 是否受支持:

    如果已知用户代理和系统 绝不会支持 mode 会话

    false 解决 promise

    如果已知用户代理和系统 通常支持 mode 会话

    promise MAY 用 true 解决, 前提是所有按用户代理字符串 不可区分的用户代理实例在此处产生相同结果。

    否则

    并行运行以下步骤:

    1. device 为针对 mode、一个空 列表和 一个空 列表 获得当前设备的结果。

    2. 如果 device 为 null,则用 false 解决 promise 并中止这些步骤。

    3. 如果 device受支持模式列表包含 mode,则 排队一个任务以用 false 解决 promise 并中止这些步骤。

    4. 使用 XRSessionSupportedPermissionDescriptor 请求使用 强大特性 "xr-session-supported"使用权限,其中 mode 等于 mode。如果它返回 "denied", 则排队一个任务以用 false 解决 promise 并中止这些步骤。更多信息见指纹识别 考量

    5. 排队一个任务,用 true 解决 promise

  5. 返回 promise

注:isSessionSupported() 的目的并不是完全准确地报告用户代理创建 XRSession 的能力,而是 告知页面是否建议宣传创建给定模式会话的能力。 即使用户代理在解决该方法之前检查了必要硬件/软件是否存在,也预期会出现 一定程度的假阳性。(例如,即使存在适当硬件, 在请求会话时它也可能已将独占访问权授予其他应用。)

预期大多数具有 XR 内容的页面会在文档生命周期早期调用 isSessionSupported()。 因此,调用 isSessionSupported() SHOULD 避免显示任何模态或其他侵入式 UI。调用 isSessionSupported() MUST NOT 触发设备选择 UI,MUST NOT 干扰系统上任何正在运行的 XR 应用,并且 MUST NOT 导致 XR 相关应用启动,例如系统托盘或商店前端。

以下代码检查是否支持 immersive-vr 会话。
const supported = await navigator.xr.isSessionSupported('immersive-vr');
if (supported) {
  // 'immersive-vr' sessions may be supported.
  // Page should advertise support to the user.
} else {
  // 'immersive-vr' sessions are not supported.
}

XRSystem 对象具有一个待处理沉浸式会话布尔值,它 MUST 初始为 false;一个活动沉浸式会话,它 MUST 初始为 null; 以及一个内联 会话列表,它 MUST 初始为空。

requestSession(mode, options) 方法尝试在可能的情况下为给定 mode 初始化一个 XRSession, 并在必要时进入沉浸式模式。

调用此方法时,用户代理 MUST 运行以下步骤:

  1. promise 为此 XRSystem相关 realm中的 新 Promise

  2. immersivetrue,如果 mode沉浸式会话模式;否则为 false

  3. global object 为调用此方法的 XRSystem相关 Global 对象

  4. 按如下方式检查会话请求是否被允许:

    如果 immersivetrue
    1. 检查是否为 global object 允许沉浸式会话请求; 如果不允许,则用 "SecurityError" DOMException 拒绝 promise 并返回 promise

    2. 如果待处理沉浸式会话true,或活动沉浸式会话不是 null,则用 "InvalidStateError" DOMException 拒绝 promise 并返回 promise

    3. 待处理沉浸式会话设为 true

    否则:

    检查是否为 global object 允许内联会话请求; 如果不允许,则用 "SecurityError" DOMException 拒绝 promise 并返回 promise

  5. 并行运行以下步骤:

    1. requiredFeaturesoptionsrequiredFeatures

    2. optionalFeaturesoptionsoptionalFeatures

    3. device 设为针对 moderequiredFeaturesoptionalFeatures 获得 当前设备的结果。

    4. 排队一个任务以执行以下步骤:

      1. 如果 devicenull,或 device受支持模式列表包含 mode,则运行以下 步骤:

        1. 用 "NotSupportedError" DOMException 拒绝 promise

        2. 如果 immersivetrue,则将 待处理沉浸式 会话设为 false

        3. 中止这些步骤。

      2. descriptor 为一个以 moderequiredFeaturesoptionalFeatures 初始化的 XRPermissionDescriptor

      3. status 为一个 XRPermissionStatus, 初始为 null

      4. descriptorstatus 请求 xr 权限

      5. 如果 statusstate"denied", 则运行以下步骤:

        1. 用 "NotSupportedError" DOMException 拒绝 promise

        2. 如果 immersivetrue,则将 待处理沉浸式 会话设为 false

        3. 中止这些步骤。

      6. granted 为从 statusgranted 获得的 集合

      7. session 为此 XRSystem相关 realm中的 一个 XRSession 对象。

      8. sessionmodegranteddevice 初始化会话

      9. 按如下方式可能设置活动沉浸式会话

        如果 immersivetrue

        活动沉浸式会话 设为 session,并将 待处理沉浸式 会话设为 false

        否则:

        session 追加到内联会话列表

      10. session 解决 promise

      11. 排队一个任务以执行以下步骤:

        注:这些步骤确保初始 inputsourceschange 事件发生在初始会话被解决之后。
        1. sessionpromise resolved 标志设为 true

        2. sources 为任何已附加到 session 的现有输入源。

        3. 如果 sources 非空,则执行以下步骤:

          1. session活动 XR 输入源列表设为 sources

          2. session 上触发一个名为 inputsourceschangeXRInputSourcesChangeEvent, 并将 added 设为 sources

  6. 返回 promise

为了针对 XRSessionMode moderequiredFeaturesoptionalFeatures 获得 当前设备,用户代理 MUST 运行以下步骤:

  1. 按如下方式选择 device

    如果 mode沉浸式会话模式:

    device 设为确保选中沉浸式 XR 设备的结果。

    否则,如果 requiredFeaturesoptionalFeatures 不为空:

    device 设为内联 XR 设备

    否则:

    device 设为默认内联 XR 设备

  2. 返回 device

注:这些步骤应始终 并行运行。

以下代码尝试获取一个 immersive-vr XRSession
const xrSession = await navigator.xr.requestSession("immersive-vr");

3.3. XRSessionMode

XRSessionMode enum 定义了 XRSession 可以运行的模式。

enum XRSessionMode {
  "inline",
  "immersive-vr",
  "immersive-ar"
};

在本文档中,术语 内联会话inline 会话,术语 沉浸式会话immersive-vrimmersive-ar 会话。

沉浸式会话 MUST 提供某种程度的观看者跟踪,并且 内容 MUST 以相对于用户和/或周围环境的适当比例显示。 此外,沉浸式 会话 MUST 被授予对沉浸式 XR 设备独占访问,这意味着当 沉浸式会话"visible" 时, HTML 文档不会显示在沉浸式 XR 设备的显示器上,来自任何其他来源的内容也没有 独占访问权。独占访问并不阻止用户代理叠加其自己的 UI,但该 UI SHOULD 是最小化的。

注:UA 可以选择叠加用于无障碍或安全的内容, 例如 guardian 边界、障碍物或用户的手,尤其是在没有替代输入 源时。

注:未来规范或模块可能扩展 沉浸式 会话的定义,以包含其他会话模式。

注:独占访问可能呈现方式的示例包括 显示在虚拟现实头显上的立体内容。

注:作为叠加 UI 的一个示例,用户代理或 操作系统在沉浸式会话中可能会在渲染内容上显示通知。

注:虽然在 沉浸式会话期间,HTML 文档不会显示在 沉浸式 XR 设备的显示器上,但它仍可能显示在单独的显示器上,例如 当用户从其计算机上的 2d 浏览器进入 沉浸式会话,而该计算机连接到其 沉浸式 XR 设备时。

3.4. 特性依赖项

XRSession 的某些特性可能因多种原因而无法普遍可用,其中一个原因是并非所有 XR 设备都能支持 完整的特性集合。另一个考虑是某些特性会暴露 敏感信息,这些信息可能 需要明确的用户 意图信号后才能发挥作用。

初始化底层 XR 平台并创建 XRSession 后 却立即通知用户应用无法正确运行,这是一种糟糕的用户体验;因此,开发者可以通过向 requestSession() 传递 XRSessionInit 字典来指明必需特性。 如果任何必需 特性因设备限制而不可用,或者缺少明确的 用户意图信号以暴露与该特性相关的 敏感 信息,这将阻止创建 XRSession

此外,鼓励开发者设计在更强大设备上运行时会渐进增强其功能的体验。 体验不需要但会在可用时加以利用的 可选特性也必须在 XRSessionInit 字典中指明,以确保在必要时启用该特性之前能够确定 用户 意图

dictionary XRSessionInit {
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

requiredFeatures 数组包含体验所需的任何 必需特性。 如果列表中的任何值不是可识别的特性描述符,则不会创建 XRSession。 如果 requiredFeatures 数组中列出的任何特性不受 XR 设备支持,或在必要时尚未收到明确的 用户意图信号,则不会创建 XRSession

optionalFeatures 数组包含体验所需的任何 可选特性。 如果列表中的任何值不是可识别的特性描述符,则会被忽略。 optionalFeatures 数组中列出的特性会在被 XR 设备支持,并且在必要时获得明确的 用户意图信号时启用; 但如果不存在,则不会阻止创建 XRSession

特性列表中给出的值如果是以下任一项,则被视为有效的 特性描述符

本规范的未来迭代和附加模块可能扩展可接受 特性 描述符的列表。

注:如果某个特性需要额外初始化, XRSessionInit 应该为该特性扩展一个新字段。

根据所请求的 XRSessionMode, 某些特性描述符会默认添加到 requiredFeaturesoptionalFeatures 列表中。下表描述与每种会话类型和特性列表关联的 默认特性

特性 会话 列表
"viewer" 所有会话 requiredFeatures
"local" 沉浸式 会话 requiredFeatures

requiredFeaturesoptionalFeatures 给出的特性描述符组合列表,被统称为 XRSession请求特性

某些特性 描述符在出现在 请求特性列表中时,受到权限策略和/或以下要求的约束:通过 显式同意隐式同意, 充分理解用户意图以使用该特性。 下表描述在启用之前必须满足的 特性要求

特性 所需权限策略 所需同意
"local" "xr-spatial-tracking" 内联会话 需要同意
"local-floor" "xr-spatial-tracking" 始终需要同意
"bounded-floor" "xr-spatial-tracking" 始终需要同意
"unbounded" "xr-spatial-tracking" 始终需要同意

注:"local" 始终作为默认特性包含在 沉浸式会话请求特性中,因此 沉浸式 会话始终需要获得显式同意隐式同意

inline-stereo 特性描述符请求 内联会话暴露用于内联 呈现的立体主视图。 如果启用,视图列表 MUST 包含两个 主视图,其中一个的 eye"left", 另一个的eye"right"

inline-stereo 特性描述符仅适用于 "inline" 会话。它 MUST NOT 被授予沉浸式会话

只有在 XR 设备 能够支持该特性时, 请求特性 才能为会话启用;这意味着已知该特性在某些配置下受到 XR 设备支持,即使尚未验证当前 配置是否支持该特性。用户代理 MAY 在需要时应用更严格的约束,以产生更一致的 用户体验。

注:例如,若干 VR 设备支持 配置供用户在其中移动的安全边界,或跳过边界配置并 以用户预期站立不动的模式运行。这样的设备可被视为 能够 支持 "bounded-floor" XRReferenceSpace, 即使它们当前未配置安全边界,因为预期用户可以在体验需要时 适当地配置设备。这是为了允许用户代理在需要时避免在 解决请求特性之前 完全初始化XR 设备或等待识别用户环境。不过,如果 用户代理在请求会话时无需额外初始化即可知道边界状态,则它可以选择在安全边界尚未配置时 拒绝 "bounded-floor" 特性。

4. 会话

4.1. XRSession

与 XR 硬件的任何交互都通过一个 XRSession 对象完成,该对象只能通过在 XRSystem 对象上调用 requestSession() 来获取。一旦成功取得会话, 它可用于 轮询观看者姿态、 查询有关用户环境的信息,并向用户呈现图像。

在可能的情况下,用户代理在取得 XRSession 之前,SHOULD NOT 初始化设备跟踪或渲染 能力。这是为了防止在 XR 系统未被主动使用时启用它们而产生不必要的副作用, 例如电池使用量增加,或在首次导航到一个只想测试 XR 硬件是否存在以便宣传 XR 特性的页面时,相关实用程序应用出现。不过,并非所有 XR 平台都提供无需初始化跟踪即可检测硬件存在的方式, 因此这只是强烈建议。

enum XRVisibilityState {
  "visible",
  "visible-blurred",
  "hidden",
};

[SecureContext, Exposed=Window] interface XRSession : EventTarget {
  // Attributes
  readonly attribute XRVisibilityState visibilityState;
  readonly attribute float? frameRate;
  readonly attribute Float32Array? supportedFrameRates;
  [SameObject] readonly attribute XRRenderState renderState;
  [SameObject] readonly attribute XRInputSourceArray inputSources;
  [SameObject] readonly attribute XRInputSourceArray trackedSources;
  readonly attribute FrozenArray<DOMString> enabledFeatures;
  readonly attribute boolean isSystemKeyboardSupported;

  // Methods
  undefined updateRenderState(optional XRRenderStateInit state = {});
  Promise<undefined> updateTargetFrameRate(float rate);
  [NewObject] Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceType type);

  unsigned long requestAnimationFrame(XRFrameRequestCallback callback);
  undefined cancelAnimationFrame(unsigned long handle);

  Promise<undefined> end();

  // Events
  attribute EventHandler onend;
  attribute EventHandler oninputsourceschange;
  attribute EventHandler onselect;
  attribute EventHandler onselectstart;
  attribute EventHandler onselectend;
  attribute EventHandler onsqueeze;
  attribute EventHandler onsqueezestart;
  attribute EventHandler onsqueezeend;
  attribute EventHandler onvisibilitychange;
  attribute EventHandler onframeratechange;
};

每个 XRSession 都有一个 mode,它是 XRSessionMode 的值之一。

每个 XRSession 都有一个 animation frame,它是一个 XRFrame, 其初始化时将 active 设为 false,将 animationFrame 设为 true,并将 session 设为该 XRSession

每个 XRSession 都有一个 已授予特性集合,它是由 DOMString 组成的集合, 对应于已授予给该 XRSession特性描述符

enabledFeatures 属性返回 已授予特性集合中的特性,作为一个新的 DOMString 数组。

isSystemKeyboardSupported 属性 表示当 XRSession 处于活动状态时,XRSystem 是否 具备显示系统键盘的能力。如果 isSystemKeyboardSupportedtrue,则会触发覆盖键盘的 Web API(例如 focus)将显示 系统键盘。在键盘显示期间,XRSession MUST 将 XRSessionvisibility state 设为 "visible-blurred"

为了初始化 会话,给定 sessionmodegranteddevice, 用户代理 MUST 运行以下步骤:

  1. sessionmode 设为 mode

  2. sessionXR device 设为 device

  3. session已授予特性集合设为 granted

  4. 初始化渲染状态

  5. 如果用户代理的其他特性尚未完成,则执行必要的平台特定步骤,以初始化设备的跟踪和渲染能力, 包括向用户显示任何必要说明。

注:某些设备需要额外的用户 激活说明。例如,在基于手机的头显设备上进入沉浸式模式 需要将手机插入头显,而在连接到外部头显的桌面浏览器上执行此操作 需要佩戴头显。确保显示任何此类说明是用户代理的责任,而不是 作者的责任。

许多不同情况都可能关闭会话,这是永久且不可逆的。一旦会话已关闭, 再次访问 XR 设备的跟踪或渲染能力的唯一方法就是请求 新会话。每个 XRSession 都有一个 ended 布尔值,初始设为 false,表示它是否 已被关闭。

当一个 XRSession session 被关闭时,运行以下步骤:

  1. sessionended 值设为 true

  2. 如果活动沉浸式会话等于 session,则将活动沉浸式会话设为 null

  3. 内联会话列表中移除 session

  4. InvalidStateError 拒绝session 返回的任何未决 promise, 但由 end() 返回的任何 promise 除外。

  5. 如果用户代理的其他特性没有正在主动使用它们,则执行必要的平台特定步骤以关闭设备的跟踪和渲染能力。 这 MUST 包括:

  6. 排队一个任务,在 session 上触发一个名为 endXRSessionEvent

end() 方法提供了一种手动关闭 会话的方式。调用时,它 MUST 运行以下步骤:

  1. promise 为此 XRSession相关 realm中的 新 Promise

  2. 如果 thisended 值为 true,则用 "InvalidStateError" DOMException 拒绝 promise 并返回 promise

  3. 关闭 this

  4. 排队一个任务以执行以下步骤:

    1. 等待与关闭会话相关的任何平台特定步骤完成。

    2. 解决 promise

  5. 返回 promise

每个 XRSession 都有一个 活动渲染 状态,它是一个新的 XRRenderState; 以及一个 待处理渲染 状态,它是一个初始为 nullXRRenderState

renderState 属性返回 XRSession活动渲染 状态

每个 XRSession 都有一个 最小 内联视场和一个 最大内联视场,以弧度定义。这些值 MUST 由用户代理确定,并且 MUST 落在 0PI 的范围内。

每个 XRSession 都有一个 最小近裁剪 平面和一个 最大远裁剪平面,以米定义。这些值 MUST 由用户代理确定,并且 MUST 为非负。最小近裁剪平面 SHOULD 小于 0.1最大远裁剪平面 SHOULD 大于 1000.0(且 MAY 为无限大)。

当用户代理将使用 XRSession sessionXRRenderStateInit newState 更新待处理层状态时,它必须运行以下步骤:

  1. 如果 newStatelayers 的 值不是 null,则抛出 NotSupportedError

注:WebXR layers module 将为 此算法引入新的语义。

当用户代理想要在 XRSession session应用标称帧率 rate 时,它 MUST 运行以下步骤:

  1. 如果 ratesession内部标称帧率相同,则中止 这些步骤。

  2. 如果 sessionended 值为 true,则中止这些步骤。

  3. session内部标称帧率设为 rate

  4. session 上触发一个名为 frameratechangeXRSessionEvent 事件。

updateTargetFrameRate(rate) 方法将目标帧率 rate 传递给 XRSession

调用此方法时,用户代理 MUST 运行以下步骤:

  1. sessionthis

  2. promisesession相关 realm中的 新 Promise

  3. 如果 session 没有内部标称帧率,则用 "InvalidStateError" DOMException 拒绝 promise 并返回 promise

  4. 如果 sessionended 值为 true,则用 "InvalidStateError" DOMException 拒绝 promise 并返回 promise

  5. 如果 rate 不在 supportedFrameRates 中,则用 "TypeError" DOMException 拒绝 promise 并返回 promise

  6. session内部目标帧率设为 rate

  7. 排队一个任务以执行以下步骤:

    1. XR 合成器 MAY 使用 rate 来计算新的 显示帧率和/或 标称帧率

    2. newrate 为新的标称帧率

    3. 排队一个任务以执行以下步骤:

      1. 等待直到 XRSystem标称帧率更新为 newrate 的操作已生效。

      2. newratesession 应用标称帧率

      3. 解决 promise

  8. 返回 promise

如果 XR 合成器出于任何原因 (例如在 "visible-blurred" 事件期间)更改了标称 帧率,则在导致帧率变化的事件结束后,它 SHOULD 使用 内部目标帧率

updateRenderState(newState) 方法将对活动渲染状态的更新排队,以便在下一帧应用。传递给此方法的 XRRenderStateInit newState 中未设置的字段不会被更改。

调用此方法时,用户代理 MUST 运行以下步骤:

  1. sessionthis

  2. 如果 sessionended 值为 true,则抛出 InvalidStateError 并中止这些步骤。

  3. 如果 newStatebaseLayer 是使用不同于 sessionXRSession 创建的,则抛出 InvalidStateError 并中止这些步骤。

  4. 如果 newStateinlineVerticalFieldOfView 已设置,且 session 是一个沉浸式会话,则抛出 InvalidStateError 并中止这些步骤。

  5. 如果 newStatedepthNeardepthFarinlineVerticalFieldOfViewbaseLayerlayers 都未设置,则中止这些步骤。

  6. sessionnewState 运行 更新待处理层状态

  7. activeStatesession活动渲染状态

  8. 如果 session待处理渲染状态null,则将其设为 activeState 的副本。

  9. 如果 newStatepassthroughFullyObscured 值已设置,则将 session待处理渲染状态passthroughFullyObscured 设为 newStatepassthroughFullyObscured

  10. 如果 newStatedepthNear 值已设置,则将 session待处理渲染状态depthNear 设为 newStatedepthNear

  11. 如果 newStatedepthFar 值已设置,则将 session待处理渲染状态depthFar 设为 newStatedepthFar

  12. 如果 newStateinlineVerticalFieldOfView 已设置,则将 session待处理渲染状态inlineVerticalFieldOfView 设为 newStateinlineVerticalFieldOfView

  13. 如果 newStatebaseLayer 已设置,则将 session待处理渲染状态baseLayer 设为 newStatebaseLayer

在请求时,XRSession session MUST 通过运行以下步骤来 应用待处理渲染状态

  1. activeStatesession活动渲染状态

  2. newStatesession待处理渲染状态

  3. session待处理渲染状态设为 null

  4. oldBaseLayeractiveStatebaseLayer

  5. oldLayersactiveStatelayers

  6. 排队一个任务以执行以下步骤:

    1. activeState 设为 newState

    2. 如果 oldBaseLayer 不等于 activeStatebaseLayeroldLayers 不等于 activeStatelayers, 或任一层的尺寸已更改,则为 session 更新 视口

    3. 如果 activeStateinlineVerticalFieldOfView 小于 session最小内联视场,则将 activeStateinlineVerticalFieldOfView 设为 session最小内联视场

    4. 如果 activeStateinlineVerticalFieldOfView 大于 session最大内联视场,则将 activeStateinlineVerticalFieldOfView 设为 session最大内联视场

    5. 如果 activeStatedepthNear 小于 session最小近 裁剪平面,则将 activeStatedepthNear 设为 session最小近裁剪平面

    6. 如果 activeStatedepthFar 大于 session最大远裁剪 平面,则将 activeStatedepthFar 设为 session最大远裁剪平面

    7. baseLayeractiveStatebaseLayer

    8. 按如下方式设置 activeStatecomposition enabledoutput canvas

      如果 session 是一个内联会话,且 baseLayer 是一个 XRWebGLLayer 实例,并且 composition enabled 设为 false

      activeStatecomposition enabled 布尔值设为 false

      activeStateoutput canvas设为 baseLayercontextcanvas

      否则:

      activeStatecomposition enabled 布尔值设为 true

      activeStateoutput canvas设为 null

requestReferenceSpace(type) 方法在可能的情况下构造一个给定 type 的新 XRReferenceSpace

调用此方法时,用户代理 MUST 运行以下步骤:

  1. promise 为此 XRSession相关 realm中的 新 Promise

  2. 并行运行以下步骤:

    1. 如果对 typesession 运行 reference space is supported 的结果为 false,则排队一个任务以用 NotSupportedError 拒绝 promise 并中止这些步骤。

    2. 设置跟踪 type 类型参考空间所需的任何平台资源。

      用户代理无需等待这类参考空间的跟踪建立完成后才解决 requestReferenceSpace()。 当会话最初尝试建立跟踪时,getViewerPose() 返回 null 是可以接受的,内容可以利用这段时间显示启动画面或其他内容。注意, 如果 type"bounded-floor", 且边界尚未建立,则用户代理 MAY 将边界设置为一个较小的初始区域,并在边界建立时使用 reset 事件。
    3. 排队一个任务以运行以下步骤:

    4. typesession 创建参考空间 referenceSpace

    5. referenceSpace 解决 promise

  3. 返回 promise

每个 XRSession 都有一个 活动 XR 输入源列表(一个由 XRInputSource 组成的列表) 以及一个 活动 XR 被跟踪源列表(一个由 XRInputSource 组成的列表),二者 MUST 初始均为空列表

每个 XRSession 都有一个 XR 设备,它是在初始化时设置的一个 XR 设备

inputSources 属性返回 XRSession活动 XR 输入源列表

trackedSources 属性返回 XRSession活动 XR 被跟踪源列表活动 XR 被跟踪源列表 MUST 仅在 tracked-sources 包含于 已授予特性集合中时填充。

用户代理 MUST 监视与 XR 设备相关联的任何 XR 输入源,包括检测 XR 输入源何时被 添加、移除或更改。

每个 XRSession 都有一个 promise resolved 标志,初始为 false

注:此标志的目的是确保 添加 输入源移除输入源更改输入源 算法不会在用户代码实际有机会附加事件监听器之前运行。 如果实现只是选择在会话解决后开始监听输入源变化,则可能不需要此标志。

当新的 XR 输入源变得可用XRSession session 时,用户代理 MUST 运行以下步骤:

  1. 如果 sessionpromise resolved 标志未设置,则中止这些 步骤。

  2. added primary sources 为新的列表

  3. added tracked sources 为新的列表

  4. 对于每个新的 XR 输入源

    1. inputSource 为此 XRSession相关 realm中的 一个 XRInputSource, 然后执行以下步骤:

      如果 inputSource 是一个主输入 源

      inputSource 添加到 added primary sources

      否则,如果 tracked-sources 包含在 session已授予特性集合中:

      inputSource 添加到 added tracked sources

  5. 排队一个任务以执行以下步骤:

    1. added primary sources 扩展 session活动 XR 输入源列表

    2. 如果 added primary sources 非空,则在 session 上触发一个名为 inputsourceschangeXRInputSourcesChangeEvent, 并将 added 设为 added primary sources

    3. added tracked sources 扩展 session活动 XR 被跟踪 源列表

    4. 如果 added tracked sources 非空,则在 session 上触发一个名为 trackedsourceschangeXRInputSourcesChangeEvent, 并将 added 设为 added tracked sources

当任何先前添加的 XR 输入源不再可用XRSession session 时,用户代理 MUST 运行以下步骤:

  1. 如果 sessionpromise resolved 标志未设置,则中止这些 步骤。

  2. removed primary sources 为新的列表

  3. removed tracked sources 为新的列表

  4. 对于每个不再可用的 XR 输入 源

    1. inputSourcesession活动 XR 输入源列表中与该 XR 输入源关联的 XRInputSource, 然后执行以下步骤:

      如果 inputSource 是一个主输入 源

      inputSource 添加到 removed primary sources

      否则,如果 tracked-sources 包含在 session已授予特性集合中:

      inputSource 添加到 removed tracked sources

  5. 排队一个任务以执行以下步骤:

    1. session活动 XR 输入源列表移除 removed primary sources 中的每个 XRInputSource

    2. 如果 removed primary sources 非空,则在 session 上触发一个名为 inputsourceschangeXRInputSourcesChangeEvent, 并将 removed 设为 removed primary sources

    3. session活动 XR 被跟踪 源列表移除 removed tracked sources 中的每个 XRInputSource

    4. 如果 removed tracked sources 非空,则在 session 上触发一个名为 trackedsourceschangeXRInputSourcesChangeEvent, 并将 removed 设为 removed tracked sources

注:当输入源暂时同时失去位置和朝向跟踪时, 用户代理 MAY 触发此事件。建议仅对物理手持控制器输入源这样做。不建议在被跟踪的手部输入源发生这种情况时触发此事件, 因为这会经常发生;也不建议在跟踪器对象输入源发生这种情况时触发, 因为这会使应用更难维持身份概念。

当任何 XR 输入源handednesstargetRayModeprofilesgripSpace 是否存在,或其作为主输入源被跟踪输入源的状态 发生变化,对于 XRSession session,用户代理 MUST 运行以下步骤:

  1. 如果 sessionpromise resolved 标志未设置,则中止这些 步骤。

  2. added primary sources 为新的列表

  3. removed primary sources 为新的列表

  4. added tracked sources 为新的列表

  5. removed tracked sources 为新的列表

  6. 对于每个发生变化的 XR 输入源

    1. oldInputSourcesession活动 XR 输入源列表中先前与该 XR 输入源关联的 XRInputSource, 然后执行以下步骤:

      如果 oldInputSource 是一个主输入 源,或者其状态从主输入 源变为被跟踪输入源

      oldInputSource 添加到 removed primary sources

      否则,如果 tracked-sources 包含在 session已授予特性集合中:

      oldInputSource 添加到 removed tracked sources

    2. newInputSourcesession相关 realm中的一个 XRInputSource, 然后执行以下步骤:

      如果 newInputSource 是一个主输入 源,或者其状态从被跟踪输入 源变为主输入源

      newInputSource 添加到 added primary sources

      否则,如果 tracked-sources 包含在 session已授予特性集合中:

      newInputSource 添加到 added tracked sources

  7. 排队一个任务以执行以下步骤:

    1. session活动 XR 输入源列表移除 removed primary sources 中的每个 XRInputSource

    2. added primary sources 扩展 session活动 XR 输入源列表

    3. 如果 added primary sourcesremoved primary sources 非空, 则在 session 上触发一个名为 inputsourceschangeXRInputSourcesChangeEvent, 将 added 设为 added primary sources,并将 removed 设为 removed primary sources

    4. session活动 XR 被跟踪 源列表移除 removed tracked sources 中的每个 XRInputSource

    5. added tracked sources 扩展 session活动 XR 输入源列表

    6. 如果 added tracked sourcesremoved tracked sources 非空, 则在 session 上触发一个名为 trackedsourceschangeXRInputSourcesChangeEvent, 将 added 设为 added tracked sources,并将 removed 设为 removed tracked sources

每个 XRSession 都有一个 visibility state 值,它是一个 enum。对于 内联会话visibility state MUST 镜像 DocumentvisibilityState。对于 沉浸式会话visibility state MUST 被设置为以下值中最能匹配会话状态的一个。

visibilityState 属性返回 XRSessionvisibility stateonvisibilitychange 属性是 visibilitychange 事件类型的事件处理器 IDL 属性

visibility state MAY 由用户代理在处理 XR animation frame 期间之外的任何时间更改,并且用户代理 SHOULD 在可能时监视 XR 平台,以观察会话可见性何时受到用户代理外部因素影响,并相应更新 visibility state

注:XRSessionvisibility state 不一定意味着 HTML 文档的可见性。 根据系统配置,页面在沉浸式会话处于活动状态时 可能仍继续可见。(例如,连接到 PC 的头显在从沉浸式会话查看内容时,可能仍会在显示器上显示页面。)开发者应继续依赖 页面可见性来 确定页面可见性。

注:XRSessionvisibility state 不会影响或限制在 沉浸式会话处于活动状态时 2D 内容仍然可见的 tethered sessions 中的鼠标行为。如果内容希望对鼠标行为有更强控制, 应考虑使用 [pointerlock] API。

XRSystem 中, 有若干可描述帧率的定义:

每个 XRSession MAY 具有一个 内部目标 frameRate,即 目标帧率

每个 XRSession MAY 具有一个 内部标称 frameRate,即 标称帧率。 如果有效 帧率低于标称帧率,则 XR 合成器 MAY 使用重投影或其他 技术来改善体验。它是可选的,并且 MUST NOT 存在于 内联会话中。

frameRate 属性反映 内部 标称帧率。如果 XRSession 没有 内部标称帧率,则返回 null

onframeratechange 属性是 frameratechange 事件类型的事件处理器 IDL 属性。如果 XRSession标称帧率 因任何原因被更改,则它 MUST 使用新的标称帧率和该 XRSession 应用标称帧率

supportedFrameRates 属性返回受支持 目标帧 率值的列表。此属性是可选的,并且 MUST NOT 存在于 内联会话中,也 MUST NOT 存在于不允许作者控制帧率的 XRSystem 中。 如果 XRSession 支持 supportedFrameRates 属性,它也 MUST 支持 frameRate

每个 XRSession 都有一个 观看者参考空间,它是一个类型为 "viewer"XRReferenceSpace, 并具有恒等 变换origin offset

每个 XRSession 都有一个 视图列表,它是一个列表,其中包含与 XR 设备所提供视图相对应的 视图视图列表XRSession 期间不可变, 并且 MUST 包含会话期间可能暴露的任何视图, 包括一开始可能不活动辅助视图

视图列表 中的主视图 MUST 由 XRSessionmode已授予特性集合确定:

onend 属性是 end 事件类型的事件处理器 IDL 属性

oninputsourceschange 属性是 inputsourceschange 事件类型的事件处理器 IDL 属性

onselectstart 属性是 selectstart 事件类型的事件处理器 IDL 属性

onselectend 属性是 selectend 事件类型的事件处理器 IDL 属性

onselect 属性是 select 事件类型的事件处理器 IDL 属性

onsqueezestart 属性是 squeezestart 事件类型的事件处理器 IDL 属性

onsqueezeend 属性是 squeezeend 事件类型的事件处理器 IDL 属性

onsqueeze 属性是 squeeze 事件类型的事件处理器 IDL 属性

4.2. XRRenderState

XRRenderState 表示一组可配置值,这些值会影响 XRSession 的 输出如何被合成。给定 XRSession活动渲染状态只能在帧边界之间改变,并且可以通过 updateRenderState() 将更新排队。

dictionary XRRenderStateInit {
  double depthNear;
  double depthFar;
  boolean passthroughFullyObscured;
  double inlineVerticalFieldOfView;
  XRWebGLLayer? baseLayer;
  sequence<XRLayer>? layers;
};

[SecureContext, Exposed=Window] interface XRRenderState {
  readonly attribute double depthNear;
  readonly attribute double depthFar;
  readonly attribute boolean? passthroughFullyObscured;
  readonly attribute double? inlineVerticalFieldOfView;
  readonly attribute XRWebGLLayer? baseLayer;
};

每个 XRRenderState 都有一个 输出画布,它是一个初始设为 nullHTMLCanvasElement输出画布是将显示为内联 会话渲染的任何内容的 DOM 元素。

每个 XRRenderState 还有一个 composition enabled 布尔值,其初始值为 true。如果渲染命令是针对由 API 提供并由 XR 合成器显示的表面执行,则认为该 XRRenderState 具有 composition enabled。如果为内联会话执行的渲染会以 直接显示到输出画布的方式进行,则该 XRRenderStatecomposition enabled 标志 MUST 为 false

注:此时,XRRenderState 只有在其 composition enabled 被设为 false 时才会拥有输出画布,但本规范未来版本可能会引入 设置输出画布的方法,以支持镜像和层合成等需要合成的更高级用途。

当为一个 XRSession session 创建 XRRenderState 对象时,用户代理 MUST 通过运行以下步骤来初始化渲染状态

  1. statesession相关 realm中的一个 XRRenderState 对象。

  2. statedepthNear 初始化为 0.1

  3. statedepthFar 初始化为 1000.0

  4. statepassthroughFullyObscured 初始化为 false

  5. 按如下方式初始化 stateinlineVerticalFieldOfView

    如果 session 是一个内联会话

    stateinlineVerticalFieldOfView 初始化为 PI * 0.5

    否则:

    stateinlineVerticalFieldOfView 初始化为 null

  6. statebaseLayer 初始化为 null

depthNear 属性定义近裁剪平面距 观看者的距离,单位为米。depthFar 属性定义远裁剪平面距 观看者的距离,单位为米。

depthNeardepthFar 用于计算 XRViewprojectionMatrix。 当 projectionMatrix 用于渲染时,只有与观看者的距离落在 depthNeardepthFar 之间的几何体才会被绘制。它们还决定 XRWebGLLayer 深度缓冲区的值如何被解释。depthNear MAY 大于 depthFar

注:通常,在构造用于渲染的透视 投影矩阵时,开发者会指定视锥体以及近、远裁剪平面。在显示到 沉浸式 XR 设备时,正确的视锥体由所使用的光学器件、显示器和摄像头的某种组合来确定。 不过,近裁剪平面和远裁剪平面可由应用修改,因为合适的值取决于所渲染内容的类型。

passthroughFullyObscured 属性是 作者提供给 XRSystem 的 一个提示,用于表明他们打算用虚拟内容完全覆盖视口。一旦视口不再被不透明像素覆盖, 作者 SHOULD 将此标志重新设为 false

注:XRSystem MAY 将其用作临时禁用透视的提示。在具有透视光学器件的设备上,用户会继续看到其环境, 此标志不会产生影响。

inlineVerticalFieldOfView 属性 定义在为 "inline" XRSession 计算投影矩阵时使用的默认垂直视场,单位为弧度。投影矩阵计算还会考虑 输出画布的宽高比。 对于启用了 inline-stereo内联会话,用户代理 MUST 使用输出画布几何来计算每个视图的投影矩阵。对于 沉浸式会话,此值 MUST 为 null

baseLayer 属性定义一个 XRWebGLLayerXR 合成器将从中获取图像。

4.3. 动画帧

XRSession 提供有关 XR 设备跟踪状态信息的主要方式,是通过在 XRSession 实例上调用 requestAnimationFrame() 来调度的回调。

callback XRFrameRequestCallback = undefined (DOMHighResTimeStamp time, XRFrame frame);

每个 XRFrameRequestCallback 对象都有一个 cancelled 布尔值,初始设为 false

每个 XRSession 都有一个 动画帧回调列表, 初始为空;一个 当前正在运行的动画帧回调列表, 也初始为空;以及一个 动画 帧回调标识符,它是一个初始为零的数字。

requestAnimationFrame(callback) 方法将 callback 排队,以便在用户代理下一次希望为设备运行动画帧时运行。

调用此方法时,用户代理 MUST 运行以下步骤:

  1. sessionthis

  2. 如果 sessionended 值为 true,则返回 0 并中止这些步骤。

  3. session动画帧回调标识符增加 一。

  4. callback 追加到 session动画帧回调列表中, 并与 session动画帧回调标识符的 当前值相关联。

  5. 返回 session动画帧回调标识符的 当前值。

cancelAnimationFrame(handle) 方法取消一个现有动画帧回调,该回调由其动画帧回调标识符 handle 给定。

调用此方法时,用户代理 MUST 运行以下步骤:

  1. sessionthis

  2. session动画帧回调列表session当前正在运行的动画帧回调列表中, 查找与值 handle 相关联的条目。

  3. 如果存在这样的条目,则将其 cancelled 布尔值设为 true,并 将其从 session动画帧回调列表中移除。

为了使用 renderState state 检查层 状态,用户代理 MUST 运行以下步骤:
  1. 如果 statebaseLayernull,则返回 false

  2. 返回 true

注:WebXR layers module 将为 此算法引入新的语义。

为了确定是否应为 XRSession session 渲染一帧,用户代理 MUST 运行以下步骤:
  1. 如果使用 sessionrenderState 检查层状态false,则返回 false

  2. 如果 session 是一个内联会话,并且 sessionrenderState输出画布null,则返回 false

  3. 返回 true

当一个 XRSession sessionXR 设备接收到时间戳 frameTime 的更新后观看者状态时,它会运行一个 XR 动画帧,无论 动画帧回调列表是否为空,该动画帧都 MUST 运行以下步骤:

  1. 排队一个任务以执行以下步骤:

    1. now当前高分辨率时间

    2. framesession动画 帧

    3. frametime 设为 frameTime

    4. framepredictedDisplayTime 设为 frameTime

    5. 如果 session 是一个沉浸式会话,则将 framepredictedDisplayTime 设为 XR 合成器预计显示此 XR 动画帧的平均时间戳。

    6. 对于 视图列表中的每个 view,将 viewviewport modifiable 标志设为 true。

    7. 如果 视图列表中任何视图active 标志自上一个 XR 动画帧以来已改变,则更新视口

    8. 如果该帧应为 session 渲染:

      1. session当前 正在运行的动画帧回调列表设为 session动画帧 回调列表

      2. session动画帧 回调列表设为空列表。

      3. frameactive 布尔值设为 true

      4. frame 应用帧更新

      5. 按顺序,对于 session当前 正在运行的动画帧回调列表中的每个 entry

      6. 如果 entrycancelled 布尔值为 true,则继续处理下一个条目。

      7. 以 « now, frame » 和 "report" 调用 entry

      8. session当前 正在运行的动画帧回调列表设为空 列表

      9. frameactive 布尔值设为 false

    9. 如果 session待处理渲染状态不是 null,则应用待处理渲染状态

Window 接口的 requestAnimationFrame() 方法的行为不会因任何活动的 XRSession 的存在而改变, 在任何 XRSession 上调用 requestAnimationFrame() 也不会以任何方式与 WindowrequestAnimationFrame() 交互。如果一个活动沉浸式会话导致页面被遮挡,则它 MAY 影响 浏览上下文渲染机会。如果 2D 浏览器视图在 活动沉浸式会话期间可见(即,当会话运行在 tethered headset 上时),使用 WindowrequestAnimationFrame()requestIdleCallback() 运行的回调时序 MAY NOT 与会话的 requestAnimationFrame() 一致,并且用户不应依赖它来渲染 XR 内容。

注:如果在通过 WindowrequestAnimationFrame() 调度的回调期间调用了 XRSessionrequestAnimationFrame(), 用户代理可能希望向开发者控制台显示警告,因为如果活动沉浸式会话 影响了浏览上下文渲染机会,则这些回调并不保证会发生,而且即使运行, 也可能没有正确的时序。

如果一个沉浸式 会话阻止了渲染机会,那么提供给 Window requestAnimationFrame() 的回调可能在会话处于活动状态时不会被处理。这取决于所使用设备的类型,并且最可能发生在移动或独立设备上, 在这些设备中沉浸式内容会完全遮挡 HTML 文档。因此,开发者不得依赖 Window requestAnimationFrame() 回调来调度 XRSession requestAnimationFrame() 回调,反之亦然,即使它们共享相同的渲染逻辑也是如此。不遵循此指导的应用可能无法在所有平台上正确执行。 对于希望在这两类动画循环之间转换的应用,更有效的模式如下所示:
let xrSession = null;

function onWindowAnimationFrame(time) {
  window.requestAnimationFrame(onWindowAnimationFrame);

  // This may be called while an immersive session is running on some devices,
  // such as a desktop with a tethered headset. To prevent two loops from
  // rendering in parallel, skip drawing in this one until the session ends.
  if (!xrSession) {
    renderFrame(time, null);
  }
}

// The window animation loop can be started immediately upon the page loading.
window.requestAnimationFrame(onWindowAnimationFrame);

function onXRAnimationFrame(time, xrFrame) {
  xrSession.requestAnimationFrame(onXRAnimationFrame);
  renderFrame(xrFrame.predictedDisplayTime, xrFrame);
}

function renderFrame(time, xrFrame) {
  // Shared rendering logic.
}

// Assumed to be called by a user gesture event elsewhere in code.
async function startXRSession() {
  xrSession = await navigator.xr.requestSession('immersive-vr');
  xrSession.addEventListener('end', onXRSessionEnded);
  // Do necessary session setup here.
  // Begin the session's animation loop.
  xrSession.requestAnimationFrame(onXRAnimationFrame);
}

function onXRSessionEnded() {
  xrSession = null;
}

使用内联会话向 HTML 文档进行渲染的应用,不需要采取任何特殊步骤来协调动画循环, 因为在沉浸式会话处于活动状态时, 用户代理会自动暂停任何内联会话的动画循环。

4.4. XR 合成器

用户代理 MUST 维护一个 XR 合成器,它处理向 XR 设备的呈现和帧时序。 合成器 MUST 使用一个独立的渲染上下文,其状态与文档创建的任何图形上下文的状态相隔离。 合成器 MUST 防止页面破坏合成器状态,或回读来自其他页面或应用的内容。合成器还 MUST 在单独线程或 进程中运行,以将页面性能与以适当帧率向用户呈现新图像的能力解耦。合成器 MAY 在渲染内容上合成额外的设备或 用户代理 UI,例如设备菜单。

注:本规范未来的扩展也可能利用该 合成器来合成来自同一页面的多个层。

5. 帧 循环

5.1. XRFrame

XRFrame 表示一个 XRSession 的所有被跟踪对象状态的快照。应用可以通过在 XRSession 上用 XRFrameRequestCallback 调用 requestAnimationFrame() 来获取 XRFrame。 当回调被调用时,会传入一个 XRFrame。 需要传达跟踪状态的事件,例如 select 事件,也会提供一个 XRFrame

[SecureContext, Exposed=Window] interface XRFrame {
  [SameObject] readonly attribute XRSession session;
  readonly attribute DOMHighResTimeStamp predictedDisplayTime;

  XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
  XRPose? getPose(XRSpace space, XRSpace baseSpace);
};

每个 XRFrame 都有 一个 active 布尔值,初始设为 false,以及一个 animationFrame 布尔值,初始设为 false

session 属性返回生成该 XRFrameXRSession

对于沉浸式 会话predictedDisplayTime 属性 MUST 返回与此 XRFrame 预计显示在设备显示器上的平均时间点相对应的 DOMHighResTimeStamp。 对于内联会话predictedDisplayTime MUST 返回与传递给 XRFrameRequestCallback 的时间戳相同的值。

predictedDisplayTime 旨在允许以该帧显示时应处的状态来渲染动画 XR 场景,而不是以 requestAnimationFrame() 回调被调度或执行时的状态来渲染。

predictedDisplayTime 并非旨在用于推断应用有多少时间进行渲染,因为在帧提交后,XR 合成器通常还需要进行额外处理。 如果体验假定它可以一直处理到 predictedDisplayTimeXR 合成器将无法使用提交的帧, 应用也将无法达到目标帧率。

每个 XRFrame 都表示给定 time 下所有被跟踪对象的状态,并且要么存储, 要么能够查询该状态在此 time 的具体信息。

getViewerPose(referenceSpace) 方法 以 XRViewerPose 的形式提供观看者相对于 referenceSpace 的姿态,时间为该 XRFrametime

调用此方法时,用户代理 MUST 运行以下步骤:

  1. framethis

  2. sessionframesession 对象。

  3. 如果 frameanimationFrame 布尔值为 false,则抛出 InvalidStateError 并中止这些步骤。

  4. posesession相关 realm中的一个 XRViewerPose 对象。

  5. referenceSpace 中,于 frame 表示的时间,将 session观看者参考空间的姿态 填充到 pose pose 中,并将 force emulation 设为 true

  6. 如果 posenull,则返回 null

  7. xrviews 为空列表

  8. offset0

  9. 对于 session 上的 视图列表中的每个 活动 视图 view, 执行以下步骤:

    1. xrviewsession相关 realm中的一个新 XRView 对象。

    2. xrviewunderlying view 初始化为 view

    3. xrvieweye 初始化为 vieweye

    4. xrviewindex 初始化为 offset

    5. xrviewframe 初始化为 frame

    6. xrviewsession 初始化为 session

    7. xrviewreference space 初始化为 referenceSpace

    8. viewtransformsession相关 realm中一个新的 XRRigidTransform 对象,等于 view视图偏移中的值。

    9. xrviewtransform 属性设为在 session 的相关 realm 中,将 XRViewerPosetransformviewtransform 变换相乘所得的结果。

    10. xrview 追加xrviews

    11. offset 增加 1

  10. poseviews 设为 xrviews

  11. 返回 pose

getPose(space, baseSpace) 方法 以 XRPose 的形式, 在 XRFrame 表示的时间, 提供 space 相对于 baseSpace 的姿态。

调用此方法时,用户代理 MUST 运行以下步骤:

  1. framethis

  2. poseframe相关 realm中的一个 XRPose 对象。

  3. baseSpace 中,于 frame 表示的时间,将 space 的 pose 填充pose 中。

  4. 返回 pose

帧更新是一种在给定 XRFrame 时可运行的算法, 旨在每个 XRFrame 运行。

每个 XRSession 都有 一个 帧更新列表,它是由帧更新组成的 列表,初始为空 列表

为了为 XRFrame frame 应用帧更新,用户代理 MUST 运行以下步骤:

  1. 对于 framesession帧更新列表中的每个 frame update, 执行以下步骤:

    1. frame 运行 frame update

注:本规范未定义任何 帧更新,但其他 规范可能会添加一些。

6. 空间

WebXR 设备 API 的一项核心特性是提供空间跟踪的能力。空间是一种接口, 使应用能够推理被跟踪实体与用户物理环境以及彼此之间的空间关系。

6.1. XRSpace

XRSpace 表示一个具有原点的虚拟坐标系,该原点对应于一个物理位置。从 API 请求或提供给 API 的空间数据, 始终在特定 XRSpace 中, 于特定 XRFrame 的时间表达。 姿态位置等数值是在该空间中相对于其原点的坐标。该接口有意保持不透明。

[SecureContext, Exposed=Window] interface XRSpace : EventTarget {

};

每个 XRSpace 都有一个 session,它被设为创建该 XRSpaceXRSession

每个 XRSpace 都有一个 native origin,它是空间中的一个位置和朝向。XRSpacenative origin 可以由 XR 设备的底层跟踪系统更新,并且不同的 XRSpace 可以定义 不同的语义,说明其 native origins 如何被跟踪和更新。

每个 XRSpace 都有 一个 effective origin,它是该 XRSpace坐标系的基础。

从有效空间到 native origin 空间的变换,由一个 origin offset 定义,它是一个初始设为恒等变换XRRigidTransform。 换言之,可以通过将 origin offsetnative origin 相乘来获得 effective origin

XRSpaceeffective origin 只能在另一个 XRSpace 的坐标系中, 作为由 XRFramegetPose() 方法返回的 XRPose 被观察到。XRSpace 之间的 空间关系 MAY 在不同 XRFrame 之间发生变化。

为了在一个 XRSpace baseSpace 中,于一个 XRFrame frame 表示的时间,将一个 XRSpace spacepose 填充到一个 XRPose pose 中,并带有可选的 force emulation 标志,用户代理 MUST 运行以下 步骤:

  1. 如果 frameactive 布尔值为 false,则抛出 InvalidStateError 并中止这些步骤。

  2. sessionframesession 对象。

  3. 如果 spacesession 不等于 session,则抛出 InvalidStateError 并中止这些步骤。

  4. 如果 baseSpacesession 不等于 session,则抛出 InvalidStateError 并中止这些步骤。

  5. 检查是否可以报告姿态,如果不能,则抛出 SecurityError 并中止这些步骤。

  6. 如果 sessionvisibilityState"visible-blurred", 且 spacebaseSpaceXRInputSource 相关联,则将 pose 设为 null 并中止这些步骤。

  7. limit 为在 spacebaseSpace 之间是否 必须限制姿态的结果。

  8. transformposetransform

  9. 查询 XR 设备的 跟踪系统,以获取 spaceframetime 相对于 baseSpace 的姿态, 然后执行以下步骤:

    如果 limitfalse,并且跟踪系统为 space 相对于 baseSpace 的姿态提供了一个位置被主动跟踪或静态已知的 6DoF 姿态:

    transformorientation 设为 spaceeffective originbaseSpace坐标 系中的朝向。

    transformposition 设为 spaceeffective originbaseSpace坐标 系中的位置。

    如果支持,将 poselinearVelocity 设为 spaceeffective origin 相对于 baseSpace坐标 系的线速度。

    如果支持,将 poseangularVelocity 设为 spaceeffective origin 相对于 baseSpace坐标 系的角速度。

    poseemulatedPosition 设为 false

    否则,如果 limitfalse,并且跟踪系统提供了一个 3DoF 姿态,或一个其位置既未被主动跟踪也非静态已知的 6DoF 姿态,用于表示 space 相对于 baseSpace 的姿态:

    transformorientation 设为 spaceeffective originbaseSpace坐标 系中的朝向。

    transformposition 设为跟踪系统对 spaceeffective originbaseSpace坐标系中位置的最佳估计。这 MAY 包括 诸如颈部或手臂模型之类的计算偏移。如果位置估计不可用, 则 MUST 使用最后已知位置。

    poselinearVelocity 设为 null

    poseangularVelocity 设为 null

    poseemulatedPosition 设为 true

    否则,如果 space 相对于 baseSpace 的姿态过去已被确定, 且 force emulationtrue

    transformposition 设为 spaceeffective originbaseSpace坐标 系中的最后已知位置。

    transformorientation 设为 spaceeffective originbaseSpace坐标 系中的最后已知朝向。

    poselinearVelocity 设为 null

    poseangularVelocity 设为 null

    poseemulatedPosition 布尔值设为 true

    否则:

    pose 设为 null

注:XRPoseemulatedPosition 布尔值并不表示 baseSpace 的位置是否被模拟,而只表示评估 space 相对于 baseSpace 的位置是否依赖模拟。例如,具有 3DoF 跟踪的控制器,在其 targetRaySpacegripSpace 针对 XRReferenceSpace 查询时,会报告 emulatedPositiontrue 的姿态;但如果在 gripSpace 中查询 targetRaySpace 的姿态,则会报告 emulatedPositionfalse,因为这两个空间之间的关系应当是精确已知的。

6.2. XRReferenceSpace

XRReferenceSpace 是若干常见 XRSpace 之一, 应用可以用它来建立与用户物理环境的空间关系。

XRReferenceSpace 通常预期在 XRSession 持续期间保持静态, 最常见的例外是用户在会话中重新配置。每个 XRReferenceSpacenative origin 描述一个坐标系, 其中 +X 被视为“右”,+Y 被视为“上”,-Z 被视为“前”。

enum XRReferenceSpaceType {
  "viewer",
  "local",
  "local-floor",
  "bounded-floor",
  "unbounded"
};

[SecureContext, Exposed=Window]
interface XRReferenceSpace : XRSpace {
  [NewObject] XRReferenceSpace getOffsetReferenceSpace(XRRigidTransform originOffset);

  attribute EventHandler onreset;
};

每个 XRReferenceSpace 都有一个 type,它是一个 XRReferenceSpaceType

XRReferenceSpace 最常通过调用 requestReferenceSpace() 获得;如果传入调用的 XRReferenceSpaceType enum 值受支持,它会创建一个 XRReferenceSpace 实例(或扩展它的接口)。该类型指明该参考空间将表现出的跟踪行为:

注:假定底层平台关于参考空间 Y 轴的约定在不同类型的 XRReferenceSpace 之间保持一致。换言之,如果 XR 系统支持多个参考空间,则在创建它们的 XRSession 持续期间,它们的 Y 轴将彼此平行并指向同一方向。这不适用于 "viewer", 后者不依赖底层平台的朝向约定。"unbounded" 参考空间在其原点相近时应使其 Y 轴与其他参考空间对齐,但如果用户移动了很长距离,则可能发生偏离。

支持 "local" 参考空间的设备 MUST 支持 "local-floor" 参考空间,必要时通过模拟实现,反之亦然。

onreset 属性是 reset 事件类型的事件处理器 IDL 属性

当为 XRSession session 请求一个类型为 XRReferenceSpaceType typeXRReferenceSpace 时,用户代理 MUST 通过运行以下步骤来创建参考空间

  1. 按如下方式初始化 referenceSpace

    如果 typebounded-floor

    referenceSpacesession相关 realm中的一个 XRBoundedReferenceSpace

    否则:

    referenceSpacesession相关 realm中的一个 XRReferenceSpace

  2. referenceSpacetype 初始化为 type

  3. referenceSpacesession 初始化为 session

  4. 返回 referenceSpace

为了检查给定参考空间类型 typeXRSession session 是否支持参考空间,运行以下步骤:
  1. 如果 type包含session已授予特性集合中,则返回 false

  2. 如果 typeviewer, 则返回 true

  3. 如果 typelocallocal-floor, 且 session 是一个沉浸式会话,则返回 true

  4. 如果 typelocallocal-floor, 且 XR 设备支持报告朝向数据,则返回 true

  5. 如果 typebounded-floorsession 是一个沉浸式会话,则返回 XR 设备是否支持有界参考空间的结果。

  6. 如果 typeunboundedsession 是一个沉浸式会话,并且 XR 设备支持在用户附近进行 无限制距离的稳定跟踪,则返回 true

  7. 返回 false

getOffsetReferenceSpace(originOffset) 方法在调用时 MUST 执行以下步骤:
  1. base 为调用此方法的 XRReferenceSpace

  2. 按如下方式初始化 offsetSpace

    如果 baseXRBoundedReferenceSpace 的实例:

    offsetSpacebase相关 realm中的一个 XRBoundedReferenceSpace, 并将 offsetSpaceboundsGeometry 设为 baseboundsGeometry, 其中每个点都乘以 originOffsetinverse

    否则:

    offsetSpacebase相关 realm中的一个 XRReferenceSpace

  3. offsetSpacetype 设为 basetype

  4. offsetSpaceorigin offset 设为在 base相关 realm中,将 baseorigin offsetoriginOffset 相乘所得的结果。

  5. 返回 offsetSpace

注:预计一些应用会使用 getOffsetReferenceSpace() 来实现基于鼠标、键盘、触摸或游戏手柄输入的场景导航控制。这会导致 getOffsetReferenceSpace() 被频繁调用,在活动输入期间至少每帧调用一次。因此,强烈鼓励 UA 使通过 getOffsetReferenceSpace() 创建新的 XRReferenceSpace 成为轻量级操作。

6.3. XRBoundedReferenceSpace

XRBoundedReferenceSpace 扩展了 XRReferenceSpace, 以包含 boundsGeometry, 用于指示用户空间的预配置边界。

[SecureContext, Exposed=Window]
interface XRBoundedReferenceSpace : XRReferenceSpace {
  readonly attribute FrozenArray<DOMPointReadOnly> boundsGeometry;
};

XRBoundedReferenceSpace 的原点 MUST 位于地板处,使得 Y 轴在地板高度等于 0XZ 的位置和朝向根据底层平台的约定初始化,通常预期位于房间中心附近,并朝向合乎逻辑的前方 方向。

注:其他 XR 平台有时会将 bounded-floor 参考空间提供的跟踪类型称为“房间尺度”跟踪。XRBoundedReferenceSpace 并非旨在描述多房间空间、地板高度不平的区域,或非常大的开放区域。 需要处理这些场景的内容应使用 unbounded 参考空间。

每个 XRBoundedReferenceSpace 都有一个 native bounds geometry,用于描述 XRBoundedReferenceSpace 周围的边界,用户可以预期在该边界内安全移动。该多边形边界以 DOMPointReadOnly 数组给出,表示安全空间边缘处的一圈点。这些点描述了相对于 native origin 的偏移,单位为米。从上方向下看、朝向 Y 轴负端时,这些点 MUST 以顺时针顺序给出。 每个点的 y 值 MUST 为 0,并且每个点的 w 值 MUST 为 1。可认为边界源自地板并无限向上延伸。它描述的形状 MAY 为凸形或凹形。

native bounds geometry 中的每个点 MUST 被限制在距参考空间的 native origin 的合理距离内。

注:建议将 native bounds geometry 的点在所有方向上 限制在距 native origin 15 米以内。

native bounds geometry 中的每个点还 MUST 被充分量化,以防止指纹识别。为保证用户安全, 量化后的点值 MUST NOT 落在平台报告的边界之外。

注:建议将 native bounds geometry 的点 量化到最接近的 5cm。

boundsGeometry 属性 是一个由 DOMPointReadOnly 组成的数组,使得每个条目等于 XRBoundedReferenceSpacenative bounds geometry 中对应条目, 预乘以 origin offsetinverse。 换言之,它在相对于 effective originXRBoundedReferenceSpace 坐标中提供同一边界。

如果 native bounds geometry 暂时 不可用,可能原因包括 XR 设备初始化期间、长时间跟踪丢失,或在预配置空间之间移动,则 boundsGeometry MUST 报告为空数组。

为了检查是否支持有界参考空间,运行以下 步骤:
  1. 如果 XR 设备不能报告边界,则返回 false。

  2. 如果 XR 设备不能识别用户物理地板的高度,则返回 false。

  3. 返回 true。

注:如果在请求参考空间时边界或地板高度尚未解析, 但已知 XR 设备支持它们,则可以返回有界参考空间。

注:内容不应要求用户移动到 boundsGeometry 之外。如果用户的物理环境允许,用户可能移动到边界之外,从而产生位于它们所描述多边形之外的位置值。 这不是错误条件,页面内容应优雅地处理。

注:内容通常不应提供 boundsGeometry 的可视化,因为确保向用户提供安全关键信息是用户代理的责任。

7. 视图

7.1. XRViewGeometry

包含 XRViewGeometry interface mixin 的对象,表示 XR 设备用于向用户呈现图像的显示器,或用于收集真实世界视觉信息的传感器。 这些对象包含一个视图几何

视图几何 对应于一组内参和外参,用于在 viewer reference space坐标系中的点与 containing object屏幕空间中的点之间进行转换。

视图几何具有一个 containing object,它是该 视图几何 包含数据所对应的物理硬件部件。

视图几何containing object 具有一个关联的 屏幕空间,它被描述为该 containing object 从中读取数据或向其渲染 数据的 2D 平面。

视图几何具有一个 关联的 视图偏移,它是一个 XRRigidTransform, 描述 containing objectviewer reference space坐标系中的位置和朝向。

注:对视图偏移可能是什么没有约束,并且允许视图具有不同的朝向。 这可能出现在眼部显示器以某个角度居中的头戴式设备中,也可能在更极端的情况中出现,例如 CAVE 渲染。因此,z 排序和剔除等技术可能需要按眼睛分别执行。

视图几何具有一个 关联的 投影矩阵,它是一个矩阵,描述向底层 XR 设备提供的 containing object 进行渲染时要使用的投影。 投影矩阵 MAY 包含诸如剪切之类的变换,使得无法用简单视锥体 准确描述该投影。

注:此矩阵的逆适合用于从 屏幕空间中“读取”像素,并将其转换回以 containing object 为原点的坐标系

[SecureContext, Exposed=Window] interface mixin XRViewGeometry {
  readonly attribute Float32Array projectionMatrix;
  [SameObject] readonly attribute XRRigidTransform transform;
};

每个 XRViewGeometry 都有关联的 内部投影矩阵,用于存储其 containing object投影矩阵。它最初为 null

注:transform 可用于在许多渲染库中定位相机对象。如果应用需要更传统的视图矩阵,可以通过调用 transform.inverse.matrix 来获取。

projectionMatrix 属性是底层 视图几何投影矩阵强烈建议应用在不修改或分解的情况下使用此矩阵。 渲染时未使用所提供的投影矩阵,可能导致呈现的帧失真或严重错位,从而造成不同程度的用户不适。 此属性 MUST 通过为该 XRViewGeometry 获得投影矩阵来计算。

transform 属性是该对象的 XRRigidTransform。 它表示该对象在用于获取该对象的 XRReferenceSpace 中的位置和朝向。

为了为给定 XRViewGeometry view geometry 获得投影矩阵

  1. 如果 view geometry内部投影矩阵不是 null,则执行以下步骤:

    1. 如果 内部投影 矩阵上的 IsDetachedBuffer 操作为 false,则返回 view geometry内部投影 矩阵

  2. view geometry内部投影矩阵设为 view geometry相关 realm中的一个 矩阵,其值等于 view geometry投影矩阵

  3. 返回 view geometry内部投影矩阵

7.2. XRView

XRView 描述 给定帧中进入 XR 场景的单个视图

视图对应于 XR 设备 用于向用户呈现图像的显示器或显示器的一部分。它们用于检索渲染内容所需的全部信息,使内容与该 视图的物理输出属性良好对齐,包括视场、眼睛偏移和其他光学属性。 视图可以覆盖用户视觉的重叠区域。 不保证任何 XR 设备使用的视图数量或顺序,也不要求视图数量在 XRSession 持续期间保持不变。

视图具有一个关联的 eye, 它是一个 XREye, 描述该视图预期显示给哪只眼睛。如果该视图没有内在关联的眼睛(例如显示器是单目的),则此值 MUST 被设为 "none"

视图具有一个 active 标志,该标志可能在 XRSession 的生命周期中变化。 Primary views MUST 始终将 active 标志设为 true

注:许多 HMD 会请求内容渲染两个 视图,一个用于左眼,一个用于右眼,而大多数 magic window 设备只会请求一个视图,但应用绝不应假定特定的视图配置。例如:magic window 设备如果支持立体输出, 可能请求两个视图,但如果关闭立体输出模式,出于性能原因也可能退回到请求单个视图。同样,HMD 可能会请求两个以上的视图,以支持宽视场或不同像素密度的显示器。

视图具有一个内部 viewport modifiable 标志,用于指示此时会话中是否可以通过 requestViewportScale() 调用更改视口缩放。它在动画帧开始时被设为 true,并在调用 getViewport() 时设为 false

视图具有一个内部 requested viewport scale 值,表示该视图请求的视口缩放。它最初设为 1.0,并且如果系统支持动态视口缩放, 可由 requestViewportScale() 方法修改。

视图具有一个内部 current viewport scale 值,表示系统内部用于此视图的当前视口缩放。它最初设为 1.0。当通过 getViewport() 调用成功应用视口更改时,它会更新为匹配 requested viewport scale

视图具有一个 reference space, 它是在 getViewerPose() 中用于获取此视图XRReferenceSpace 空间。

注:动态视口缩放允许应用使用每个动画帧都可以更改的缩放因子, 渲染到完整大小视口的一个子集。其意图是在不重新分配的情况下,以每帧为基础高效修改。为正确渲染, XR 系统和应用必须对活动视口达成一致。应用可以在单个动画帧内对一个 XRView 多次调用 requestViewportScale(), 但请求的缩放直到应用为该视图调用 getViewport() 才会生效。动画帧中的第一次 getViewport 调用会应用更改(立即对当前动画帧生效), 锁定该视图在此动画帧剩余期间的当前缩放视口,并将该缩放设为未来动画帧的新默认值。 可选地,系统可以通过 recommendedViewportScale 属性,基于内部性能启发式和目标帧率提供建议值。

enum XREye {
  "none",
  "left",
  "right"
};

[SecureContext, Exposed=Window] interface XRView {
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  readonly attribute double? recommendedViewportScale;

  undefined requestViewportScale(double? scale);
};

XRView includes XRViewGeometry;

transform 在其 reference space 中给出。

eye 属性描述底层视图eye。此属性的主要目的,是确保预渲染的立体 内容可以将内容的正确部分呈现给正确的眼睛。

index 属性描述当此 XRViewgetViewerPose()views 数组中返回时的偏移。

可选的 recommendedViewportScale 属性包含 UA 推荐的视口缩放值,应用可将其用于 requestViewportScale() 调用,以配置动态视口缩放。如果系统未实现用于确定推荐缩放的启发式或方法,则它为 null。如果不为 null,则该值 MUST 是大于 0.0 且小于或等于 1.0 的数值,并且 MUST 被量化,以避免提供详细的性能或 GPU 利用率 数据。

注:建议通过将推荐的视口缩放舍入到一小组可能缩放值中最接近的值, 并使用滞后来避免接近边界值时立即发生变化,从而量化推荐的视口缩放。(这也有助于避免快速振荡的缩放值, 因为这可能造成视觉干扰或不适。)

每个 XRView 都有 一个关联的 session,它是生成该对象的 XRSession

每个 XRView 都有 一个关联的 frame,它是生成该对象的 XRFrame

每个 XRView 都有 一个关联的 underlying view,它是其所表示的底层视图

requestViewportScale(scale) 方法请求用户代理将此视口的 requested viewport scale 设置为所请求的值。

当此方法在一个 XRView xrview 上调用时,用户代理 MUST 运行以下步骤:

  1. 如果 scale 为 null 或 undefined,则中止这些步骤。

  2. 如果 scale 小于或等于 0.0,则中止这些步骤。

  3. 如果 scale 大于 1.0,则将 scale 设为 1.0。

  4. viewxrviewunderlying view

  5. viewrequested viewport scale 值设为 scale

注:该方法会忽略 null 或 undefined 缩放 值,使应用即使在系统不提供推荐缩放时,也能安全地使用 view.requestViewportScale(view.recommendedViewportScale)

视图列表中任何 视图active 标志改变时, 可以通过执行以下步骤为 XRSession session 更新 视口

  1. layerrenderStatebaseLayer

  2. 如果 layernull,则中止这些步骤。

  3. layer视口对象列表设为空列表

  4. 对于 视图列表中的每个 活动 视图 view

    1. viewport 为从与 sessionview 关联的 完整大小视口列表获得缩放视口所得的 XRViewport 结果。

    2. viewport 追加layer视口对象列表

为了为给定 XRView viewXRSession session 获得缩放视口

  1. glFullSizedViewport 为来自与 view 关联的 完整大小视口列表WebGL viewport

  2. scaleviewcurrent viewport scale

  3. 用户代理 MAY 选择钳制 scale,以应用最小视口缩放因子。

  4. glViewport 为一个新的 WebGL viewport

  5. glViewportwidth 设为小于或等于 glFullSizedViewportwidth 乘以 scale 的整数值。

  6. 如果 glViewportwidth 小于 1,则将其设为 1。

  7. glViewportheight 设为小于或等于 glFullSizedViewportheight 乘以 scale 的整数值。

  8. 如果 glViewportheight 小于 1,则将其设为 1。

  9. glViewportx 分量设为一个整数值,该值位于 glFullSizedViewportx 分量(含)与 glFullSizedViewportx 分量加上 glFullSizedViewportwidth 再减去 glViewportwidth(含)之间。

  10. glViewporty 分量设为一个整数值,该值位于 glFullSizedViewporty 分量(含)与 glFullSizedViewporty 分量加上 glFullSizedViewportheight 再减去 glViewportheight(含)之间。

  11. viewportsession相关 realm中的一个 XRViewport

  12. viewportx 初始化为 glViewportx 分量。

  13. viewporty 初始化为 glViewporty 分量。

  14. viewportwidth 初始化为 glViewportwidth

  15. viewportheight 初始化为 glViewportheight

  16. 返回 viewport

注:具体整数值的计算有意留给 UA 自行决定。 直接向下舍入 width/height 并按原样使用 xy 偏移是有效的, 但 UA MAY 也选择在指定约束内略作调整的值,例如为了效率将视口对齐到 2 的幂像素网格。 缩放视口 MUST 完全包含在完整大小视口内,但 MAY 按 UA 自行决定放置在完整大小视口内的任意位置。 大小和位置计算 MUST 是确定性的,并且在会话内对相同输入值返回一致结果。

7.3. 主视图和次视图

当为了 XR 体验必须向某个 视图渲染时,该视图就是 主视图主 视图 MUST 在整个 XRSession 持续期间保持活动

当内容可以选择不向某个 视图渲染且仍能产生可用的 沉浸式体验时,该视图就是 次视图。当内容选择不向这些视图渲染时, 用户代理 MAY 能够通过重投影重建它们。除非启用了 "secondary-views" 特性,否则次视图 MUST NOT 为 活动

主视图的示例包括手持式 AR 会话的主单目视图、 头戴式 AR/VR 会话的主双立体视图,或启用了 inline-stereo内联会话的主双立体视图, 或 CAVE 会话的所有墙面视图。

次 视图的示例包括用于视频捕获的 第一人称观察者视图,或每只眼睛有两个不同分辨率和视场的视图的 “quad views”。

虽然内容应编写为假定可能存在任意数量的视图,但我们预期大量内容会对 views 数组做出错误假设,因此在呈现超过两个视图时会出错。

由于用户代理可能具备使用重投影等机制来替代内容渲染这些 次视图的能力, 因此最好能够区分哪些内容计划自行处理这些 次视图, 以及哪些内容要么不知道这些 次视图的存在,要么不希望处理它们。

为支持这一点,暴露 次视图的用户代理 MUST 支持 "secondary-views" 特性描述符作为提示。启用此特性的内容预期会:

当启用 "secondary-views" 时,用户代理 MAY 在必要时向 XRSession 公开设备支持的任何次视图。 在这种情况下,用户代理 MUST NOT 使用重投影来重建次视图,而应依赖内容决定渲染的任何内容。

注:我们建议内容使用 optionalFeatures 启用 "secondary-views",以确保最大兼容性。

如果次视图具有较低的 底层帧率,则 XRSession MAY 选择执行以下一项或多项:

7.4. XRViewport

XRViewport 对象描述图形表面的视口,或矩形区域。

[SecureContext, Exposed=Window] interface XRViewport {
  readonly attribute long x;
  readonly attribute long y;
  readonly attribute long width;
  readonly attribute long height;
};

xy 属性定义相对于表面原点的偏移,而 widthheight 属性定义视口的矩形尺寸。

视口值的确切解释取决于与该视口关联的图形 API 的约定:

以下代码遍历一个 XRViewerPose 的所有 XRView, 为每一个从 XRWebGLLayer 查询一个 XRViewport, 并用它们设置用于渲染的适当 WebGL viewport
xrSession.requestAnimationFrame((time, xrFrame) => {
  const viewer = xrFrame.getViewerPose(xrReferenceSpace);

  gl.bindFramebuffer(xrWebGLLayer.framebuffer);
  for (xrView of viewer.views) {
    let xrViewport = xrWebGLLayer.getViewport(xrView);
    gl.viewport(xrViewport.x, xrViewport.y, xrViewport.width, xrViewport.height);

    // WebGL draw calls will now be rendered into the appropriate viewport.
  }
});

8. 几何基元

8.1. 矩阵

WebXR 以 矩阵的形式提供各种变换。WebXR 在传递矩阵时使用 WebGL 约定,其中 4x4 矩阵作为 16 元素的 Float32Array 给出,采用列优先存储,并通过从左侧预乘矩阵应用到列向量。它们可以直接传递给 WebGL 的 uniformMatrix4fv 函数,用于创建等效的 DOMMatrix, 或与各种第三方数学库一起使用。

WebXR 设备 API 返回的矩阵将是一个 16 元素的 Float32Array, 布局如下:
[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15]

将此矩阵作为变换应用于一个指定为 DOMPointReadOnly 的列向量,如下:

{x:X, y:Y, z:Z, w:1}

会产生以下结果:

a0 a4 a8  a12  *  X  =  a0 * X + a4 * Y +  a8 * Z + a12
a1 a5 a9  a13     Y     a1 * X + a5 * Y +  a9 * Z + a13
a2 a6 a10 a14     Z     a2 * X + a6 * Y + a10 * Z + a14
a3 a7 a11 a15     1     a3 * X + a7 * Y + a11 * Z + a15

8.2. 归一化

有若干算法要求对向量或四元数进行归一化,这意味着缩放其各分量,使其总体大小为 1.0

为了归一化一个分量列表, UA MUST 执行以下步骤:

  1. length 为各分量平方之和的平方根。

  2. 如果 length0,则抛出 InvalidStateError 并中止这些步骤。

  3. 将每个分量除以 length 并设置该分量。

8.3. XRRigidTransform

XRRigidTransform 是由 positionorientation 描述的变换。在解释 XRRigidTransform 时,orientation 始终先于 position 应用。

XRRigidTransform 包含一个 内部矩阵,它是一个矩阵

[SecureContext, Exposed=Window]
interface XRRigidTransform {
  constructor(optional DOMPointInit position = {}, optional DOMPointInit orientation = {});
  [SameObject] readonly attribute DOMPointReadOnly position;
  [SameObject] readonly attribute DOMPointReadOnly orientation;
  readonly attribute Float32Array matrix;
  [SameObject] readonly attribute XRRigidTransform inverse;
};

XRRigidTransform(position, orientation) 构造函数在调用时 MUST 执行以下步骤:

  1. transform新的 XRRigidTransform 对象,位于当前 realm 中。

  2. transformposition新的 DOMPointReadOnly, 位于当前 realm 中。

  3. 如果 positionw 值不是 1.0,则抛出 TypeError 并中止这些步骤。

  4. 如果 positionorientation 的一个或多个值为 NaN 或 其他非有限数,例如 infinity,则抛出 TypeError 并中止这些步骤。

  5. transformpositionx 值设为 position 的 x 字典成员,将 y 值设为 position 的 y 字典成员,将 z 值设为 position 的 z 字典成员,并将 w 值设为 position 的 w 字典成员。

  6. transformorientation新的 DOMPointReadOnly, 位于当前 realm 中。

  7. transformorientationx 值设为 orientation 的 x 字典成员,将 y 值设为 orientation 的 y 字典成员,将 z 值设为 orientation 的 z 字典成员,并将 w 值设为 orientation 的 w 字典成员。

  8. transform内部矩阵null

  9. 归一化 transformorientationxyzw 分量。

  10. 返回 transform

position 属性是一个三维点,以米为单位给出, 描述该变换的平移分量。positionw 属性 MUST 为 1.0

orientation 属性是描述该变换旋转分量的四元数。 orientation MUST 被归一化,使其长度为 1.0

matrix 属性以矩阵形式返回由 positionorientation 属性描述的变换。此属性 MUST 通过为该 XRRigidTransform 获得矩阵来计算。

注:当此矩阵预乘到列向量上时,会先按照 orientation 描述的 3D 旋转来旋转该向量,然后再按 position 对其进行平移。在数学上的列向量表示法中,这是 M = T * R,其中 T 是对应于 position 的平移矩阵,而 R 是对应于 orientation 的旋转矩阵。

为了为给定 XRRigidTransform transform 获得矩阵

  1. 如果 transform内部矩阵不是 null, 则执行以下步骤:

    1. 如果 内部矩阵上的 IsDetachedBuffer 操作为 false,则返回 transform内部矩阵

  2. translation 为新的矩阵,该矩阵是对应于 position 的列向量平移矩阵。数学上,如果 position(x, y, z),则该矩阵为

    列向量平移矩阵的数学表达式

  3. rotation 为新的矩阵,该矩阵是对应于 orientation 的列向量旋转矩阵。数学上,如果 orientation 是单位四元数 (qx, qy, qz, qw),则该矩阵为

    列向量旋转矩阵的数学表达式

  4. transform内部矩阵设为 transform相关 realm中的一个 Float32Array, 其值是在 transform相关 realm中,将 translationrotation 相乘且 translation 在左侧(translation * rotation)的结果。 数学上,此矩阵为

    平移与旋转相乘且平移在左侧所得矩阵的数学表达式

  5. 返回 transform内部矩阵

XRRigidTransform transforminverse 属性返回一个位于 transform 的相关 realm 中的 XRRigidTransform, 如果将其应用于之前已由 transform 变换过的对象,它会撤销该变换并将对象恢复到其初始姿态。 此属性 SHOULD 惰性求值。由 inverse 返回的 XRRigidTransform MUST 将 transform 作为其 inverse 返回。

一个 XRRigidTransform, 其 position{ x: 0, y: 0, z: 0 w: 1 },且其 orientation{ x: 0, y: 0, z: 0, w: 1 },称为 恒等变换

为了在 Realm realm将两个 XRRigidTransform 相乘BA,UA MUST 执行以下步骤:

  1. resultrealm 中一个新的 XRRigidTransform 对象。

  2. resultmatrix 设为 realm 中一个新的 Float32Array, 其值为将 Bmatrix 从左侧预乘到 Amatrix 上的结果。

  3. resultorientation 设为 realm 中一个新的 DOMPointReadOnly, 其值为描述 resultmatrix 左上 3x3 子矩阵所表示旋转的四元数。

  4. resultposition 设为 realm 中一个新的 DOMPointReadOnly, 其值为 resultmatrix 第四列给出的向量。

  5. 返回 result

result 是从 A 的源空间到 B 的目标空间的变换。

注:这等价于构造一个 XRRigidTransform, 其 orientationAB 的朝向组合,且其 position 等于 ApositionBorientation 旋转后,再加上 Bposition

9. 姿态

9.1. XRPose

XRPose 描述 空间中相对于一个 XRSpace 的 位置和朝向。

[SecureContext, Exposed=Window] interface XRPose {
  [SameObject] readonly attribute XRRigidTransform transform;
  [SameObject] readonly attribute DOMPointReadOnly? linearVelocity;
  [SameObject] readonly attribute DOMPointReadOnly? angularVelocity;

  readonly attribute boolean emulatedPosition;
};

transform 属性描述相对于基准 XRSpace 的 位置和朝向。

linearVelocity 属性描述相对于基准 XRSpace 的线速度, 单位为米每秒。如果用户代理无法填充它,则允许返回 null

angularVelocity 属性描述相对于基准 XRSpace 的角速度, 单位为弧度每秒。如果用户代理无法填充它,则允许返回 null

transform 表示基于传感器读数的主动跟踪 6DoF 姿态时, emulatedPosition 属性为 false; 如果其 position 值包含一个计算偏移,例如由颈部或手臂模型提供的偏移,则为 true。在确定一个 XRPose 是否包含 计算 偏移时,MUST NOT 考虑估计地板 高度

9.2. XRViewerPose

XRViewerPose 是一个 XRPose, 描述由 XR 设备跟踪的 XR 场景 观看者状态。观看者可以表示被跟踪的硬件部件、相对于硬件观察到的用户位置, 或计算进入 XR 场景的一系列视点的其他方式。XRViewerPose 只能相对于 XRReferenceSpace 查询。除了 XRPose 值之外, 它还提供一个视图数组,其中包含刚性变换, 用于指示视点和投影矩阵。应用在渲染 XR 场景的一帧时应使用这些值。

[SecureContext, Exposed=Window] interface XRViewerPose : XRPose {
  [SameObject] readonly attribute FrozenArray<XRView> views;
};

views 数组是一个 XRView 序列, 描述 XR 场景的视点,这些视点相对于查询该 XRViewerPose 时所用的 XRReferenceSpace。 为了在 XR 设备上正确显示,数组中 XR 场景的每个 视图都必须被渲染。每个 XRView 都包含 刚性变换,用于指示视点和投影矩阵,并且在需要时可用于从层中查询 XRViewport

注:XRViewerPosetransform 可用于为场景的旁观者视图或多用户交互定位观看者的图形表示。

10. 输入

10.1. XRInputSource

XRInputSource 表示一个 XR 输入 源,它是任何允许用户在与观看者相同的 虚拟空间中执行定向操作的输入机制。XR 输入源的示例 包括但不限于手持控制器、光学跟踪的手,以及基于注视、并基于观看者姿态 运行的输入方法。未明确关联到 XR 设备的输入机制,例如传统游戏手柄、鼠标或键盘,SHOULD NOT 被 视为 XR 输入 源

enum XRHandedness {
  "none",
  "left",
  "right"
};

enum XRTargetRayMode {
  "gaze",
  "tracked-pointer",
  "screen",
  "transient-pointer"
};

[SecureContext, Exposed=Window]
interface XRInputSource {
  readonly attribute XRHandedness handedness;
  readonly attribute XRTargetRayMode targetRayMode;
  [SameObject] readonly attribute XRSpace targetRaySpace;
  [SameObject] readonly attribute XRSpace? gripSpace;
  [SameObject] readonly attribute FrozenArray<DOMString> profiles;
  readonly attribute boolean skipRendering;
};

注:XRInputSource 接口也由 WebXR 游戏手柄 模块扩展

handedness 属性描述该 XR 输入源关联到哪只手, 如果有关联的话。没有自然 handedness 的输入源(例如头显安装的控制器),或当前 handedness 未知的输入源, MUST 将此属性设为 "none"

targetRayMode 属性描述用于生成目标射线的方法, 并指示应用在需要时应如何向用户呈现目标射线。

targetRaySpace 属性是一个 XRSpace,它具有一个 native origin, 跟踪该 XRInputSource 的首选指向射线的位置和朝向(沿其 -Z 轴),如 targetRayMode 所定义。

对于 targetRayMode"transient-pointer" 的输入源,targetRaySpace 表示交互开始时指向交互目标的射线。其姿态应在此 XRInput 的 gripSpace 内保持静态。

gripSpace 属性是一个 XRSpace,它具有一个 native origin, 跟踪应当用于渲染虚拟对象的姿态,使这些对象看起来像被握在用户手中。如果用户握着一根直杆,则此 XRSpacenative origin 放在其弯曲手指的质心处,并使 -Z 轴沿着杆的长度指向其拇指。X 轴垂直于所描述的手背, 用户右手手背指向 +X,用户左手手背指向 -XY 轴由 XZ 轴之间的关系隐含,其中 +Y 大致指向用户手臂的方向。

skipRendering 属性指示此输入是可见的,并且 MAY NOT 需要由当前会话渲染。如果 skipRendering 为 true 且 targetRayMode 为 "tracked-pointer",则用户代理 MUST 确保始终向用户显示该 XR 输入源的表示。

向用户显示控制器的示例包括控制器位于显示器和用户之间、显示器是透明的,或控制器由操作系统渲染。

skipRendering 是给开发者的提示,表示不要渲染控制器等输入源。拾取射线和光标仍应被渲染。

对于 targetRayMode"transient-pointer" 的输入源,如果存在关联的用户手势,则 gripSpace 应为该关联用户手势,否则应为用户控制的另一个空间,例如 ViewerSpace,或另一个 XRInput 的 gripSpace 或 targetRaySpace。这样做是为了允许用户仍然操纵 targetRaySpace。

如果输入源并非固有可跟踪,例如 targetRayMode"gaze""screen" 的输入源,则 gripSpace MUST 为 null

profiles 属性是一个列表,包含若干 输入配置文件 名称,用于指示输入源的首选视觉表示和行为。

输入配置文件名称是一个 ASCII 小写的 DOMString, 不包含空格,并用连字符(-)连接分隔的单词。应选择描述性名称,并尽可能使用设备供应商首选的措辞。 如果平台提供了适当的标识符,例如 USB 供应商和产品 ID,则 MAY 使用它。MUST NOT 使用唯一标识单个设备的值, 例如序列号。输入配置文件名称 MUST NOT 包含设备 handedness 的指示。 如果多个用户代理暴露同一设备,它们 SHOULD 努力报告相同的 输入配置文件名称WebXR 输入 配置文件注册表是管理输入配置文件 名称的推荐位置。

Profiles 按具体性降序给出。列表中第一个条目之后给出的任何输入配置文件 名称都应提供 fallback 值,表示设备的替代表示。这可以包括设备的更通用或先前版本、足够相似且更广泛认可的设备, 或设备类型的宽泛描述(例如 "generic-trigger-touchpad")。如果给出多个 profiles,则它们描述的布局必须都表示 列表中其他每个 profile 的超集或子集。

如果 XRSession 是 一个内联会话,则 profiles MUST 是一个空列表。

用户代理 MAY 自行决定只报告适当的通用输入配置文件 名称或空列表。适合这样做的一些场景包括:输入设备无法被可靠识别、没有已知输入 profiles 与输入设备匹配, 或用户代理希望掩盖正在使用的输入设备。

例如,Samsung HMD Odyssey 的控制器是标准 Windows Mixed Reality 控制器的设计变体。两个控制器共享相同的输入布局。 因此,Samsung HMD Odyssey 控制器的 profiles 可以是: ["samsung-odyssey", "microsoft-mixed-reality", "generic-trigger-squeeze-touchpad-thumbstick"]。 控制器外观由列表中的第一个 profile 最精确地传达,第二个 profile 描述可接受的替代项,最后一个 profile 是通用 fallback,以最粗略的意义描述该设备。(它是一个带有扳机、握压按钮、触摸板和摇杆的控制器。)
同样,Valve Index 控制器向后兼容 HTC Vive 控制器,但 Index 控制器有额外的按钮和轴。因此,Valve Index 控制器的 profiles 可以是: ["valve-index", "htc-vive", "generic-trigger-squeeze-touchpad-thumbstick"]。在这种情况下, "valve-index" profile 描述的输入布局是 "htc-vive" profile 描述的布局的超集。 此外,"valve-index" profile 指示控制器的精确外观,而 "htc-vive" 控制器具有显著不同的外观。 在这种情况下,UA 会认为这种差异是可接受的。并且与第一个示例一样,最后一个 profile 是通用 fallback。
(确切字符串仅为示例。实际 profile 名称在 WebXR 输入 配置文件注册表中管理。)

注:XRInputSourceXRSessioninputSources 数组中是“live”的。因此,它们内部的值会被就地更新。这意味着保存某一帧上 XRInputSource 的 属性引用,并在后续帧中将其与同一属性比较以测试状态变化,是不可行的,因为它们将是同一个对象。因此, 希望逐帧比较输入状态的开发者应复制相关状态的内容。

如果一个 XR 输入源 支持 主要操作, 则它是一个 主要输入 源主要操作是一种平台特定操作,当其被触发时,会产生 selectstartselectendselect 事件。可能的主要操作示例包括按下扳机、触摸板或按钮,说出命令,或做出手势。如果平台指南定义了推荐的 主要输入,则应将其用作主要 操作,否则用户代理可自由选择一个。设备 MUST 支持至少一个主要输入 源

如果一个 XR 输入源不支持 主要操作,则它是一个 被跟踪输入 源。这些输入主要旨在提供姿态数据。 注:被跟踪输入源的一个示例是用于用户腿部或道具的跟踪附件。如果未执行手势识别来检测 主要 操作,则被跟踪的手也可被视为被跟踪输入源

XRSession session 的一个 XR 输入 源 source 开始其主要操作时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsessiontime 为该操作发生的时间。

  2. 排队一个任务,以触发输入 源事件,其名称为 selectstart, frame 为 frame,source 为 source

XRSession session 的一个 XR 输入 源 source 结束其主要操作时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsessiontime 为该操作发生的时间。

  2. 排队一个任务,以执行以下步骤:

    1. 触发输入源事件,其名称为 select, frame 为 frame,source 为 source

    2. 触发输入源事件,其名称为 selectend, frame 为 frame,source 为 source

每个 XR 输入源 MAY 定义一个 主要 squeeze 操作主要 squeeze 操作是一种平台特定操作,当其被触发时,会产生 squeezestartsqueezeendsqueeze 事件。主要 squeeze 操作应用于大致映射为挤压或抓取的操作。可能的 主要 squeeze 操作示例包括 按下 grip trigger 或做出抓取手势。如果平台指南定义了推荐的主要 squeeze 操作,则应将其用作 主要 squeeze 操作,否则用户代理 MAY 选择一个。

XRSession session 的一个 XR 输入 源 source 开始其主要 squeeze 操作时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsessiontime 为该操作发生的时间。

  2. 排队一个任务,以触发输入 源事件,其名称为 squeezestart, frame 为 frame,source 为 source

XRSession session 的一个 XR 输入 源 source 结束其主要 squeeze 操作时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsessiontime 为该操作发生的时间。

  2. 排队一个任务,以执行以下步骤:

    1. 触发输入源事件,其名称为 squeeze, frame 为 frame,source 为 source

    2. 触发输入源事件,其名称为 squeezeend, frame 为 frame,source 为 source

有时,平台特定行为可能导致主要操作主要 squeeze 操作被 中断或取消。例如,一个 XR 输入源可能在主要操作主要 squeeze 操作开始之后、结束之前,从 XR 设备中被移除。

XRSession session 的一个 XR 输入 源 source主要操作被取消时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsessiontime 为该操作发生的时间。

  2. 排队一个任务,以触发输入 源事件,触发一个名称为 selectendXRInputSourceEvent, frame 为 frame,source 为 source

XRSession session 的一个 XR 输入 源 source主要 squeeze 操作被取消时,UA MUST 运行以下 步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsessiontime 为该操作发生的时间。

  2. 排队一个任务,以触发输入 源事件,触发一个名称为 squeezeendXRInputSourceEvent, frame 为 frame,source 为 source

10.2. 瞬态输入

一些 XR 设备可能支持 瞬态输入 源,其中 XR 输入源只有在执行一个 瞬态操作时才有意义, 该操作可以是 主要操作 (针对主要输入 源),也可以是设备特定的 辅助操作(针对被跟踪输入源)。

一个示例是针对内联会话的鼠标、触摸或触控笔输入,它 MUST 产生一个瞬态 XRInputSource, 其 targetRayMode 设为 screen, 并被视为主要操作 (针对 主指针),以及非主指针的非主要 辅助操作

另一个示例是来自操作系统的意图,其输入派生自无法直接暴露的敏感信息,例如基于注视的交互。这些会产生一个瞬态 XRInputSource, 其 targetRayMode 设为 transient-pointer, 并被视为主要 操作

瞬态输入 源只在瞬态操作持续期间存在于会话的 活动 XR 输入源列表中。

瞬态输入 源在处理瞬态操作时遵循以下序列,而不是用于非瞬态 主要操作的算法:

XRSession session 的一个 瞬态输入源 source 开始其 瞬态操作时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsession,时间为该操作发生的时间。

  2. 排队一个任务,以执行以下步骤:

    1. 如有必要,触发由 XR 输入源操作产生的任何 "pointerdown" 事件。

    2. XR 输入源添加活动 XR 输入源列表

    3. 如果 瞬态操作是一个 主要操作,则触发输入源事件,其名称为 selectstart, frame 为 frame,source 为 source

XRSession session 的一个 瞬态输入源 source 结束其 瞬态操作时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsession,时间为该操作发生的时间。

  2. 排队一个任务,以执行以下步骤:

    1. 如果 瞬态操作是一个 主要操作,则触发输入源事件,其名称为 select, frame 为 frame,source 为 source

    2. 如有必要,触发由 XR 输入源操作产生的任何 "click" 事件。

    3. 如果 瞬态操作是一个 主要操作,则触发输入源事件,其名称为 selectend, frame 为 frame,source 为 source

    4. 活动 XR 输入源列表移除 XR 输入源

    5. 如有必要,触发由 XR 输入源操作产生的任何 "pointerup" 事件。

XRSession session 的一个 瞬态输入源 source瞬态操作被取消时,UA MUST 运行以下步骤:

  1. framesession相关 realm中的一个 XRFrame, 其 sessionsession,时间为该操作发生的时间。

  2. 排队一个任务,以执行以下步骤:

    1. 如果 瞬态操作是一个 主要操作,则触发输入源事件,其名称为 selectend, frame 为 frame,source 为 source

    2. 活动 XR 输入源列表移除 XR 输入源

    3. 如有必要,触发由 XR 输入源操作产生的任何 "pointerup" 事件。

10.3. XRInputSourceArray

XRInputSourceArray 表示一个由 XRInputSource 组成的列表。 当列表内容预期会随时间变化时使用它, 而不是使用 冻结 数组类型,例如 XRSessioninputSources 属性。

[SecureContext, Exposed=Window]
interface XRInputSourceArray {
  iterable<XRInputSource>;
  readonly attribute unsigned long length;
  getter XRInputSource(unsigned long index);
};

XRInputSourceArraylength 属性指示该 XRInputSourceArray 中包含多少个 XRInputSource

XRInputSourceArray索引属性 getter 会检索所提供索引处的 XRInputSource

11.

注:虽然本规范只定义了 XRWebGLLayer 层,但预计规范的未来扩展会添加更多层类型以及它们所绘制的图像源。

11.1. XRLayer

[SecureContext, Exposed=Window]
interface XRLayer : EventTarget {};

XRLayerXRWebGLLayer 和未来扩展引入的其他层类型的基类。

11.2. XRWebGLLayer

XRWebGLLayer 是一个提供 WebGL framebuffer 以供渲染的层,使硬件加速的 3D 图形渲染能够呈现在 XR 设备上。

typedef (WebGLRenderingContext or
         WebGL2RenderingContext) XRWebGLRenderingContext;

dictionary XRWebGLLayerInit {
  boolean antialias = true;
  boolean depth = true;
  boolean stencil = false;
  boolean alpha = true;
  boolean ignoreDepthValues = false;
  double framebufferScaleFactor = 1.0;
};

[SecureContext, Exposed=Window]
interface XRWebGLLayer: XRLayer {
  constructor(XRSession session,
             XRWebGLRenderingContext context,
             optional XRWebGLLayerInit layerInit = {});
  // Attributes
  readonly attribute boolean antialias;
  readonly attribute boolean ignoreDepthValues;
  attribute float? fixedFoveation;

  [SameObject] readonly attribute WebGLFramebuffer? framebuffer;
  readonly attribute unsigned long framebufferWidth;
  readonly attribute unsigned long framebufferHeight;

  // Methods
  XRViewport? getViewport(XRView view);

  // Static Methods
  static double getNativeFramebufferScaleFactor(XRSession session);
};

每个 XRWebGLLayer 都有一个 context 对象,初始为 null,它是 WebGLRenderingContextWebGL2RenderingContext 的实例。

每个 XRWebGLLayer 都有关联的 session,它是创建该对象时所用的 XRSession

XRWebGLLayer(session, context, layerInit) 构造函数在调用时 MUST 执行以下步骤:

  1. layersession相关 realm中的一个 XRWebGLLayer

  2. 如果 sessionended 值为 true,则抛出 InvalidStateError 并中止这些步骤。

  3. 如果 context is lost,则抛出 InvalidStateError 并中止这些步骤。

  4. 如果 session 是一个沉浸式会话,并且 contextXR compatible 布尔值为 false,则抛出 InvalidStateError 并中止这些步骤。

  5. layercontext 初始化为 context

  6. layersession 初始化为 session

  7. 按如下方式初始化 layerignoreDepthValues

    如果 layerInitignoreDepthValues 值为 falseXR Compositor 将使用深度值:

    layerignoreDepthValues 初始化为 false

    否则:

    layerignoreDepthValues 初始化为 true

  8. 按如下方式初始化 layercomposition enabled 布尔值:

    如果 session 是一个内联会话

    layercomposition enabled 初始化为 false

    否则:

    layercomposition enabled 布尔值初始化为 true

  9. 如果 layercomposition enabled 布尔值为 true
    1. layerantialias 初始化为 layerInitantialias 值。

    2. scaleFactorlayerInitframebufferScaleFactor

    3. 用户代理 MAY 在此处选择按其认为合适的方式钳制或舍入 scaleFactor, 例如,如果它出于性能原因希望将缓冲区尺寸适配为 2 的幂。

    4. framebufferSize推荐的 WebGL framebuffer 分辨率,其宽度和高度分别乘以 scaleFactor

    5. layerframebuffer 初始化为 context相关 realm中的一个 WebGLFramebuffer, 它是一个由 context 创建、尺寸为 framebufferSize不透明 framebuffer,其 session 初始化为 session,并使用 layerInitdepthstencilalpha 值。

    6. 根据支持 layer 合成的需要,分配并初始化与 sessionXR 设备兼容的资源,包括 GPU 可访问 内存缓冲区。

    7. 如果 layer 的资源因任何原因无法创建,则抛出 OperationError 并中止这些步骤。

    否则:
    1. layerantialias 初始化为 layercontext实际上下文参数中的 antialias 值。

    2. layerframebuffer 初始化为 null

  10. 返回 layer

注:如果一个 XRWebGLLayercomposition enabled 布尔值被设为 false,则 XRWebGLLayerInit 对象上的所有值都会被忽略,因为 WebGLRenderingContext 的 默认 framebuffer 已经使用上下文的实际上下文参数分配,且不能被覆盖。

context 属性是创建该 XRWebGLLayer 时所用的 WebGLRenderingContext

每个 XRWebGLLayer 都有一个 composition enabled 布尔值,初始设为 true。如果设为 false,则表示该 XRWebGLLayer MUST NOT 分配自己的 WebGLFramebuffer, 并且 XRWebGLLayer 上所有反映 framebuffer 属性的属性 MUST 改为反映 context 的默认 framebuffer 的属性。

XRWebGLLayerframebuffer 属性是一个 WebGLFramebuffer 实例;如果 composition enabledtrue,它已被标记为不透明,否则为 null。在 XRWebGLLayer 创建之后,开发者不能调整 framebuffer 大小。

不透明 framebuffer 的功能与标准 WebGLFramebuffer 相同,但有以下更改,使其行为更像默认 framebuffer

注:用户代理需要尊重 depthstenciltrue 值,这类似于 WebGL 在创建绘图缓冲区时的行为。

附加到不透明 framebuffer的缓冲区 MUST 在首次创建时,或在处理每个 XR 动画帧之前,被清除为下表中的值。 这与 WebGL 上下文的默认 framebuffer行为相同。无论关联 WebGL 上下文的 preserveDrawingBuffer 值如何,不透明 framebuffers 都将始终被清除。

缓冲区 清除值
颜色 (0, 0, 0, 0)
深度 1.0
模板 0

注:实现可以优化掉不透明 framebuffer所需的隐式清除操作,只要能保证开发者无法访问来自另一进程的 缓冲区内容。例如,如果开发者执行了显式清除,则不需要隐式清除。

如果一个 XRWebGLLayer 创建时 alpha 被设为 true,则 framebuffer 必须由具有 RGBA 颜色格式的纹理支持。 如果一个 XRWebGLLayer 创建时 alpha 被设为 false,则 framebuffer 必须由具有 RGB 颜色格式的纹理支持。

但是,XR Compositor MUST 将 framebuffer 的 backing 像素视为处于 SRGB8_ALPHA8SRGB8 colorFormat 中。

注:这意味着 XR Compositor 在处理作为 framebuffer backing 的纹理时,MUST 不对线性 RGBARGB 执行任何 gamma 转换。否则,最终渲染中的像素会显得过亮,这将与常规 2D WebGLRenderingContext 上下文中的渲染不匹配。

当一个 XRWebGLLayer 被设置为一个沉浸式 会话baseLayer 时,不透明 framebuffer 的内容会在一个 XR 动画 帧完成后立即呈现给 沉浸式 XR 设备,但仅当自前一个 XR 动画帧以来至少发生以下任一情况:

不透明 framebuffer呈现给沉浸式 XR 设备之前,用户代理应确保所有渲染操作都已刷新到 不透明 framebuffer

每个 XRWebGLLayer 都有一个 target framebuffer,如果 composition enabledtrue,它就是 framebuffer, 否则是 context 的 默认 framebuffer。

framebufferWidthframebufferHeight 属性分别返回 target framebuffer 附件的宽度和高度。

antialias 属性在 target framebuffer 使用 UA 选择的技术支持抗锯齿时为 true,如果不会执行抗锯齿则为 false

ignoreDepthValues 属性如果为 true,则表示 XR Compositor MUST NOT 在渲染时使用深度缓冲区附件中的值。 当该属性为 false 时,表示深度缓冲区附件的内容将由 XR Compositor 使用,并预期能代表渲染到该层中的场景。

存储在缓冲区中的深度值预期位于 0.01.0 之间,其中 0.0 表示 depthNear 的距离,1.0 表示 depthFar 的距离,中间值线性插值。这是 WebGL 的默认行为。(更多细节见 depthRange 函数文档。)

注:使场景的深度缓冲区可供 compositor 使用,可允许某些平台提供质量和舒适度改进, 例如改进的重投影。

fixedFoveation 属性控制 XR Compositor 使用的 foveation 量。如果用户代理或设备不支持此属性,它们在获取时应返回 null,设置时应为 no-op。将 fixedFoveation 设为小于 0 的值会将其设为 0,设为大于 1 的值会将其设为 10 设置最小的 foveation 量,而 1 设置最大值。XR Compositor 如何解释这些值由用户代理决定。 如果 fixedFoveation 级别发生变化,它将在下一个 XRFrame 生效。

注:Fixed foveation 是一种降低用户视场边缘附近内容渲染分辨率的技术。 它可以显著改善受限于 GPU 填充性能的体验。它会降低功耗,并使应用能够提高眼部纹理的分辨率。它最适用于低对比度纹理, 例如背景图像,而较不适用于高对比度内容,例如文本或详细图像。作者可以按帧调整级别,以实现性能和视觉质量之间的最佳权衡。

每个 XRWebGLLayer MUST 有一个 完整大小视口列表,它是一个列表,包含一个 WebGL viewport,对应 XRSession 可能暴露的每个 视图,包括当前未活动但可能在当前会话中变为 活动次 视图。这些视口 MUST 具有大于 0widthheight, 并且 MUST 描述一个不超过 target framebuffer 边界的矩形。这些视口 MUST NOT 重叠。

如果 composition enabledfalse,且 session视图 列表包含单个 视图,其 eye"none", 则 完整大小视口列表 MUST 包含单个 WebGL viewport,覆盖 context 的整个默认 framebuffer。

如果 composition enabledfalse,且 session视图 列表包含两个主 视图,其中一个的 eye"left", 另一个的 eye"right", 则 完整大小视口列表 MUST 包含两个 WebGL viewport:一个关联到 "left" 视图,另一个关联到 "right" 视图。用户代理在受上述约束限制的情况下确定这些视口的大小和 位置。

注:本规范不要求立体内联视口采用特定打包方式,例如并排排列。 作者应为每个 XRView 使用 getViewport(), 而不是假定某种布局。

每个 XRWebGLLayer MUST 有一个 视口对象 列表,它是一个列表,包含一个 XRViewport, 对应 XRSession 当前暴露的每个活动 视图

getViewport() 查询给定 XRView 在渲染到该层时应使用的 XRViewport

getViewport(view) 方法在 XRWebGLLayer layer 上调用时,MUST 运行以下步骤:

  1. sessionviewsession

  2. framesessionanimation frame

  3. 如果 session 不等于 layersession,则抛出 InvalidStateError 并中止这些步骤。

  4. 如果 frameactive 布尔值为 false,则抛出 InvalidStateError 并中止这些步骤。

  5. 如果 viewframe 不等于 frame,则抛出 InvalidStateError 并中止这些步骤。

  6. 如果 viewport modifiable 标志为 true,且 viewrequested viewport scale 不等于 current viewport scale

    1. current viewport scale 设置为 requested viewport scale

    2. 视口对象列表中,将与 view 关联的 XRViewport 设为从与 sessionview 关联的 完整大小视口列表获得缩放视口所得的 XRViewport 结果。

  7. viewviewport modifiable 标志设为 false。

  8. viewport视口对象列表中与 view 关联的 XRViewport

  9. 返回 viewport

注:viewport modifiable 标志会被有意设为 false, 即使 current viewport scale 没有变化也是如此。这确保 getViewport(view) 调用在一个动画帧内对于该视图总是返回一致的结果, 因此第一个检索到的值会在该帧剩余期间被锁定。如果应用在 getViewport() 之后调用 requestViewportScale(), 则请求的值只会在未来帧中再次调用 getViewport() 时才被应用。

每个 XRSession MUST 标识一个 原生 WebGL framebuffer 分辨率,即为匹配 XR 设备的物理像素分辨率而需要的 WebGL framebuffer 像素分辨率。

一个 XRSession session原生 WebGL framebuffer 分辨率通过运行以下步骤确定:

  1. 如果 session 是一个内联会话,则将 原生 WebGL framebuffer 分辨率设为 sessionrenderStateoutput canvas 的物理显示像素大小, 每次 canvas 大小发生变化或output canvas 发生变化时重新评估这些步骤,并中止这些步骤。

  2. 原生 WebGL framebuffer 分辨率设为如下所需的分辨率: 一个足够大、能够包含会话所有 XRView 的 framebuffer 的像素,与最高放大倍率下显示区域中的物理屏幕像素之间具有 1:1 比率。如果不存在如上所述确定原生分辨率的方法, MAY 使用 推荐的 WebGL framebuffer 分辨率

此外,XRSession MUST 标识一个 推荐的 WebGL framebuffer 分辨率,它表示一个最佳估计的 WebGL framebuffer 分辨率:足够大以包含会话所有 XRView, 并为普通应用在性能和质量之间提供良好平衡。它 MAY 小于、大于或等于 原生 WebGL framebuffer 分辨率。新的 不透明 framebuffer 将使用此分辨率创建,其宽度和高度分别按任何所提供的 XRWebGLLayerInitframebufferScaleFactor 缩放。

注:用户代理可自由使用其选择的任何方法来估计 推荐的 WebGL framebuffer 分辨率。如果存在用于查询推荐大小的平台特定方法, 建议使用它们,但并非必须。framebufferScaleFactorgetNativeFramebufferScaleFactor() 使用的缩放因子分别应用于宽度和高度,因此缩放因子为二会导致总体像素数变为四倍。如果平台暴露基于面积的渲染缩放, 且它基于像素数,则用户代理需要对其取平方根,以将其转换为 WebXR 缩放因子。

getNativeFramebufferScaleFactor(session) 方法在调用时 MUST 运行以下步骤:

  1. sessionthis

  2. 如果 sessionended 值为 true,则返回 0.0 并中止这些步骤。

  3. 返回一个值,使 session推荐的 WebGL framebuffer 分辨率的宽度和高度分别乘以该值后,得到 session原生 WebGL framebuffer 分辨率

11.3. WebGL 上下文兼容性

为了让 WebGL 上下文可用作沉浸式 XR 图像的源,它必须为沉浸式 XR 设备创建在一个 兼容的 图形适配器上。什么被视为兼容的图形 适配器取决于平台,但可理解为该图形适配器能够在没有过度延迟的情况下向 沉浸式 XR 设备提供图像。如果 WebGL 上下文尚未创建在 兼容的图形适配器上,则在它能够与 XRWebGLLayer 一起使用之前,通常必须在相关适配器上重新创建它。

注:在只有单个 GPU 的 XR 平台上,可以安全地假定该 GPU 与平台公布的 沉浸式 XR 设备兼容,因此任何硬件加速的 WebGL 上下文也同样兼容。 在同时具有集成 GPU 和独立 GPU 的 PC 上,独立 GPU 通常被视为兼容的图形适配器,因为它通常是性能更高的芯片。在安装了多个图形适配器的台式 PC 上,与沉浸式 XR 设备物理连接的那个适配器可能会被视为兼容的图形 适配器

注:内联会话使用与 canvas 相同的图形适配器进行渲染, 因此不需要 xrCompatible 上下文。

partial dictionary WebGLContextAttributes {
    boolean xrCompatible = false;
};

partial interface mixin WebGLRenderingContextBase {
    [NewObject] Promise<undefined> makeXRCompatible();
};

当用户代理实现本规范时,它 MUST 在每个 WebGLRenderingContextBase 上设置一个 XR compatible 布尔值,初始设为 false。 一旦 XR compatible 布尔值 被设为 true,该上下文就可与从当前沉浸式 XR 设备请求的任何 XRSession 的层一起使用。

注:此标志会引入缓慢的同步行为,因此不建议使用。请考虑改为调用 makeXRCompatible() 作为异步解决方案。

XR compatible 布尔值可以在 上下文创建时设置,也可以在上下文创建之后设置,后者可能导致上下文丢失。要在上下文创建时设置 XR compatible 布尔值, 在请求 WebGL 上下文时,xrCompatible 上下文创建属性必须设为 true。如果发起请求的文档的 origin 不允许使用 "xr-spatial-tracking" 权限策略,则 xrCompatible 没有效果。

xrCompatible 标志位于 WebGLContextAttributes 上;如果为 true,它会通过请求用户代理为沉浸式 XR 设备使用兼容的图形适配器创建 WebGL 上下文,从而影响上下文创建。如果用户代理成功执行此操作,则创建的上下文的 XR compatible 布尔值将被设为 true。为了获取 沉浸式 XR 设备,SHOULD 调用确保已选择沉浸式 XR 设备

注:确保已选择沉浸式 XR 设备需要 并行运行,这会在主线程上引入缓慢的同步行为。用户代理 SHOULD 向控制台打印警告, 要求改用 makeXRCompatible()

以下代码创建一个与沉浸式 XR 设备兼容的 WebGL 上下文, 然后使用它创建一个 XRWebGLLayer
function onXRSessionStarted(xrSession) {
  const glCanvas = document.createElement("canvas");
  const gl = glCanvas.getContext("webgl", { xrCompatible: true });

  loadWebGLResources();

  xrSession.updateRenderState({ baseLayer: new XRWebGLLayer(xrSession, gl) });
}

要在上下文创建之后设置 XR compatible 布尔值,需要使用 makeXRCompatible() 方法。

注:在某些系统上,此标志可能会开启高功率独立 GPU,或者将所有命令代理到设备上的 GPU。例如,如果你处于可能使用也可能不使用 XR 的情形,建议仅在打算启动沉浸式会话时才调用 makeXRCompatible()

makeXRCompatible() 方法确保该 WebGLRenderingContextBase 正在为沉浸式 XR 设备运行在兼容的图形适配器上。

当调用此方法时,用户代理 MUST 运行以下步骤:

  1. 如果发起请求的文档的origin 不允许使用 "xr-spatial-tracking" 权限策略,则resolve promise 并返回它。当 XR 权限策略被禁用时,我们希望在这种情况下表现得仿佛没有 XR 设备, 因为 makeXRCompatible() 应该是一个设置后即忘的方法。

  2. promise 为在此 WebGLRenderingContextBaseRealm 中创建的新 Promise

  3. contextthis

  4. 并行运行以下步骤:

    1. device确保已选择沉浸式 XR 设备的结果。

    2. 按如下方式设置 contextXR compatible 布尔值:

      如果 contextWebGL context lost flag 已设置:

      排队一个任务,将 contextXR compatible 布尔值设为 false,并以 InvalidStateError reject promise

      如果 devicenull

      排队一个任务,将 contextXR compatible 布尔值设为 false,并以 InvalidStateError reject promise

      如果 contextXR compatible 布尔值为 true

      排队一个任务,以resolve promise

      如果 context 是在用于 device兼容的图形适配器上创建的:

      排队一个任务,将 contextXR compatible 布尔值设为 true,并resolve promise

      否则:

      WebGL task source排队一个任务,以执行以下步骤:

      1. 强制 context 丢失。

      2. 按照 WebGL 规范所述处理上下文丢失

        1. canvascontextcanvas

        2. 如果 contextwebgl context lost flag 已设置,则中止这些步骤。

        3. 设置 contextwebgl context lost flag

        4. 设置由 context 创建的每个 WebGLObject 实例的 invalidated 标志。

        5. 禁用除 "WEBGL_lose_context" 之外的所有扩展。

        6. WebGL task source排队一个任务,以执行以下步骤:

          1. canvas触发一个 WebGL 上下文事件 e,名称为 "webglcontextlost",并将 statusMessage 设为 ""。

          2. 如果 ecanceled flag 未设置, 则以 AbortError reject promise 并中止这些步骤。

          3. 并行运行以下步骤。

            1. 等待用于 device兼容的 图形适配器上的可恢复绘图缓冲区。

            2. WebGL task source排队一个任务, 以执行以下步骤:

              1. 在用于 device兼容的 图形适配器恢复 上下文

              2. contextXR compatible 布尔值设为 true

              3. Resolve promise

  5. 返回 promise

此外,当任何 WebGL 上下文丢失时,在触发 "webglcontextlost" 事件之前运行以下步骤:

  1. 将该上下文的 XR compatible 布尔值设为 false

以下代码从一个预先存在的 WebGL 上下文创建一个 XRWebGLLayer
const glCanvas = document.createElement("canvas");
const gl = glCanvas.getContext("webgl");

loadWebGLResources();

glCanvas.addEventListener("webglcontextlost", (event) => {
  // 表示 WebGL 上下文可以恢复。
  event.canceled = true;
});

glCanvas.addEventListener("webglcontextrestored", (event) => {
  // 上下文丢失后需要重新创建 WebGL 资源。
  loadWebGLResources();
});

async function onXRSessionStarted(xrSession) {
  // 确保我们想要使用的 canvas 上下文与设备兼容。
  // 可能触发上下文丢失。
  await gl.makeXRCompatible();
  xrSession.updateRenderState({ baseLayer: new XRWebGLLayer(xrSession, gl) });
}

12. 事件

除非另有规定,本规范中所有排队的 任务源都是 XR 任务源

12.1. XRSessionEvent

XRSessionEvent 会被触发,以指示 XRSession 状态的变化。

[SecureContext, Exposed=Window]
interface XRSessionEvent : Event {
  constructor(DOMString type, XRSessionEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
};

dictionary XRSessionEventInit : EventInit {
  required XRSession session;
};

session 属性指示生成该事件的 XRSession

12.2. XRInputSourceEvent

XRInputSourceEvent 会被触发,以指示 XRInputSource 状态的变化。

[SecureContext, Exposed=Window]
interface XRInputSourceEvent : Event {
  constructor(DOMString type, XRInputSourceEventInit eventInitDict);
  [SameObject] readonly attribute XRFrame frame;
  [SameObject] readonly attribute XRInputSource inputSource;
};

dictionary XRInputSourceEventInit : EventInit {
  required XRFrame frame;
  required XRInputSource inputSource;
};

inputSource 属性指示生成此事件的 XRInputSource

frame 属性是一个 XRFrame,它 对应该事件发生的时间。它可以表示历史数据。当在 frame 上调用 getViewerPose() 时,MUST 抛出异常。

当用户代理必须以名称 nameXRFrame frameXRInputSource source 触发输入源事件时,它 MUST 运行以下步骤:

  1. 创建一个 XRInputSourceEvent event,其 typenameframeframe,并且 inputSourcesource

  2. frameactive 布尔值设为 true

  3. frame 应用帧更新

  4. framesession派发 event

  5. frameactive 布尔值设为 false

12.3. XRInputSourcesChangeEvent

XRInputSourcesChangeEvent 会被触发,以指示可供 XRSession 使用的活动 XR 输入源列表的变化。

[SecureContext, Exposed=Window]
interface XRInputSourcesChangeEvent : Event {
  constructor(DOMString type, XRInputSourcesChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  [SameObject] readonly attribute FrozenArray<XRInputSource> added;
  [SameObject] readonly attribute FrozenArray<XRInputSource> removed;
};

dictionary XRInputSourcesChangeEventInit : EventInit {
  required XRSession session;
  required sequence<XRInputSource> added;
  required sequence<XRInputSource> removed;

};

session 属性指示生成该事件的 XRSession

added 属性是一个列表,包含在事件发生时被添加到 XRSessionXRInputSource

removed 属性是一个列表,包含在事件发生时从 XRSession 中 移除的 XRInputSource

12.4. XRReferenceSpaceEvent

XRReferenceSpaceEvent 会被触发,以指示 XRReferenceSpace 状态的变化。

[SecureContext, Exposed=Window]
interface XRReferenceSpaceEvent : Event {
  constructor(DOMString type, XRReferenceSpaceEventInit eventInitDict);
  [SameObject] readonly attribute XRReferenceSpace referenceSpace;
  [SameObject] readonly attribute XRRigidTransform? transform;
};

dictionary XRReferenceSpaceEventInit : EventInit {
  required XRReferenceSpace referenceSpace;
  XRRigidTransform? transform = null;
};

referenceSpace 属性 指示生成此事件的 XRReferenceSpace

可选的 transform 属性描述事件后 referenceSpacenative origin 在事件前坐标系中的位置和朝向。如果 XRSystem 无法 确定旧坐标系和新坐标系之间的 delta,则此属性 MAY 为 null

注:referenceSpacereferenceSpace 可能发生的情况包括头显在两个不同位置之间被摘下并重新戴上。在这种情况下,如果体验依赖世界锁定内容,它应警告用户并重置场景。

12.5. XRVisibilityMaskChangeEvent

由于视锥体并不与矩形显示区域精确相交,XRLayer 的整个区域 MAY 不会被显示。此事件会将 XRView 中显示给用户的区域 告知体验。

当用户代理想要通知体验 XRLayer 的显示区域已发生变化时,会触发 XRVisibilityMaskChangeEvent 事件。体验 MAY 选择只绘制该区域,这 MAY 有助于性能。

注:体验 MUST 在 requestSession 的 promise 解析期间注册此事件。否则,该事件可能会触发,而 mask 将对体验丢失。

[SecureContext, Exposed=Window]
interface XRVisibilityMaskChangeEvent : Event {
  constructor(DOMString type, XRVisibilityMaskChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  [SameObject] readonly attribute Float32Array vertices;
  [SameObject] readonly attribute Uint32Array indices;
};

dictionary XRVisibilityMaskChangeEventInit : EventInit {
  required XRSession session;
  required XREye eye;
  required unsigned long index;
  required Float32Array vertices;
  required Uint32Array indices;
};

session 属性指示生成该事件的 XRSession

eye 属性指示该 mask 适用于哪个 XREye

index 属性指示此 mask 所适用的 XRView视图列表中的偏移。

vertices 属性是一个列表,包含 XY 坐标。体验 MUST 假定 Z 坐标为 -1。每个 XYZ 坐标描述一个顶点。 如果此数组为空,则整个 XRView 区域 SHOULD 被绘制。

indices 属性是一个列表,包含若干索引, 描述进入由 vertices 所描述的顶点列表中的索引。这些索引将描述眼睛的 XRView 中 SHOULD 被绘制的区域。 如果此数组为空,则整个 XRView 区域 SHOULD 被绘制。

该区域 MUST 使用 projectionMatrix ,该矩阵来自 eyeXRView, 并使用默认的 XRRigidTransform

注:这意味着该区域 MUST NOT 使用 XRRigidTransform ,该 transform 来自 eye 的当前 XRView

12.6. 事件类型

用户代理 MUST 提供以下新事件。事件的注册和触发必须遵循 DOM Events 的通常行为。

用户代理 MUST 在 XRSystem 对象上 触发事件,该事件名为 devicechange,以指示 沉浸式 XR 设备的可用性已发生变化,除非文档的 origin 不允许使用 "xr-spatial-tracking" 权限策略

每当 XRSessionvisibility state 发生变化时,用户代理 MUST 在一个 XRSession 上使用 XRSessionEvent 触发一个事件,其名称为 visibilitychange

当会话由应用或用户代理结束时,用户代理 MUST 在一个 XRSession 上使用 XRSessionEvent 触发一个事件,其名称为 end

当会话的活动 XR 输入源列表发生变化时,用户代理 MUST 在一个 XRSession 上使用 XRInputSourcesChangeEvent 触发一个事件,其名称为 inputsourceschange

当会话的活动 XR 跟踪源列表发生变化时,用户代理 MUST 在一个 XRSession 上使用 XRInputSourcesChangeEvent 触发一个事件,其名称为 trackedsourceschange。仅当 tracked-sources 包含在会话的 已授予特性集合中时,才 MUST 触发此事件。

当一个 XRSession 的某个 XRInputSource 开始其主要操作时,用户代理 MUST 在该会话上使用 XRInputSourceEvent 触发一个事件,其名称为 selectstart。 该事件 MUST 为 类型。

当一个 XRSession 的某个 XRInputSource 结束其主要操作,或一个已开始 主要 操作XRInputSource 被断开连接时,用户代理 MUST 在该会话上使用 XRInputSourceEvent 触发一个事件,其名称为 selectend

当一个 XRSession 的某个 XRInputSource 已完全完成一个主要 操作时,用户代理 MUST 在该会话上使用 XRInputSourceEvent 触发一个事件,其名称为 select

当一个 XRSession 的某个 XRInputSource 开始其主要 squeeze 操作时,用户代理 MUST 在该会话上使用 XRInputSourceEvent 触发一个事件,其名称为 squeezestart

当一个 XRSession 的某个 XRInputSource 结束其主要 squeeze 操作,或一个已开始 主要 squeeze 操作XRInputSource 被断开连接时,用户代理 MUST 在该会话上使用 XRInputSourceEvent 触发一个事件,其名称为 squeezeend

当一个 XRSession 的某个 XRInputSource 已完全完成一个主要 squeeze 操作时,用户代理 MUST 在该会话上使用 XRInputSourceEvent 触发一个事件,其名称为 squeeze

XR Compositor 改变 XRSession内部标称帧率时,用户代理 MUST 在一个 XRSession 上使用 XRSessionEvent 触发一个事件,其名称为 frameratechange

XRSystem 想要 发出显示给用户的 XRView 可见区域变化的信号时, 用户代理 MUST 在一个 XRSession 上使用 XRVisibilityMaskChangeEvent 触发一个事件,其名称为 visibilitymaskchange

native origineffective origin 发生不连续时, 即 origin 相对于用户环境的位置或朝向发生显著变化时,用户代理 MUST 在一个 XRReferenceSpace 上使用 XRReferenceSpaceEvent 触发一个事件,其名称为 reset。(例如:在用户重新校准其 XR 设备之后,或 XR 设备在丢失并重新获得跟踪后自动移动其 origin 时。)当 XRBoundedReferenceSpaceboundsGeometry 发生变化时,也 MUST 派发 reset 事件。如果 viewer 的姿态发生不连续,但 XRReferenceSpace 的 origin 物理映射保持稳定,则 MUST NOT 派发 reset 事件,例如当 viewer 在同一跟踪区域内短暂丢失并重新获得跟踪时。如果未发生显著不连续, 当一个 unbounded reference space 随时间对其 native origin 做出小调整以维持用户附近的空间稳定性时,也 MUST NOT 派发 reset 事件。该事件 MUST 在执行任何使用新 origin 的 XR 动画 帧之前派发。一个 reference space 触发 reset 事件时,MUST 在其所有 offset reference spaces 上也派发 reset 事件,并且 offset XRBoundedReferenceSpaceboundsGeometry 也应重新计算。

注:这确实意味着会话需要持有对任何具有 reset 监听器的 XRReferenceSpace 的强引用。

注:viewer 位置的跳变可由应用通过观察 emulatedPosition 布尔值来处理。如果 viewer 位置的跳变 与 emulatedPositiontrue 切换到 false 同时发生,这表示 viewer 已重新获得跟踪,并且其新位置表示对先前模拟值的修正。 对于没有“传送”机制的体验,即 viewer 无需物理移动即可在虚拟世界中移动的体验,这通常是应用所期望的行为。但是,如果某个体验确实提供了“传送”机制, 在跟踪恢复后将 viewer 的位置跳回可能会造成不必要的突兀感。 相反,当此类应用恢复跟踪时,它可以通过将位置中的突然跳变吸收到其传送偏移中,简单地从 viewer 在虚拟世界中的当前位置恢复体验。 为此,开发者调用 getOffsetReferenceSpace() 来创建替代 reference space,并按自上一帧以来 viewer 位置跳变的量调整其 effective origin

13. 安全、隐私与舒适性考量

WebXR Device API 提供了强大的新特性,这些特性也带来了若干独特的隐私、安全和舒适性风险, 用户代理必须采取措施加以缓解。

13.1. 敏感信息

在 XR 的上下文中,敏感信息包括但不限于 用户可配置的数据,例如瞳距(IPD),以及基于传感器的数据,例如 XRPose。所有沉浸式会话 都会暴露一定量的敏感数据,因为渲染任何内容都需要用户姿态。 但是,在某些情况下,同样的敏感信息也会通过内联会话暴露。

13.2. 用户意图

给定操作的用户意图 是来自用户的信号,表明此类操作是有意的并且得到了用户同意。

在暴露敏感信息或允许对用户体验产生重大影响的操作之前,通常需要确定用户意图。这种意图可以通过多种方式传达或观察到。

注:确定用户意图的一种常见方式是 UI 控件的瞬态激活,通常是一个“进入 VR”按钮。由于激活是瞬态的, 请求 XR 会话的浏览上下文必须是包含该 UI 控件的上下文的祖先,或同同源域后代,并且必须在近期 曾是该浏览上下文的活动文档

13.2.1. 用户激活

瞬态激活 MAY 在某些场景中作为用户意图的指示。

13.2.2. 启动 Web 应用程序

在某些环境中,页面可能会以应用程序形式呈现,并以运行沉浸式内容的明确意图安装。 在这种情况下,启动 Web 应用程序 MAY 也作为用户意图的指示。

隐式同意是指 用户代理在不明确询问用户的情况下,对用户的同意作出判断,例如基于 Web 应用程序的安装状态、 访问频率和近因,或用户代理定义的某个操作,其中用户明确表示其希望进入沉浸式体验的意图。 鉴于 XR 数据的敏感性,在依赖隐式信号时强烈建议保持谨慎。

显式同意是指 用户代理基于已明确询问用户这一事实,对用户的同意作出判断。在收集显式 同意时,用户代理会说明正在请求的内容,并向用户提供拒绝选项。 用户同意请求可以根据受保护特性和用户代理选择,以多种视觉形式呈现。Web 应用程序的安装状态 MAY 被视为 显式同意的信号, 前提是在安装时请求了某种形式的显式同意

建议一旦针对特定显式同意被授予某个origin,该同意就持续到浏览上下文结束为止。用户代理可基于用户意图的隐式或显式信号选择延长或缩短此同意持续时间, 但建议实现在偏离此建议时保持谨慎,尤其是在依赖隐式信号时。例如,对于以运行沉浸式内容为明确意图而安装的 Web 应用程序,持久化用户同意可能是合适的,但对于沉浸式内容只是次要特性的已安装 Web 应用程序则不一定合适。

无论用户代理选择将用户同意持久化多长时间,敏感信息 MUST 只能由尚未 结束XRSession 暴露。

有多个非 XR API 会导致用户代理请求显式同意以使用某个特性。如果用户代理将在存在 活动沉浸式会话期间请求用户同意, 则用户代理 MUST 在向用户显示同意请求之前关闭会话。如果在创建活动沉浸式会话之前, 用户已授予该特性的同意,则不需要终止该会话。

注:此限制旨在确保在就用户代理应如何管理会话中的显式 同意达成共识之前,所有用户代理之间具有行为一致性。预计这不会是长期要求。

13.4. 数据调整

在某些情况下,可以通过数据调整来缓解安全和隐私威胁, 例如节流、量化、舍入、限制,或以其他方式操纵从 XR 设备报告的数据。有时即使已经建立 用户意图,这也可能是避免指纹识别所必需的。 但是,数据调整缓解措施 MUST 只能用于不会导致用户不适的情形。

13.4.1. 节流

节流是指以低于原本可能频率的频率报告敏感 信息。此缓解措施有可能降低站点推断用户意图、推断位置或执行用户画像的能力。但是,如果使用不当, 节流有很大风险导致用户不适。此外,在许多情况下,它可能不足以提供完整的缓解。

13.4.2. 舍入、量化和模糊化

舍入、量化和模糊化是三类缓解措施,它们会修改原本将返回给开发者的原始数据。 舍入通过减少用于表达数据的位数来降低数据精度。量化 将连续数据约束为报告一组离散值。模糊化是向数据中引入轻微的随机误差。 总体而言,这些缓解措施有助于避免指纹识别,并且在不会对用户舒适性造成明显影响时尤其有用。

13.4.3. 限制

限制是指只有当数据位于特定范围内时才报告数据。 例如,当用户移动到距离已批准位置超过特定距离之外时,可以在保持舒适的情况下限制位置姿态数据的报告。 采用此缓解措施时应谨慎,以确保用户体验不会受到负面影响。通常希望避免在范围末端出现“硬停止”, 因为这可能造成破坏性的用户体验。

13.5. 受保护功能

该 API 暴露的敏感 信息可以被划分为若干类别,这些类别具有共同的威胁概况以及针对这些威胁所需的保护。

13.5.1. 沉浸性

用户必须能够控制何时创建沉浸式会话,因为创建它会在用户机器上造成侵入性变化。例如,启动一个 沉浸式会话将启用 XR 设备传感器,接管对设备显示器的访问, 并开始呈现沉浸式内容,这可能会终止另一个应用程序对 XR 硬件的访问。 在某些系统上,这还可能产生显著的功耗或性能开销,或触发状态托盘或商店界面的启动。

要确定对于给定的 global object沉浸式会话请求是否被允许,用户代理 MUST 运行以下步骤:

  1. 如果请求不是在 global object 具有瞬态激活时发起,也不是在启动 Web 应用程序时发起,则返回 false

  2. 如果通过显式 同意隐式同意都不能很好地理解用户开始 沉浸式会话用户意图, 则返回 false

  3. 返回 true

启动内联会话 并不隐式带有与启动沉浸式会话相同的要求, 但取决于会话的请求特性,可能会施加附加要求。

要确定对于给定的 global object内联会话请求是否被允许,用户代理 MUST 运行以下步骤:

  1. 如果会话请求包含除 inline-stereo 之外的任何 必需 特性可选特性,则令 requiresUserIntenttrue,否则为 false

  2. 如果 requiresUserIntenttrue,且请求不是在 global object 具有瞬态激活时发起,也不是在启动 Web 应用程序时发起,则返回 false

  3. 如果 global object 不是 Window, 则返回 false

  4. 返回 true

13.5.2. 姿态

当基于传感器数据时,XRPoseXRViewerPose 将暴露敏感 信息,这些信息可能以多种方式被滥用,包括输入嗅探、注视跟踪或指纹识别。

要确定姿态是否可以被报告给一个 XRSession session,用户代理 MUST 运行以下步骤:

  1. 如果 session相关全局对象不是当前全局对象,则返回 false

  2. 如果 sessionvisibilityState"hidden", 则返回 false

  3. 按如下方式确定是否可以返回姿态数据:

    如果用户代理已知该姿态数据不会暴露可用于指纹识别的传感器数据

    返回 true

    如果将对底层传感器数据应用数据调整以防止指纹识别或画像

    返回 true

    如果通过显式同意隐式 同意可以很好地理解用户 意图

    返回 true

    否则

    返回 false

注:用户代理用于确定姿态不会暴露可指纹识别数据的方法, 由用户代理自行决定。

XRViewerPoseXRPose 之间的主要区别是 包含 XRView 信息。 当存在多个视图,且这些视图之间的物理关系可由用户配置时,这些视图之间的关系会被视为敏感信息, 因为它可用于对用户进行指纹识别或画像。

如果 XRView 之间的关系可能唯一标识 XR 设备,则用户代理 MUST 对 XRView 数据进行匿名化, 以防止指纹识别。匿名化方法由用户代理自行决定。

注:此外,如果 XRView 之间的关系 受用户配置的瞳距(IPD)影响,则强烈建议用户代理在会话创建期间、报告任何 XRView 数据之前, 要求显式 同意

13.5.3. 参考空间

取决于所使用的参考空间,可能会向应用暴露几种不同类型的敏感信息

因此,各种 reference space 类型在创建时受到限制,以确保暴露的敏感 信息得到安全处理:

大多数 reference spaces 要求通过显式同意隐式同意很好地理解使用该 reference space 的 用户意图。详见特性 要求表。

任何由 "local""local-floor""bounded-floor" reference spaces 组成、且能够彼此关联的组,MUST 共享一个共同的 native origin;此限制仅在 "unbounded" reference spaces 的创建受到限制时适用。

要确定在两个空间 spacebaseSpace 之间姿态是否必须受限制,用户代理 MUST 运行以下步骤:

  1. 如果 spacebaseSpace 任一是 XRBoundedReferenceSpace, 且另一个空间的 native origin 超出native bounds geometry 的距离大于用户代理确定的合理距离,则返回 true。

  2. 如果 spacebaseSpace 任一是 XRReferenceSpace, 且其 type"local""local-floor", 并且这些空间的 native origins 之间的距离大于用户代理确定的合理距离, 则返回 true

  3. 返回 false

注:文档可见性的要求基于 [DEVICE-ORIENTATION]

注:建议将相对于 "local""local-floor" reference space 报告的姿态限制在距离 XRReferenceSpacenative origin 15 米范围内。

注:建议将相对于 XRBoundedReferenceSpace 报告的姿态限制在该 XRBoundedReferenceSpacenative bounds geometry 外 1 米范围内。

13.6. 可信环境

可信 UI是由用户代理呈现的界面, 用户能够与之交互,但页面不能。用户代理 MUST 支持显示可信 UI

可信 UI MUST 具有以下属性:

宽泛地说,希望支持可信 UI的用户代理有两个选项。其中一个选项是 可信沉浸式 UI,它是一种不会退出 沉浸模式的可信 UI。实现可信沉浸式 UI可能很有挑战性,因为 XRWebGLLayer 缓冲区会填满 XR 设备显示器,而用户代理通常不会为自身使用“保留” 像素。用户代理不需要支持可信沉浸式 UI,它们可以改为 暂时暂停/退出沉浸模式,并向用户显示非沉浸式可信 UI

注:可信 UI示例包括:

读取姿态和输入信息的能力会对可信 UI的完整性构成风险,因为页面可能使用这些信息窥探用户在与 可信 UI交互时作出的选择,包括猜测键盘输入。为防止此风险, 当用户正在与 URL 栏或系统对话框等可信 UI沉浸式或非沉浸式)交互时,用户代理 MUST 将所有 XRSessionvisibility state 设为 "hidden""visible-blurred"。 此外,为防止恶意页面能够监控其他页面上的输入,如果当前聚焦区域不属于创建 XRSession 的文档, 则用户代理 MUST 将该 XRSessionvisibility state 设为 "hidden"

在为特定可信 UI实例选择使用 "hidden" 还是 "visible-blurred" 时,用户代理 MUST 考虑姿态信息是否构成安全风险。例如,涉及文本输入,尤其是密码输入的可信 UI, 可能通过用户输入时的移动泄漏输入文本。在这些情况下,用户代理 SHOULD 也停止暴露任何与眼动跟踪相关的信息。

用户代理 MUST 使用可信 UI 来显示权限提示。

如果虚拟环境不能以低延迟和高帧率持续更新用户视点,用户可能会迷失方向或身体不适。 由于无法强制页面生成始终高性能且正确的内容,用户代理 MUST 提供一个被跟踪的可信环境,以及一个 与页面内容异步运行的 XR Compositor。compositor 负责合成可信和不可信内容。如果内容性能不足、不提交帧或意外终止, 用户代理应能够继续呈现响应式的可信 UI

此外,页面内容还能以与性能无关的方式让用户感到不适。 错误应用的跟踪、频闪颜色,以及意图冒犯、惊吓或恐吓的内容,都是可能导致用户想要快速退出 XR 体验的内容示例。 在这些情况下,移除 XR 设备并不总是快速或实际的选择。为适应这一点,用户代理 MUST 向用户提供一个操作, 例如按下保留的硬件按钮或执行手势,以退出 WebXR 内容并显示用户代理的可信 UI

13.7. 上下文隔离

可信 UI 必须由独立的渲染上下文绘制,其状态与页面使用的任何渲染上下文隔离。 (例如,任何 WebGL 渲染上下文。)这是为了防止页面破坏可信 UI 上下文的状态, 从而可能阻止其正确渲染被跟踪的环境。它还防止页面可能捕获来自可信 UI 的图像, 这可能导致私密信息泄漏。

此外,为防止 CORS 相关漏洞,每个浏览上下文都会看到 API 返回对象的新实例,例如 XRSession。 在一个具有某个相关 realmXRWebGLLayer 上设置的 context 等属性,不应能通过具有不同 相关 realm且不具有相同 originXRWebGLLayer 读取。类似地,在 API 上调用的方法 MUST NOT 对其他浏览上下文造成可观察的状态变化。例如:不会暴露可启用系统级朝向重置的方法, 因为恶意页面可能反复调用它,以阻止其他页面正确跟踪。但是,用户代理 MUST 尊重由用户手势或系统菜单触发的系统级朝向重置。

注:这不适用于由一个浏览上下文进入沉浸模式、获取设备锁,并可能在其他浏览上下文上触发 devicechange 事件所导致的状态变化。

13.8. 指纹识别

鉴于该 API 描述用户可用的硬件及其能力,它不可避免地会为指纹识别提供额外表面。 虽然不可能完全避免这一点,用户代理应采取措施缓解该问题。本规范将可用硬件的报告限制为一次只报告单个设备, 这防止将连接多个头显的罕见情况用作指纹识别信号。此外,被报告的设备没有字符串标识符,并且在创建 XRSession 之前几乎不暴露关于设备能力的信息;而当敏感信息将被暴露时,创建 XRSession 需要额外保护。

13.8.1. isSessionSupported() 的指纹识别考量

由于 isSessionSupported() 可以在没有用户激活的情况下调用,它可能被用作指纹识别向量。

"xr-session-supported" 强大特性限制对 isSessionSupported() API 的访问。

"xr-session-supported" 的权限相关算法和类型定义如下:

权限描述符类型
dictionary XRSessionSupportedPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
};

name 对于 XRPermissionDescriptor"xr-session-supported"

13.8.2. 何时自动授予 "xr-session-supported" 的考量

Web 上的隐私和个性化之间经常存在张力。本节提供指导,说明可以在何处限定这种权衡, 以及用户代理何时可以通过 isSessionSupported() 向站点描述浏览器的 WebXR 能力而不造成任何隐私降低。

"xr-session-supported" 可基于以下标准在某些系统上自动授予。 这可以提供更好的用户体验,并缓解权限疲劳。

如果一组用户代理都报告相同的 userAgentappVersion, 则它们是通过用户代理字符串不可区分的。 此类通常由浏览器版本以及运行平台/设备标识,但不能通过任何已连接外部设备的状态来区分。我们可以使用 通过用户代理字符串不可区分的用户代理概念, 来正确评估指纹识别风险。

某些通过用户代理字符串不可区分的用户代理将 永不支持 给定 XRSessionMode 的会话。例如:运行在已知不满足移动 AR 支持要求的某款手机型号上的用户代理。 在这些情况下,让 isSessionSupported() 始终报告该 XRSessionMode 不受支持,几乎没有指纹识别风险,因为每个此类设备都会一致报告相同值,并且假定设备类型和型号可通过其他方式推断, 例如通过 userAgent。 因此,在此类系统上,用户代理应自动拒绝相关 XRSessionMode"xr-session-supported"

其他通过用户代理字符串不可区分的用户代理将 通常支持 给定 XRSessionMode 的会话。例如:已知支持 WebXR 且专门运行在 VR 头显内的用户代理,可能支持 "immersive-vr" 会话,除非用户明确阻止。 在这些情况下,报告该 XRSessionMode 不受支持虽然准确,却会提供更多可唯一标识用户的信息。因此,报告该 XRSessionMode 始终可用,并允许 requestSession() 失败,更能保护隐私,同时也不太可能成为用户困惑的来源。在此类系统上,用户代理应自动授予相关 XRSessionMode"xr-session-supported"

对于 XR 能力可用性高度可变的通过用户代理字符串不可区分的用户代理, 例如支持 XR 外设的桌面系统,呈现最高的指纹识别风险。此类设备上的用户代理不应以允许 isSessionSupported() API 提供额外指纹识别位的方式,自动授予 "xr-session-supported"

注:处理此类情况的一些可接受方法如下:
  • 在调用 isSessionSupported() 时,始终判断用户是否对 "xr-session-supported" 给出了 显式同意(可使用可能被缓存的权限提示或类似机制)。

  • 自动授予 "xr-session-supported",但让 isSessionSupported() 始终报告 true,即使在并非始终具备 XR 能力的平台上也是如此,无论相应硬件或软件是否存在。 这会以用户工效为代价,因为它会导致页面向无法查看 XR 内容的用户宣传 XR 内容。

  • 当适当硬件存在时,让 isSessionSupported() 请求用户对 "xr-session-supported" 给出 显式同意;当此类硬件 _不存在_ 时,在适当随机长度的时间之后返回 false。在此类实现中,内容不得能够区分用户代理未连接 XR 硬件的情况, 与用户代理已连接 XR 硬件但用户拒绝提供显式同意的情况。

无论选择何种技术,都不应在没有显式 同意的情况下揭示有关已连接 XR 硬件的额外知识。

14. 集成

14.1. 权限策略

本规范定义了一个策略控制特性,该特性控制是否可由 requestSession() 返回任何需要使用空间跟踪的 XRSession, 以及是否可由 isSessionSupported()navigator.xr 对象上的 devicechange 事件表明需要空间跟踪的会话模式支持。

此特性的特性标识符是 "xr-spatial-tracking"

此特性的默认允许列表["self"]

注:如果文档的origin 不允许使用 "xr-spatial-tracking" 权限策略,任何沉浸式会话都将被阻止, 因为所有沉浸式 会话都需要某种空间跟踪使用。"inline" 会话仍会被允许,但会被限制为仅使用 "viewer" XRReferenceSpace

14.2. Permissions API 集成

[permissions] API 提供了一种统一方式, 使网站能够向用户请求权限,并查询它们已被授予哪些权限。

"xr" 强大特性的权限相关算法和类型定义如下:

权限描述符类型
dictionary XRPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

name 对于 XRPermissionDescriptor"xr"

权限结果类型
[Exposed=Window]
interface XRPermissionStatus: PermissionStatus {
  attribute FrozenArray<DOMString> granted;
};
权限查询算法
要使用 XRPermissionDescriptor descriptorXRPermissionStatus status 查询 "xr" 权限,UA MUST 运行以下步骤:
  1. statusstate 设为 descriptor权限状态

  2. 如果 statusstate"denied", 则将 statusgranted 设为空的 FrozenArray 并中止这些步骤。

  3. result 为给定 descriptorrequiredFeaturesoptionalFeaturesmode 解析所请求特性的结果。

  4. 如果 resultnull,则运行以下步骤:

    1. statusgranted 设为空的 FrozenArray

    2. statusstate 设为 "denied"

    3. 中止这些步骤。

  5. 令 (consentRequired, consentOptional, granted) 为 result 的字段。

  6. statusgranted 设为 granted

  7. 如果 consentRequired 为空consentOptional 为空, 则将 statusstate 设为 "granted" 并中止这些步骤

  8. statusstate 设为 "prompt"

权限请求算法
要使用 XRPermissionDescriptor descriptorXRPermissionStatus status 请求 "xr" 权限,UA MUST 运行以下步骤:
  1. statusgranted 设为空的 FrozenArray

  2. requiredFeaturesdescriptorrequiredFeatures

  3. optionalFeaturesdescriptoroptionalFeatures

  4. device 为针对 moderequiredFeaturesoptionalFeatures 获取当前设备的结果。

  5. result 为给定 requiredFeaturesoptionalFeaturesmode 解析所请求特性的结果。

  6. 如果 resultnull,则运行以下步骤:

    1. statusstate 设为 "denied"

    2. 中止这些步骤。

  7. 令 (consentRequired, consentOptional, granted) 为 result 的字段。

  8. 用户代理 MAY 在此时请求用户许可调用算法使用 consentRequiredconsentOptional 中的任何特性。这些提示的结果应在确定是否存在明确的 用户 意图信号来启用这些特性时纳入考虑。

  9. consentRequired 中的每个 feature 执行以下步骤:

    1. 用户代理 MAY 在此时请求用户许可调用算法使用 feature。 这些提示的结果应在确定是否存在明确的用户意图信号来启用 feature 时纳入考虑。

    2. 如果尚未确定存在明确的用户意图信号来启用 feature, 则运行以下步骤:

      1. statusstate 设为 "denied" 并中止这些步骤。

    3. 如果 feature 不在 granted 中,则将 feature 追加到 granted

  10. consentOptional 中的每个 feature 执行以下步骤:

    1. 用户代理 MAY 在此时请求用户许可调用算法使用 feature。 这些提示的结果应在确定是否存在明确的用户意图信号来启用 feature 时纳入考虑。

    2. 如果尚未确定存在明确的用户意图信号来启用 feature, 则继续到下一项。

    3. 如果 feature 不在 granted 中,则将 feature 追加到 granted

  11. statusgranted 设为 granted

  12. granted 的所有元素添加到 device 针对 mode已授予特性集合中。

  13. statusstate 设为 "granted"

注:在判断是否存在明确的 用户意图信号时, 用户代理可以自由地将所有所请求特性的权限提示合并批量显示,也允许逐个显示。

注:在确定 Web 应用程序的用户意图时, 用户代理必须检查它是否作为 Web 应用程序由用户明确启动。 它们 MUST NOT 只检查 origin 是否与某个已安装 Web 应用程序的 origin 匹配。

要为 XRSessionMode mode,在给定 requiredFeaturesoptionalFeatures 的情况下 解析所请求的特性, 用户代理 MUST 运行以下步骤:

  1. consentRequired 为空的 DOMString 列表

  2. consentOptional 为空的 DOMString 列表

  3. granted 为空的 DOMString 列表

  4. device 为针对 moderequiredFeaturesoptionalFeatures 获取当前设备的结果。

  5. previouslyEnableddevice 针对 mode已授予特性集合

  6. 如果 devicenull,或 device支持模式列表包含 mode,则运行以下步骤:

    1. 返回 元组 (consentRequired, consentOptional, granted)

  7. 将与 mode 关联的默认特性表中的每个 特性描述符添加到 granted, 如果其中尚不存在该项。

  8. requiredFeatures 中的每个 feature 执行以下步骤:

    1. 如果 featurenull,则继续到下一项。

    2. 如果 feature 不是有效的特性描述符,则返回 null

    3. 如果 feature 已在 granted 中,则继续到下一项。

    4. 如果请求文档的 origin 不允许使用 feature 所需的任何 权限策略(如特性要求表所示),则返回 null

    5. 如果 sessionXR 设备不能支持 feature 所描述的功能,或用户代理以其他方式确定拒绝该特性,则返回 null

    6. 如果 feature 所描述的功能需要显式同意,且 feature 不在 previouslyEnabled 中,则将其追加到 consentRequired

    7. 否则将 feature 追加到 granted

  9. optionalFeatures 中的每个 feature 执行以下步骤:

    1. 如果 featurenull,则继续到下一项。

    2. 如果 feature 不是有效的特性描述符,则继续到下一项。

    3. 如果 feature 已在 granted 中,则继续到下一项。

    4. 如果请求文档的 origin 不允许使用 feature 所需的任何 权限策略(如特性要求表所示),则继续到下一项。

    5. 如果 sessionXR 设备不能支持 feature 所描述的功能,或用户代理以其他方式确定拒绝该特性,则继续到下一项。

    6. 如果 feature 所描述的功能需要显式同意,且 feature 不在 previouslyEnabled 中,则将其追加到 consentOptional

    7. 否则将 feature 追加到 granted

  10. 返回 元组 (|consentRequired|, |consentOptional|, |granted|)

更改

相对于 候选推荐快照,2022 年 3 月 31 日的更改

相对于 2020 年 7 月 24 日工作草案的更改

相对于 2019 年 10 月 10 日工作草案的更改

新特性:

更改:

相对于 2019 年 2 月 5 日首次公开工作草案的更改

新特性:

移除的特性:

更改:

15. 致谢

感谢以下个人对 WebXR Device API 规范作出的贡献:

并特别感谢 Vladimir VukicevicUnity)开启了这整段冒险!

一致性

文档约定

一致性要求通过描述性断言 与 RFC 2119 术语相结合来表达。 关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY” 和 “OPTIONAL” 在本文档的规范性部分中 应按 RFC 2119 所述解释。 但是,为了可读性, 这些词在本规范中并不全部以大写字母出现。

本规范的所有文本都是规范性的, 除非小节明确标记为非规范性、示例和注释。[RFC2119]

本规范中的示例以 “for example” 字样引入, 或通过 class="example" 与规范性文本分开, 如下所示:

这是一个信息性示例的例子。

信息性注释以 “Note” 一词开头, 并通过 class="note" 与规范性文本分开, 如下所示:

注:这是一个信息性注释。

一致性 算法

作为算法一部分、以祈使语气表述的要求 (例如 "strip any leading space characters" 或 "return false and abort these steps") 应按引入该算法时所使用的关键词 ("must"、"should"、"may" 等) 的含义来解释。

以算法或具体步骤表述的一致性要求 可以以任意方式实现, 只要最终结果等效即可。 特别是,本规范中定义的算法 旨在易于理解, 而不是为了高性能。 鼓励实现者进行优化。

索引

由本规范定义的术语

由引用定义的术语

参考文献

规范性参考文献

[DOM]
Anne van Kesteren. DOM Standard. 现行标准. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[GEOMETRY-1]
Sebastian Zartner; Yehonatan Daniv. Geometry Interfaces Module Level 1. 2025 年 12 月 4 日. CRD. URL: https://www.w3.org/TR/geometry-1/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. 2026 年 3 月 24 日. WD. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren; et al. HTML Standard. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. 现行标准. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. 2025 年 10 月 6 日. WD. URL: https://www.w3.org/TR/permissions/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. 2025 年 10 月 6 日. WD. URL: https://www.w3.org/TR/permissions-policy-1/
[POINTEREVENTS4]
Patrick Lauke; Robert Flack. Pointer Events. 2026 年 5 月 22 日. WD. URL: https://www.w3.org/TR/pointerevents4/
[POINTERLOCK]
Mustaq Ahmed; Vincent Scheib. Pointer Lock 2.0. 2026 年 2 月 25 日. WD. URL: https://www.w3.org/TR/pointerlock-2/
[REQUESTIDLECALLBACK]
Scott Haseley. requestIdleCallback(). 2025 年 5 月 21 日. WD. URL: https://www.w3.org/TR/requestidlecallback/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997 年 3 月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBGL-2]
Dean Jackson; Jeff Gilbert. WebGL 2.0 Specification. 2017 年 8 月 12 日. URL: https://www.khronos.org/registry/webgl/specs/latest/2.0/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. 现行标准. URL: https://webidl.spec.whatwg.org/
[WEBXRLAYERS-1]
Rik Cabanier. WebXR Layers API Level 1. 2026 年 1 月 22 日. WD. URL: https://www.w3.org/TR/webxrlayers-1/

非规范性参考文献

[DEVICE-ORIENTATION]
Reilly Grant; Marcos Caceres. Device Orientation and Motion. 2025 年 2 月 12 日. CRD. URL: https://www.w3.org/TR/orientation-event/
[WEBXR-AR-MODULE-1]
Brandon Jones; Manish Goregaokar; Rik Cabanier. WebXR Augmented Reality Module - Level 1. 2025 年 4 月 25 日. CRD. URL: https://www.w3.org/TR/webxr-ar-module-1/

IDL 索引

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute XRSystem xr;
};

[SecureContext, Exposed=Window] interface XRSystem : EventTarget {
  // Methods
  Promise<boolean> isSessionSupported(XRSessionMode mode);
  [NewObject] Promise<XRSession> requestSession(XRSessionMode mode, optional XRSessionInit options = {});

  // Events
  attribute EventHandler ondevicechange;
};

enum XRSessionMode {
  "inline",
  "immersive-vr",
  "immersive-ar"
};

dictionary XRSessionInit {
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

enum XRVisibilityState {
  "visible",
  "visible-blurred",
  "hidden",
};

[SecureContext, Exposed=Window] interface XRSession : EventTarget {
  // Attributes
  readonly attribute XRVisibilityState visibilityState;
  readonly attribute float? frameRate;
  readonly attribute Float32Array? supportedFrameRates;
  [SameObject] readonly attribute XRRenderState renderState;
  [SameObject] readonly attribute XRInputSourceArray inputSources;
  [SameObject] readonly attribute XRInputSourceArray trackedSources;
  readonly attribute FrozenArray<DOMString> enabledFeatures;
  readonly attribute boolean isSystemKeyboardSupported;

  // Methods
  undefined updateRenderState(optional XRRenderStateInit state = {});
  Promise<undefined> updateTargetFrameRate(float rate);
  [NewObject] Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceType type);

  unsigned long requestAnimationFrame(XRFrameRequestCallback callback);
  undefined cancelAnimationFrame(unsigned long handle);

  Promise<undefined> end();

  // Events
  attribute EventHandler onend;
  attribute EventHandler oninputsourceschange;
  attribute EventHandler onselect;
  attribute EventHandler onselectstart;
  attribute EventHandler onselectend;
  attribute EventHandler onsqueeze;
  attribute EventHandler onsqueezestart;
  attribute EventHandler onsqueezeend;
  attribute EventHandler onvisibilitychange;
  attribute EventHandler onframeratechange;
};

dictionary XRRenderStateInit {
  double depthNear;
  double depthFar;
  boolean passthroughFullyObscured;
  double inlineVerticalFieldOfView;
  XRWebGLLayer? baseLayer;
  sequence<XRLayer>? layers;
};

[SecureContext, Exposed=Window] interface XRRenderState {
  readonly attribute double depthNear;
  readonly attribute double depthFar;
  readonly attribute boolean? passthroughFullyObscured;
  readonly attribute double? inlineVerticalFieldOfView;
  readonly attribute XRWebGLLayer? baseLayer;
};

callback XRFrameRequestCallback = undefined (DOMHighResTimeStamp time, XRFrame frame);

[SecureContext, Exposed=Window] interface XRFrame {
  [SameObject] readonly attribute XRSession session;
  readonly attribute DOMHighResTimeStamp predictedDisplayTime;

  XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
  XRPose? getPose(XRSpace space, XRSpace baseSpace);
};

[SecureContext, Exposed=Window] interface XRSpace : EventTarget {

};

enum XRReferenceSpaceType {
  "viewer",
  "local",
  "local-floor",
  "bounded-floor",
  "unbounded"
};

[SecureContext, Exposed=Window]
interface XRReferenceSpace : XRSpace {
  [NewObject] XRReferenceSpace getOffsetReferenceSpace(XRRigidTransform originOffset);

  attribute EventHandler onreset;
};

[SecureContext, Exposed=Window]
interface XRBoundedReferenceSpace : XRReferenceSpace {
  readonly attribute FrozenArray<DOMPointReadOnly> boundsGeometry;
};

[SecureContext, Exposed=Window] interface mixin XRViewGeometry {
  readonly attribute Float32Array projectionMatrix;
  [SameObject] readonly attribute XRRigidTransform transform;
};

enum XREye {
  "none",
  "left",
  "right"
};

[SecureContext, Exposed=Window] interface XRView {
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  readonly attribute double? recommendedViewportScale;

  undefined requestViewportScale(double? scale);
};

XRView includes XRViewGeometry;

[SecureContext, Exposed=Window] interface XRViewport {
  readonly attribute long x;
  readonly attribute long y;
  readonly attribute long width;
  readonly attribute long height;
};

[SecureContext, Exposed=Window]
interface XRRigidTransform {
  constructor(optional DOMPointInit position = {}, optional DOMPointInit orientation = {});
  [SameObject] readonly attribute DOMPointReadOnly position;
  [SameObject] readonly attribute DOMPointReadOnly orientation;
  readonly attribute Float32Array matrix;
  [SameObject] readonly attribute XRRigidTransform inverse;
};

[SecureContext, Exposed=Window] interface XRPose {
  [SameObject] readonly attribute XRRigidTransform transform;
  [SameObject] readonly attribute DOMPointReadOnly? linearVelocity;
  [SameObject] readonly attribute DOMPointReadOnly? angularVelocity;

  readonly attribute boolean emulatedPosition;
};

[SecureContext, Exposed=Window] interface XRViewerPose : XRPose {
  [SameObject] readonly attribute FrozenArray<XRView> views;
};

enum XRHandedness {
  "none",
  "left",
  "right"
};

enum XRTargetRayMode {
  "gaze",
  "tracked-pointer",
  "screen",
  "transient-pointer"
};

[SecureContext, Exposed=Window]
interface XRInputSource {
  readonly attribute XRHandedness handedness;
  readonly attribute XRTargetRayMode targetRayMode;
  [SameObject] readonly attribute XRSpace targetRaySpace;
  [SameObject] readonly attribute XRSpace? gripSpace;
  [SameObject] readonly attribute FrozenArray<DOMString> profiles;
  readonly attribute boolean skipRendering;
};

[SecureContext, Exposed=Window]
interface XRInputSourceArray {
  iterable<XRInputSource>;
  readonly attribute unsigned long length;
  getter XRInputSource(unsigned long index);
};

[SecureContext, Exposed=Window]
interface XRLayer : EventTarget {};


typedef (WebGLRenderingContext or
         WebGL2RenderingContext) XRWebGLRenderingContext;

dictionary XRWebGLLayerInit {
  boolean antialias = true;
  boolean depth = true;
  boolean stencil = false;
  boolean alpha = true;
  boolean ignoreDepthValues = false;
  double framebufferScaleFactor = 1.0;
};

[SecureContext, Exposed=Window]
interface XRWebGLLayer: XRLayer {
  constructor(XRSession session,
             XRWebGLRenderingContext context,
             optional XRWebGLLayerInit layerInit = {});
  // Attributes
  readonly attribute boolean antialias;
  readonly attribute boolean ignoreDepthValues;
  attribute float? fixedFoveation;

  [SameObject] readonly attribute WebGLFramebuffer? framebuffer;
  readonly attribute unsigned long framebufferWidth;
  readonly attribute unsigned long framebufferHeight;

  // Methods
  XRViewport? getViewport(XRView view);

  // Static Methods
  static double getNativeFramebufferScaleFactor(XRSession session);
};

partial dictionary WebGLContextAttributes {
    boolean xrCompatible = false;
};

partial interface mixin WebGLRenderingContextBase {
    [NewObject] Promise<undefined> makeXRCompatible();
};

[SecureContext, Exposed=Window]
interface XRSessionEvent : Event {
  constructor(DOMString type, XRSessionEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
};

dictionary XRSessionEventInit : EventInit {
  required XRSession session;
};

[SecureContext, Exposed=Window]
interface XRInputSourceEvent : Event {
  constructor(DOMString type, XRInputSourceEventInit eventInitDict);
  [SameObject] readonly attribute XRFrame frame;
  [SameObject] readonly attribute XRInputSource inputSource;
};

dictionary XRInputSourceEventInit : EventInit {
  required XRFrame frame;
  required XRInputSource inputSource;
};

[SecureContext, Exposed=Window]
interface XRInputSourcesChangeEvent : Event {
  constructor(DOMString type, XRInputSourcesChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  [SameObject] readonly attribute FrozenArray<XRInputSource> added;
  [SameObject] readonly attribute FrozenArray<XRInputSource> removed;
};

dictionary XRInputSourcesChangeEventInit : EventInit {
  required XRSession session;
  required sequence<XRInputSource> added;
  required sequence<XRInputSource> removed;

};

[SecureContext, Exposed=Window]
interface XRReferenceSpaceEvent : Event {
  constructor(DOMString type, XRReferenceSpaceEventInit eventInitDict);
  [SameObject] readonly attribute XRReferenceSpace referenceSpace;
  [SameObject] readonly attribute XRRigidTransform? transform;
};

dictionary XRReferenceSpaceEventInit : EventInit {
  required XRReferenceSpace referenceSpace;
  XRRigidTransform? transform = null;
};

[SecureContext, Exposed=Window]
interface XRVisibilityMaskChangeEvent : Event {
  constructor(DOMString type, XRVisibilityMaskChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  [SameObject] readonly attribute Float32Array vertices;
  [SameObject] readonly attribute Uint32Array indices;
};

dictionary XRVisibilityMaskChangeEventInit : EventInit {
  required XRSession session;
  required XREye eye;
  required unsigned long index;
  required Float32Array vertices;
  required Uint32Array indices;
};

dictionary XRSessionSupportedPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
};

dictionary XRPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

[Exposed=Window]
interface XRPermissionStatus: PermissionStatus {
  attribute FrozenArray<DOMString> granted;
};

MDN

Navigator/xr

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

WebGLRenderingContext/makeXRCompatible

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?

WebGLRenderingContext/makeXRCompatible

In only one current engine.

FirefoxNoneSafariNoneChrome79+
OperaNoneEdge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera MobileNone
MDN

XRBoundedReferenceSpace/boundsGeometry

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRBoundedReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame/getPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame/getViewerPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame/session

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/gripSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/handedness

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/profiles

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/targetRayMode

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/targetRaySpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceArray/length

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceArray

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent/XRInputSourceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent/frame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent/inputSource

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/XRInputSourcesChangeEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/added

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/removed

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/session

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRLayer

In only one current engine.

FirefoxNoneSafariNoneChrome84+
Opera?Edge84+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRPose/angularVelocity

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRPose/emulatedPosition

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRPose/linearVelocity

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRPose/transform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace/getOffsetReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace/reset_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace/reset_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent/XRReferenceSpaceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent/referenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent/transform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRenderState/baseLayer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState/depthFar

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState/depthNear

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState/inlineVerticalFieldOfView

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRigidTransform/XRRigidTransform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/inverse

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/matrix

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/orientation

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/position

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/cancelAnimationFrame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/end

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/end_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/end_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/inputSources

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/inputsourceschange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/inputsourceschange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/renderState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/requestAnimationFrame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/requestReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/select_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/select_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectend_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectend_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectstart_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectstart_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/squeeze_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeeze_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezeend_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezeend_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezestart_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezestart_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/updateRenderState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/visibilitychange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/visibilitychange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/visibilityState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSessionEvent/XRSessionEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSessionEvent/session

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSessionEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSystem/devicechange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem/devicechange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem/isSessionSupported

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem/requestSession

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRView/eye

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRView/recommendedViewportScale

In only one current engine.

FirefoxNoneSafariNoneChrome90+
Opera?Edge90+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRView/requestViewportScale

In only one current engine.

FirefoxNoneSafariNoneChrome90+
Opera?Edge90+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRView

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewerPose/views

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewerPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/height

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/width

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/x

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/y

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/XRWebGLLayer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/antialias

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/fixedFoveation

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRWebGLLayer/framebuffer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/framebufferHeight

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/framebufferWidth

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/getNativeFramebufferScaleFactor_static

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/getViewport

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/ignoreDepthValues

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

Headers/Feature-Policy/xr-spatial-tracking

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera MobileNone

Headers/Permissions-Policy/xr-spatial-tracking

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera MobileNone