XR_EXT_spatial_anchor
Name String
XR_EXT_spatial_anchor
Extension Type
Instance extension
Registered Extension Number
763
Revision
1
Ratification Status
Ratified
Extension and Version Dependencies
XR_EXT_spatial_entity
Contributors
Nihav Jain, Google
Natalie Fleury, Meta
Yuichi Taguchi, Meta
Ron Bessems, Meta
Yin Li, Microsoft
Jimmy Alamparambil, ByteDance
Zhipeng Liu, ByteDance
Jun Yan, ByteDance
Overview
This extension builds on XR_EXT_spatial_entity and allows applications to create spatial anchors, which are arbitrary points in the user’s physical environment that will then be tracked by the runtime. The runtime should then adjust the position and orientation of the anchor’s origin over time as needed, independent of all other spaces & anchors, to ensure that it maintains its original mapping to the real world.
An anchor that tracks a given position and orientation within an XrSpatialContextEXT is represented as a spatial entity with (or "that has") the XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT component.
Benefit of using anchors
As the runtime’s understanding of the user’s physical environment updates throughout the lifetime of an XrSpatialContextEXT , virtual objects may appear to drift away from where they were placed by the application, which impacts the application’s realism and the quality of the user’s experience. By creating an anchor close to where a virtual object is placed, and then always rendering that virtual object relative to its anchor, an application can ensure that each virtual object appears to stay at the same position and orientation in the physical environment. Also, unlike certain reference spaces, anchors are unaffected by system-level recentering.
Runtime support
If the runtime supports spatial anchors, it must indicate this by enumerating XR_SPATIAL_CAPABILITY_ANCHOR_EXT in xrEnumerateSpatialCapabilitiesEXT .
Configuration
The XrSpatialCapabilityConfigurationAnchorEXT structure is defined as:
typedef struct XrSpatialCapabilityConfigurationAnchorEXT {
XrStructureType type;
const void* next;
XrSpatialCapabilityEXT capability;
uint32_t enabledComponentCount;
const XrSpatialComponentTypeEXT* enabledComponents;
} XrSpatialCapabilityConfigurationAnchorEXT;
Member Descriptions
typeis the XrStructureType of this structure.nextisNULLor a pointer to the next structure in a structure chain.capabilityis an XrSpatialCapabilityEXT .enabledComponentCountis auint32_tdescribing the count of elements in theenabledComponentsarray.enabledComponentsis a pointer to an array of XrSpatialComponentTypeEXT .
Applications can enable the XR_SPATIAL_CAPABILITY_ANCHOR_EXT spatial capability by including a pointer to an XrSpatialCapabilityConfigurationAnchorEXT structure in XrSpatialContextCreateInfoEXT :: capabilityConfigs .
The runtime must return XR_ERROR_VALIDATION_FAILURE if capability is not XR_SPATIAL_CAPABILITY_ANCHOR_EXT .
Valid Usage (Implicit)
- The
XR_EXT_spatial_anchorextension must be enabled prior to using XrSpatialCapabilityConfigurationAnchorEXT -
typemust beXR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT -
nextmust beNULLor a valid pointer to the next structure in a structure chain -
capabilitymust be a valid XrSpatialCapabilityEXT value -
enabledComponentsmust be a pointer to an array ofenabledComponentCountvalid XrSpatialComponentTypeEXT values - The
enabledComponentCountparameter must be greater than0
Guaranteed Components
A runtime that supports XR_SPATIAL_CAPABILITY_ANCHOR_EXT must provide the following spatial components as guaranteed components of all entities created or discovered by this capability and must enumerate them in xrEnumerateSpatialCapabilityComponentTypesEXT :
XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT
Anchor Component
Component Data
The XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT uses XrPosef for its data which provides the position and orientation of the anchor.
Component List Structure to Query Data
The XrSpatialComponentAnchorListEXT structure is defined as:
typedef struct XrSpatialComponentAnchorListEXT {
XrStructureType type;
void* next;
uint32_t locationCount;
XrPosef* locations;
} XrSpatialComponentAnchorListEXT;
Member Descriptions
typeis the XrStructureType of this structure.nextisNULLor a pointer to the next structure in a structure chain.locationCountis auint32_tdescribing the count of elements in thelocationsarray.locationsis an array of XrPosef .
The runtime must return XR_ERROR_VALIDATION_FAILURE from xrQuerySpatialComponentDataEXT if XrSpatialComponentAnchorListEXT is in the XrSpatialComponentDataQueryResultEXT :: next chain but XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT is not included in XrSpatialComponentDataQueryConditionEXT :: componentTypes .
The runtime must return XR_ERROR_SIZE_INSUFFICIENT from xrQuerySpatialComponentDataEXT if locationCount is less than XrSpatialComponentDataQueryResultEXT :: entityIdCountOutput .
Valid Usage (Implicit)
- The
XR_EXT_spatial_anchorextension must be enabled prior to using XrSpatialComponentAnchorListEXT -
typemust beXR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT -
nextmust beNULLor a valid pointer to the next structure in a structure chain -
locationsmust be a pointer to an array oflocationCountXrPosef structures - The
locationCountparameter must be greater than0
Configuration
If XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT is enumerated in XrSpatialCapabilityComponentTypesEXT :: componentTypes for some capability, an application can enable it by including the enumerant in the XrSpatialCapabilityConfigurationBaseHeaderEXT :: enabledComponents list of the XrSpatialCapabilityConfigurationBaseHeaderEXT derived structure of the capability that supports this component.
This component does not require any special configuration to be included in the XrSpatialCapabilityConfigurationBaseHeaderEXT :: next chain.
Creating a Spatial Anchor
The xrCreateSpatialAnchorEXT function is defined as:
XrResult xrCreateSpatialAnchorEXT(
XrSpatialContextEXT spatialContext,
const XrSpatialAnchorCreateInfoEXT* createInfo,
XrSpatialEntityIdEXT* anchorEntityId,
XrSpatialEntityEXT* anchorEntity);
Parameter Descriptions
spatialContextis an XrSpatialContextEXT previously created using xrCreateSpatialContextAsyncEXT .createInfois a pointer to an XrSpatialAnchorCreateInfoEXT .anchorEntityIdis a pointer to anXrSpatialEntityIdEXTin which the anchor entity’s ID is returned.anchorEntityis a pointer to an XrSpatialEntityEXT in which the anchor entity’s handle is returned.
The application can create a spatial anchor by using xrCreateSpatialAnchorEXT .
To get updated component data for an anchor, pass the value populated in anchorEntity into the XrSpatialUpdateSnapshotCreateInfoEXT :: entities when creating a snapshot. The application can use anchorEntityId to uniquely identify this anchor in the XrSpatialComponentDataQueryResultEXT :: entityIds array when using xrQuerySpatialComponentDataEXT .
The runtime must return XR_ERROR_VALIDATION_FAILURE from xrCreateSpatialAnchorEXT if XR_SPATIAL_CAPABILITY_ANCHOR_EXT was not configured for spatialContext . See Configuration for how to configure an XrSpatialContextEXT for the XR_SPATIAL_CAPABILITY_ANCHOR_EXT capability.
The anchor represented by anchorEntity is only valid for the lifetime of spatialContext , or until the application calls xrDestroySpatialEntityEXT on it, whichever comes first. Other extensions may offer functions to persist this newly created anchor across multiple XrSession or to share it across process boundaries with other applications.
A newly created anchor, until destroyed, must be discoverable in its parent spatial context. This means that the runtime must include anchorEntityId in the snapshot created using xrCreateSpatialDiscoverySnapshotAsyncEXT for spatialContext if the anchor matches the discovery criteria set in XrSpatialDiscoverySnapshotCreateInfoEXT . The newly created anchor may also be discoverable in other spatial contexts configured with XR_SPATIAL_CAPABILITY_ANCHOR_EXT , although with a different XrSpatialEntityIdEXT since a particular XrSpatialEntityIdEXT is unique to its XrSpatialContextEXT .
Valid Usage (Implicit)
- The
XR_EXT_spatial_anchorextension must be enabled prior to calling xrCreateSpatialAnchorEXT -
spatialContextmust be a valid XrSpatialContextEXT handle -
createInfomust be a pointer to a valid XrSpatialAnchorCreateInfoEXT structure -
anchorEntityIdmust be a pointer to anXrSpatialEntityIdEXTvalue -
anchorEntitymust be a pointer to an XrSpatialEntityEXT handle
Return Codes
XR_SUCCESSXR_SESSION_LOSS_PENDING
XR_ERROR_FUNCTION_UNSUPPORTEDXR_ERROR_HANDLE_INVALIDXR_ERROR_INSTANCE_LOSTXR_ERROR_LIMIT_REACHEDXR_ERROR_OUT_OF_MEMORYXR_ERROR_POSE_INVALIDXR_ERROR_RUNTIME_FAILUREXR_ERROR_SESSION_LOSTXR_ERROR_TIME_INVALIDXR_ERROR_VALIDATION_FAILUREXR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID(ifXR_ANDROID_spatial_entity_bound_anchoris enabled)XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT(ifXR_ANDROID_spatial_entity_bound_anchoris enabled)
The XrSpatialAnchorCreateInfoEXT structure is defined as:
typedef struct XrSpatialAnchorCreateInfoEXT {
XrStructureType type;
const void* next;
XrSpace baseSpace;
XrTime time;
XrPosef pose;
} XrSpatialAnchorCreateInfoEXT;
Member Descriptions
typeis the XrStructureType of this structure.nextisNULLor a pointer to the next structure in a structure chain.baseSpaceis the XrSpace in whichposeis applied.timeis theXrTimeat whichbaseSpaceis located (andposeis applied).poseis the location for the anchor entity.
Valid Usage (Implicit)
- The
XR_EXT_spatial_anchorextension must be enabled prior to using XrSpatialAnchorCreateInfoEXT -
typemust beXR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT -
nextmust beNULLor a valid pointer to the next structure in a structure chain . See also: XrSpatialAnchorParentANDROID -
baseSpacemust be a valid XrSpace handle
Query Anchor Pose
After the anchor is created, the runtime should then adjust its position and orientation over time relative to other spaces in order to maintain the best possible alignment to its original real-world location, even if that changes the anchor’s relationship to the original XrSpatialAnchorCreateInfoEXT :: baseSpace used to initialize it.
The application can use xrCreateSpatialUpdateSnapshotEXT with the anchor’s XrSpatialEntityEXT to create a new XrSpatialSnapshotEXT and then query the XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT component from that snapshot using xrQuerySpatialComponentDataEXT . The application can add XrSpatialComponentAnchorListEXT to XrSpatialComponentDataQueryResultEXT :: next to retrieve the latest location data for the anchors.
The runtime may set the tracking state of a newly created anchor to XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT . The application must only read the anchor entity’s state provided in XrSpatialComponentDataQueryResultEXT :: entityStates and the entity’s anchor component data if the tracking state is XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT .
Guidelines For Using Anchors
- Each anchor’s pose adjusts independent of any other anchor or space. Separately anchored virtual objects may shift or rotate relative to each other, breaking the spatial hierarchy in cases where these virtual objects are expected to stay in place relative to each other. For such cases, the application should reuse the same anchor for all virtual objects that do not move relative to each other.
- Application should destroy any XrSpatialEntityEXT handles for anchors that are no longer being used in order to free up the resources the runtime may be using to track those anchors.
Example Code
Configure Anchor Capability
The following example demonstrates how to configure the anchor capability when creating a spatial context.
// Create a spatial spatial context
XrSpatialContextEXT spatialContext{};
{
std::vector<XrSpatialComponentTypeEXT> enabledComponents = {
XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT,
};
XrSpatialCapabilityConfigurationAnchorEXT anchorConfig{XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT};
anchorConfig.capability = XR_SPATIAL_CAPABILITY_ANCHOR_EXT;
anchorConfig.enabledComponentCount = enabledComponents.size();
anchorConfig.enabledComponents = enabledComponents.data();
std::array<XrSpatialCapabilityConfigurationBaseHeaderEXT*, 1> capabilityConfigs = {
reinterpret_cast<XrSpatialCapabilityConfigurationBaseHeaderEXT*>(&anchorConfig),
};
XrSpatialContextCreateInfoEXT spatialContextCreateInfo{XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT};
spatialContextCreateInfo.capabilityConfigCount = capabilityConfigs.size();
spatialContextCreateInfo.capabilityConfigs = capabilityConfigs.data();
XrFutureEXT createContextFuture;
CHK_XR(xrCreateSpatialContextAsyncEXT(session, &spatialContextCreateInfo, &createContextFuture));
waitUntilReady(createContextFuture);
XrCreateSpatialContextCompletionEXT completion{XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT};
CHK_XR(xrCreateSpatialContextCompleteEXT(session, createContextFuture, &completion));
if (completion.futureResult != XR_SUCCESS) {
return;
}
spatialContext = completion.spatialContext;
}
// ...
// Create spatial anchors and get their latest pose in the frame loop.
// ...
CHK_XR(xrDestroySpatialContextEXT(spatialContext));
Create Spatial Anchor & Get Its Location
The following example demonstrates how to create a spatial anchor & gets its pose every frame.
XrSpatialAnchorCreateInfoEXT createInfo{XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT};
createInfo.baseSpace = localSpace;
createInfo.time = predictedDisplayTime;
createInfo.pose = {{0, 0, 0, 1}, {1, 1, 1}};
XrSpatialEntityIdEXT spatialAnchorEntityId;
XrSpatialEntityEXT spatialAnchorEntity;
CHK_XR(xrCreateSpatialAnchorEXT(spatialContext, &createInfo, &spatialAnchorEntityId, &spatialAnchorEntity));
auto updateAnchorLocation = [&](XrTime time) {
// We want to get updated data for all components of the entities, so skip specifying componentTypes.
XrSpatialUpdateSnapshotCreateInfoEXT snapshotCreateInfo{XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT};
snapshotCreateInfo.entityCount = 1;
snapshotCreateInfo.entities = &spatialAnchorEntity;
snapshotCreateInfo.baseSpace = localSpace;
snapshotCreateInfo.time = time;
XrSpatialSnapshotEXT snapshot;
CHK_XR(xrCreateSpatialUpdateSnapshotEXT(spatialContext, &snapshotCreateInfo, &snapshot));
// Query for the entities that have the anchor component on them.
std::array<XrSpatialComponentTypeEXT, 1> componentsToQuery {XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT};
XrSpatialComponentDataQueryConditionEXT queryCond{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT};
queryCond.componentTypeCount = componentsToQuery.size();
queryCond.componentTypes = componentsToQuery.data();
XrSpatialComponentDataQueryResultEXT queryResult{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT};
CHK_XR(xrQuerySpatialComponentDataEXT(snapshot, &queryCond, &queryResult));
std::vector<XrSpatialEntityIdEXT> entityIds(queryResult.entityIdCountOutput);
std::vector<XrSpatialEntityTrackingStateEXT> entityStates(queryResult.entityIdCountOutput);
queryResult.entityIdCapacityInput = entityIds.size();
queryResult.entityIds = entityIds.data();
queryResult.entityStateCapacityInput = entityStates.size();
queryResult.entityStates = entityStates.data();
// query for the pose data
std::vector<XrPosef> locations(queryResult.entityIdCountOutput);
XrSpatialComponentAnchorListEXT locationList{XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT};
locationList.locationCount = locations.size();
locationList.locations = locations.data();
queryResult.next = &locationList;
CHK_XR(xrQuerySpatialComponentDataEXT(snapshot, &queryCond, &queryResult));
for (int32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
if (entityStates[i] == XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT) {
// Pose for entity entityIds[i] is locations[i].
}
}
CHK_XR(xrDestroySpatialSnapshotEXT(snapshot));
};
while (1) {
// ...
// For every frame in frame loop
// ...
XrFrameState frameState; // previously returned from xrWaitFrame
const XrTime time = frameState.predictedDisplayTime;
updateAnchorLocation(time);
// ...
// Finish frame loop
// ...
}
CHK_XR(xrDestroySpatialEntityEXT(spatialAnchorEntity));
New Commands
New Structures
- XrSpatialAnchorCreateInfoEXT
- XrSpatialCapabilityConfigurationAnchorEXT
Extending XrSpatialComponentDataQueryResultEXT :
New Enum Constants
XR_EXT_SPATIAL_ANCHOR_EXTENSION_NAMEXR_EXT_spatial_anchor_SPEC_VERSIONExtending XrSpatialCapabilityEXT :
XR_SPATIAL_CAPABILITY_ANCHOR_EXT
Extending XrSpatialComponentTypeEXT :
XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT
Extending XrStructureType :
XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXTXR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXTXR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT
Issues
Why does xrCreateSpatialAnchorEXT output an entity ID as well as an entity handle?
- Resolved
- Answer: The xrCreateSpatialAnchorEXT function could very well have provided just the entity id as the output and applications could create an entity handle for that id using xrCreateSpatialEntityFromIdEXT . However, given the typical usage of an anchor where applications query the anchor pose every frame, it becomes a good candidate to be used in an "update snapshot", which requires entity handles as input. Anticipating this typical usecase, xrCreateSpatialAnchorEXT performs xrCreateSpatialEntityFromIdEXT on behalf of the application and provides it with the entity handle to use with xrCreateSpatialUpdateSnapshotEXT .
Version History
Revision 1, 2024-07-10 (Nihav Jain, Google)
- Initial extension description