XR_ANDROID_spatial_component_subsumed_by

名称字符串

XR_ANDROID_spatial_component_subsumed_by

扩展程序类型

实例扩展程序

已注册的扩展程序编号

792

修订版本

1

批准状态

未批准

扩展程序和版本依赖项

XR_EXT_spatial_entity

XR_EXT_spatial_plane_tracking

上次修改日期

2025-08-19

IP 状态

没有已知的 IP 权利主张。

创作贡献者

Brian Chen,Google
Kyle Chen,Google
Levana Chen,Google
Nihav Jain,Google
Spencer Quin,Google

概览

此扩展程序以 XR_EXT_spatial_entity 为基础,并为 XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT 提供了一个新组件,以公开 subsumed_by 属性。

当运行时获取足够的环境信息来检测 2 个被跟踪的平面实际上是同一个平面时,系统会将包含其中一个平面的 ID 的 subsumed_by 组件附加到另一个平面。

此扩展程序还引入了一个新过滤器,应用可以 将其链接到 XrSpatialDiscoverySnapshotCreateInfoEXT,以过滤掉附加了 subsumed_by 组件的所有实体。

权限

Android 应用必须 在其清单中列出 android.permission.SCENE_UNDERSTANDING_COARSE 权限,因为此扩展程序会跟踪环境中的平面。android.permission.SCENE_UNDERSTANDING_COARSE 权限被视为危险权限。

(保护级别:危险)

运行时支持

如果运行时支持 subsumed_by,则必须 支持平面跟踪功能,并通过在 xrEnumerateSpatialCapabilitiesEXT 中枚举 XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT 来指明这一点。

如果运行时提供 subsumed_by,则必须 通过在 xrEnumerateSpatialCapabilityComponentTypesEXT 中将 XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID 枚举为 XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT 功能的受支持组件来指明这一点。

附加实体的 XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID 的所有组件数据必须 与 subsume 它的实体相同。

Subsumed By 组件

组件数据

XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID 使用 XrSpatialEntityIdEXT 结构作为其数据,该结构表示 subsuming 实体的 ID。

用于查询数据的组件列表结构体

XrSpatialComponentSubsumedByListANDROID 结构的定义如下:

typedef struct XrSpatialComponentSubsumedByListANDROID {
    XrStructureType          type;
    void*                    next;
    uint32_t                 subsumedUniqueIdCount;
    XrSpatialEntityIdEXT*    subsumedUniqueIds;
} XrSpatialComponentSubsumedByListANDROID;

成员说明

  • type 是此结构的 XrStructureType
  • nextNULL 或指向结构链中下一个结构的指针。
  • subsumedUniqueIdCount 是一个 uint32_t,用于描述 subsumedUniqueIds 数组中的元素数量。
  • subsumedUniqueIdsXrSpatialEntityIdEXT 的数组。

应用可以 通过将 XR_TYPE_SPATIAL_COMPONENT_SUBSUMED_BY_LIST_ANDROID 添加到 XrSpatialComponentDataQueryResultEXT 的下一个链中,查询 XrSpatialSnapshotEXT 中空间实体的 subsumed_by 组件。

如果 XR_TYPE_SPATIAL_COMPONENT_SUBSUMED_BY_LIST_ANDROID 位于 XrSpatialComponentDataQueryResultEXT :: next 的下一个链中,但 XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID 未包含在 XrSpatialComponentDataQueryConditionEXT :: componentTypes 中,则运行时 必须xrQuerySpatialComponentDataEXT 返回 XR_ERROR_VALIDATION_FAILURE

如果 subsumedUniqueIdCount 小于 XrSpatialComponentDataQueryResultEXT :: entityIdCountOutput,则运行时 必须xrQuerySpatialComponentDataEXT 返回 XR_ERROR_SIZE_INSUFFICIENT

有效用法(隐式)

  • 必须先启用 XR_ANDROID_spatial_component_subsumed_by 扩展程序,然后才能使用 XrSpatialComponentSubsumedByListANDROID
  • type 必须XR_TYPE_SPATIAL_COMPONENT_SUBSUMED_BY_LIST_ANDROID
  • next 必须NULL 或指向结构链中下一个结构的有效指针
  • subsumedUniqueIds 必须 是指向 subsumedUniqueIdCount XrSpatialEntityIdEXT 值数组的指针
  • subsumedUniqueIdCount 参数 必须 大于 0

配置

如果为 XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT 功能在 XrSpatialCapabilityComponentTypesEXT :: componentTypes 中枚举了 XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID,则应用可以 通过在支持此组件的功能的 XrSpatialCapabilityConfigurationBaseHeaderEXT 派生结构的 XrSpatialCapabilityConfigurationBaseHeaderEXT :: enabledComponents 列表中添加枚举来启用它。

过滤 subsumed 实体

XrSpatialDiscoveryUniqueEntitiesFilterANDROID 结构的定义如下:

typedef struct XrSpatialDiscoveryUniqueEntitiesFilterANDROID {
    XrStructureType    type;
    const void*        next;
} XrSpatialDiscoveryUniqueEntitiesFilterANDROID;

成员说明

  • type 是此结构的 XrStructureType
  • nextNULL 或指向结构链中下一个结构的指针。

应用可以XrSpatialDiscoverySnapshotCreateInfoEXT 的下一个链中添加 XrSpatialDiscoveryUniqueEntitiesFilterANDROID,以获取包含未被其他实体 subsumed 的实体的快照。

如果应用将 XrSpatialDiscoveryUniqueEntitiesFilterANDROID 链接到 XrSpatialDiscoverySnapshotCreateInfoEXT,同时在 XrSpatialDiscoverySnapshotCreateInfoEXT :: componentTypes 中添加 XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID 组件,则运行时 必须 返回 XR_ERROR_VALIDATION_FAILURE

如果应用将 XrSpatialDiscoveryUniqueEntitiesFilterANDROID 链接到 XrSpatialDiscoverySnapshotCreateInfoEXT,但未在 XrSpatialDiscoverySnapshotCreateInfoEXT :: componentTypes 中列出任何组件,则运行时必须在快照中添加所有空间实体,这些实体具有为 spatialContext 配置的功能的 XrSpatialCapabilityConfigurationBaseHeaderEXT :: enabledComponents 中枚举的组件集,但具有 XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID 组件的实体除外。

有效用法(隐式)

示例代码

配置平面跟踪功能

以下示例代码演示了如何创建具有 XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT 功能的空间上下文,该功能支持 subsumed_by。

// Check runtime supported capabilities
uint32_t capabilityCount;
CHK_XR(xrEnumerateSpatialCapabilitiesEXT(instance, systemId, 0, &capabilityCount, nullptr));
std::vector<XrSpatialCapabilityEXT> capabilities(capabilityCount);
CHK_XR(xrEnumerateSpatialCapabilitiesEXT(instance, systemId, capabilityCount, &capabilityCount, capabilities.data()));

if (std::find(capabilities.begin(), capabilities.end(), XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT) == capabilities.end()) {
  return;
}

std::vector<XrSpatialComponentTypeEXT> planeTrackingComponents {
  XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT,
  XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT,
  XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID,
};

// Create capability config for plane tracking
XrSpatialCapabilityConfigurationPlaneTrackingEXT planeTrackingConfig {
  .type = XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT,
  .next = nullptr,
  .capability = XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT,
  .enabledComponentCount = (uint32_t)planeTrackingComponents.size(),
  .enabledComponents = planeTrackingComponents.data(),
};

// Create spatial context
std::vector<const XrSpatialCapabilityConfigurationBaseHeaderEXT*> capabilityConfigs;
capabilityConfigs.push_back(reinterpret_cast<const XrSpatialCapabilityConfigurationBaseHeaderEXT*>(&planeTrackingConfig));

XrSpatialContextCreateInfoEXT contextCreateInfo{
  .type = XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT,
  .next = nullptr,
  .capabilityConfigCount = (uint32_t)capabilityConfigs.size(),
  .capabilityConfigs = capabilityConfigs.data(),
};

CHK_XR(xrCreateSpatialContextAsyncEXT(session, &contextCreateInfo, &future))

// Completes creating spatial context
XrCreateSpatialContextCompletionEXT contextCompletion{
XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT};

CHK_XR(xrCreateSpatialContextCompleteEXT(session, future, &contextCompletion))

查询组件数据

以下示例代码演示了如何从使用 XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT 配置的上下文中查询 subsumed_by 组件数据。

// Create Discovery Snapshot
XrSpatialDiscoverySnapshotCreateInfoEXT discoverySnapshotCreateInfo {
  .type = XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT,
};

CHK_XR(xrCreateSpatialDiscoverySnapshotAsyncEXT (
spatialContext, &discoverySnapshotCreateInfo, &future));

// Poll the state till snapshot it's ready.
waitUntilReady(future);

// Complete async operation.
XrCreateSpatialDiscoverySnapshotCompletionInfoEXT
  createSnapshotCompletionInfo {
    .type   = XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT,
    .baseSpace = space,
    .time      = updateTime,
    .future    = future,
  };

XrCreateSpatialDiscoverySnapshotCompletionEXT completion {
  XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT};

CHK_XR(xrCreateSpatialDiscoverySnapshotCompleteEXT(
           spatialContext, &createSnapshotCompletionInfo,
           &completion));

if(completion.futureResult != XR_SUCCESS) return;

// Query subsumed_by components
std::array<XrSpatialComponentTypeEXT, 1> enabledComponents = {
  XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID
};

XrSpatialComponentDataQueryConditionEXT queryCond {
  .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT,
  .componentTypeCount = 1,
  .componentTypes     = enabledComponents.data(),
};

XrSpatialComponentDataQueryResultEXT queryResult {
  .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT,
};

CHK_XR(xrQuerySpatialComponentDataEXT(
           completion.snapshot, &queryCond, &queryResult));

// Query again with allocated memory
std::vector<XrSpatialEntityIdEXT> subsumedUniqueIds;
subsumedUniqueIds.resize(queryResult.entityIdCountOutput);
XrSpatialComponentSubsumedByListANDROID subsumedByList {
  .type = XR_TYPE_SPATIAL_COMPONENT_SUBSUMED_BY_LIST_ANDROID,
  .subsumedUniqueIdCount = static_cast<uint32_t>(subsumedUniqueIds.size()),
  .subsumedUniqueIds = subsumedUniqueIds.data(),
};

queryResult.next = &subsumedByList;
CHK_XR(xrQuerySpatialComponentDataEXT(
           completion.snapshot, &queryCond, &queryResult));

std::vector<XrSpatialEntityEXT> subsumedEntities;
for(uint32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
    // access planes[i] for merged plane id
    XrSpatialEntityIdEXT entityId = queryResult.entityIds[i];
    XrSpatialEntityIdEXT subsumedUniqueId = subsumedUniqueIds[i];

    // create handle via entityId
  XrSpatialEntityFromIdCreateInfoEXT entityCreateInfo {
    .type = XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT,
    .entityId = entityId,
  };

  XrSpatialEntityEXT entity = XR_NULL_HANDLE;
  xrCreateSpatialEntityFromIdEXT(spatialContext, &entityCreateInfo, &entity);

  subsumedEntities.push_back(entity);
}

// Cleanup
xrDestroySpatialSnapshotEXT(completion.snapshot);

过滤掉 subsumed 实体

以下示例代码演示了如何使用过滤器从发现快照中过滤掉附加了 subsumed_by 组件的实体,以及如何查询 subsuming 实体的实体 ID。

// Init filter
XrSpatialDiscoveryUniqueEntitiesFilterANDROID filter {
  .type = XR_TYPE_SPATIAL_DISCOVERY_UNIQUE_ENTITIES_FILTER_ANDROID,
};

// Chain filter to the snapshot create info
// WARNING: Chain the filter while include subsumed_by component in the
// componentTypes is invalid
XrSpatialDiscoverySnapshotCreateInfoEXT discoverySnapshotCreateInfo {
  .type = XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT,
  .next = &filter
};

waitUntilReady(future);

// Complete async operation.
XrCreateSpatialDiscoverySnapshotCompletionInfoEXT
  createSnapshotCompletionInfo {
    .type   = XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT,
    .baseSpace = space,
    .time      = updateTime,
    .future    = future,
  };

XrCreateSpatialDiscoverySnapshotCompletionEXT completion {
  XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT};

CHK_XR(xrCreateSpatialDiscoverySnapshotCompleteEXT(
           spatialContext, &createSnapshotCompletionInfo,
           &completion));

if(completion.futureResult != XR_SUCCESS) return;

// Subsumed entities has already been filtered out in this snapshot,now query
// Bounded2D to render subsuming planes
std::vector<XrSpatialComponentTypeEXT> queryComponents {
  XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT,
};

XrSpatialComponentDataQueryConditionEXT queryCond {
  .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT,
  .componentTypeCount = 1,
  .componentTypes     = queryComponents.data(),
};

XrSpatialComponentDataQueryResultEXT queryResult {
  .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT,
};

CHK_XR(xrQuerySpatialComponentDataEXT(
           completion.snapshot, &queryCond, &queryResult));

// Chain Bounded2D list
std::vector<XrSpatialBounded2DDataEXT> bounded2dData;
bounded2dData.resize(queryResult.entityIdCountOutput);

XrSpatialComponentBounded2DListEXT bounded2dList {
  .type = XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT,
  .boundCount = static_cast<uint32_t>(bounded2dData.size()),
  .bounds = bounded2dData.data(),
};

// Query again
queryResult.next = &bounded2dList;
CHK_XR(xrQuerySpatialComponentDataEXT(
           completion.snapshot, &queryCond, &queryResult));

std::vector<XrSpatialEntityEXT> subsumingPlanes;
for(uint32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
  // access planes[i] for merged plane id
  XrSpatialEntityIdEXT entityId = queryResult.entityIds[i];

  // create handle via entityId.
  XrSpatialEntityFromIdCreateInfoEXT entityCreateInfo {
    .type = XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT,
    .entityId = entityId,
  };

  XrSpatialEntityEXT entity = XR_NULL_HANDLE;
  xrCreateSpatialEntityFromIdEXT(spatialContext, &entityCreateInfo, &entity);

  subsumingPlanes.push_back(entity);
}

// Cleanup
xrDestroySpatialSnapshotEXT(completion.snapshot);

新结构

新枚举常量

  • XR_ANDROID_SPATIAL_COMPONENT_SUBSUMED_BY_EXTENSION_NAME
  • XR_ANDROID_spatial_component_subsumed_by_SPEC_VERSION
  • 扩展 XrSpatialComponentTypeEXT

    • XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID
  • 扩展 XrStructureType

    • XR_TYPE_SPATIAL_COMPONENT_SUBSUMED_BY_LIST_ANDROID
    • XR_TYPE_SPATIAL_DISCOVERY_UNIQUE_ENTITIES_FILTER_ANDROID

问题

版本记录

  • 修订版 1,2025-11-19(Brian Chen)

    • 初始扩展程序说明。