XR_EXT_spatial_persistence

Name String

XR_EXT_spatial_persistence

Extension Type

Instance extension

Registered Extension Number

764

Revision

1

Ratification Status

Ratified

Extension and Version Dependencies

XR_EXT_spatial_entity
and
XR_EXT_future

Contributors

Nihav Jain, Google
Jared Finder, 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 allows applications to discover and correlate spatial entities across application sessions, OpenXR sessions and multiple OpenXR spatial contexts within a session. The XR_EXT_spatial_entity extension established that an entity within an XrSpatialContextEXT is represented by an XrSpatialEntityIdEXT . This extension extends on that concept by establishing that an entity, if persisted, is represented by an XrUuid across application and OpenXR sessions i.e. an application can use the XrUuid provided by this extension to identify an entity across sessions. This extension also provides useful overlaps with the XR_EXT_spatial_entity extension to discover persisted entities in the user’s environment and the ability to query their component data.

Spatial Persistence Context

Create a spatial persistence context

XR_DEFINE_HANDLE(XrSpatialPersistenceContextEXT)

The XrSpatialPersistenceContextEXT handle represents the connection to a persistent spatial entity storage.

The xrCreateSpatialPersistenceContextAsyncEXT function is defined as:

XrResult xrCreateSpatialPersistenceContextAsyncEXT(
    XrSession                                   session,
    const XrSpatialPersistenceContextCreateInfoEXT* createInfo,
    XrFutureEXT*                                future);

Parameter Descriptions

An application can create an XrSpatialPersistenceContextEXT handle using the xrCreateSpatialPersistenceContextAsyncEXT function and configure the scope of the persistence context in createInfo .

The runtime must return XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT if XrSpatialPersistenceContextCreateInfoEXT :: scope is not enumerated by xrEnumerateSpatialPersistenceScopesEXT .

If a runtime enforces a permission system to control application access to the persistence storage represented by XrSpatialPersistenceContextEXT , then the runtime must return XR_ERROR_PERMISSION_INSUFFICIENT if those permissions have not been granted to this application.

Valid Usage (Implicit)

Return Codes

Success

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

Failure

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_LIMIT_REACHED
  • XR_ERROR_OUT_OF_MEMORY
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT
  • XR_ERROR_VALIDATION_FAILURE

The XrSpatialPersistenceContextCreateInfoEXT structure is defined as:

typedef struct XrSpatialPersistenceContextCreateInfoEXT {
    XrStructureType                 type;
    const void*                     next;
    XrSpatialPersistenceScopeEXT    scope;
} XrSpatialPersistenceContextCreateInfoEXT;

Member Descriptions

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
  • scope is an XrSpatialPersistenceScopeEXT defining the storage scope of the persistence context.

The XrSpatialPersistenceContextCreateInfoEXT structure describes the information to create an XrSpatialPersistenceContextEXT handle.

Valid Usage (Implicit)

typedef enum XrSpatialPersistenceScopeEXT {
    XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT = 1,
    XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT = 1000781000,
    XR_SPATIAL_PERSISTENCE_SCOPE_MAX_ENUM_EXT = 0x7FFFFFFF
} XrSpatialPersistenceScopeEXT;

The XrSpatialPersistenceScopeEXT enumeration identifies the different types of persistence context scopes.

The enums have the following meanings:

Enum Description

XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT

Provides the application with read-only access (i.e. application cannot modify the store associated with this scope) to spatial entities persisted and managed by the system. The application can use the UUID in the persistence component for this scope to correlate entities across spatial contexts and device reboots.

XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT

Persistence operations and data access is limited to spatial anchors, on the same device, for the same user and same app (Added by the XR_EXT_spatial_persistence_operations extension)

The xrEnumerateSpatialPersistenceScopesEXT function is defined as:

XrResult xrEnumerateSpatialPersistenceScopesEXT(
    XrInstance                                  instance,
    XrSystemId                                  systemId,
    uint32_t                                    persistenceScopeCapacityInput,
    uint32_t*                                   persistenceScopeCountOutput,
    XrSpatialPersistenceScopeEXT*               persistenceScopes);

Parameter Descriptions

  • instance is a handle to an XrInstance .
  • systemId is the XrSystemId whose spatial persistence scopes will be enumerated.
  • persistenceScopeCapacityInput is the capacity of the persistenceScopes array, or 0 to indicate a request to retrieve the required capacity.
  • persistenceScopeCountOutput is the number of persistence scopes, or the required capacity in the case that persistenceScopeCapacityInput is insufficient.
  • persistenceScopes is an array of XrSpatialPersistenceScopeEXT . It can be NULL if persistenceScopeCapacityInput is 0.
  • See Buffer Size Parameters chapter for a detailed description of retrieving the required persistenceScopes size.

The application can enumerate the list of spatial persistence scopes supported by a given XrSystemId using xrEnumerateSpatialPersistenceScopesEXT .

Valid Usage (Implicit)

  • The XR_EXT_spatial_persistence extension must be enabled prior to calling xrEnumerateSpatialPersistenceScopesEXT
  • instance must be a valid XrInstance handle
  • persistenceScopeCountOutput must be a pointer to a uint32_t value
  • If persistenceScopeCapacityInput is not 0 , persistenceScopes must be a pointer to an array of persistenceScopeCapacityInput XrSpatialPersistenceScopeEXT values

Return Codes

Success

  • XR_SUCCESS

Failure

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SIZE_INSUFFICIENT
  • XR_ERROR_SYSTEM_INVALID
  • XR_ERROR_VALIDATION_FAILURE

The xrCreateSpatialPersistenceContextCompleteEXT function is defined as:

XrResult xrCreateSpatialPersistenceContextCompleteEXT(
    XrSession                                   session,
    XrFutureEXT                                 future,
    XrCreateSpatialPersistenceContextCompletionEXT* completion);

Parameter Descriptions

xrCreateSpatialPersistenceContextCompleteEXT completes the asynchronous operation started by xrCreateSpatialPersistenceContextAsyncEXT . The runtime must return XR_ERROR_FUTURE_PENDING_EXT if future is not in ready state. The runtime must return XR_ERROR_FUTURE_INVALID_EXT if future has already been completed or cancelled.

Valid Usage (Implicit)

Return Codes

Success

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

Failure

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_FUTURE_INVALID_EXT
  • XR_ERROR_FUTURE_PENDING_EXT
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_LIMIT_REACHED
  • XR_ERROR_OUT_OF_MEMORY
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_VALIDATION_FAILURE

The XrCreateSpatialPersistenceContextCompletionEXT structure is defined as:

typedef struct XrCreateSpatialPersistenceContextCompletionEXT {
    XrStructureType                         type;
    void*                                   next;
    XrResult                                futureResult;
    XrSpatialPersistenceContextResultEXT    createResult;
    XrSpatialPersistenceContextEXT          persistenceContext;
} XrCreateSpatialPersistenceContextCompletionEXT;

Member Descriptions

Future Return Codes

futureResult values:

Success

  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING

Failure

  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_OUT_OF_MEMORY
  • XR_ERROR_LIMIT_REACHED
  • XR_ERROR_PERMISSION_INSUFFICIENT

If futureResult and createResult are both success codes, persistenceContext must be valid. If persistenceContext is valid, it must remain so within the lifecycle of xrCreateSpatialPersistenceContextAsyncEXT :: session or until the application uses xrDestroySpatialPersistenceContextEXT with persistenceContext , whichever comes first.

The runtime must set createResult only if futureResult is a success code.

Valid Usage (Implicit)

typedef enum XrSpatialPersistenceContextResultEXT {
    XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT = 0,
    XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_ENTITY_NOT_TRACKING_EXT = -1000781001,
    XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_PERSIST_UUID_NOT_FOUND_EXT = -1000781002,
    XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_MAX_ENUM_EXT = 0x7FFFFFFF
} XrSpatialPersistenceContextResultEXT;

The XrSpatialPersistenceContextResultEXT enumeration identifies the different types of result codes for a persistence operation. Failures during persistence operations are not always in control of the application and this enumeration is used for conveying such cases. Similar to XrResult , success codes in the XrSpatialPersistenceContextResultEXT enumeration are non-negative values, and failure codes are negative values.

The enums have the following meanings:

Enum Description

XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT

The persistence context operation was a success.

XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_ENTITY_NOT_TRACKING_EXT

The persistence operation failed because the entity could not be tracked by the runtime. (Added by the XR_EXT_spatial_persistence_operations extension)

XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_PERSIST_UUID_NOT_FOUND_EXT

The provided persist UUID was not found in the storage. (Added by the XR_EXT_spatial_persistence_operations extension)

Destroy the spatial persistence context

The xrDestroySpatialPersistenceContextEXT function is defined as:

XrResult xrDestroySpatialPersistenceContextEXT(
    XrSpatialPersistenceContextEXT              persistenceContext);

Parameter Descriptions

The application can use xrDestroySpatialPersistenceContextEXT to release the persistenceContext handle when it is finished with spatial persistence tasks.

The runtime must not destroy the underlying resources for persistenceContext when xrDestroySpatialPersistenceContextEXT is called if there are any valid XrSpatialContextEXT handles that persistenceContext was linked to via XrSpatialContextPersistenceConfigEXT . This is because the persistence context’s resources are still used by the spatial context for discovering persisted entities. Destroying the persistence context handle in such a situation only removes the application’s access to these resources.

The resources for a destroyed XrSpatialPersistenceContextEXT must be freed when all the XrSpatialContextEXT handles the persistence context was linked to are destroyed.

Valid Usage (Implicit)

Thread Safety

  • Access to persistenceContext , and any child handles, must be externally synchronized

Return Codes

Success

  • XR_SUCCESS

Failure

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_RUNTIME_FAILURE

Discover persisted entities

Persistence component

Persisted spatial entities have the persistence component on them which the runtime must include in the discovery and update snapshots if XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT is enabled during the creation of XrSpatialContextEXT and included in XrSpatialDiscoverySnapshotCreateInfoEXT :: componentTypes or XrSpatialUpdateSnapshotCreateInfoEXT :: componentTypes .

Component Data

The XrSpatialPersistenceDataEXT structure is defined as:

typedef struct XrSpatialPersistenceDataEXT {
    XrUuid                          persistUuid;
    XrSpatialPersistenceStateEXT    persistState;
} XrSpatialPersistenceDataEXT;

Member Descriptions

Valid Usage (Implicit)

typedef enum XrSpatialPersistenceStateEXT {
    XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT = 1,
    XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT = 2,
    XR_SPATIAL_PERSISTENCE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
} XrSpatialPersistenceStateEXT;

The XrSpatialPersistenceStateEXT enumeration identifies the different states of the persisted uuid.

The enums have the following meanings:

Enum Description

XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT

The persisted UUID has been successfully loaded from the storage.

XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT

The persisted UUID was not found in the storage and was either removed from it or never was in it.

Component list structure to query data

The XrSpatialComponentPersistenceListEXT structure is defined as:

typedef struct XrSpatialComponentPersistenceListEXT {
    XrStructureType                 type;
    void*                           next;
    uint32_t                        persistDataCount;
    XrSpatialPersistenceDataEXT*    persistData;
} XrSpatialComponentPersistenceListEXT;

Member Descriptions

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
  • persistDataCount is a uint32_t describing the count of elements in the persistData array.
  • persistData is an array of XrSpatialPersistenceDataEXT .

The runtime must return XR_ERROR_VALIDATION_FAILURE from xrQuerySpatialComponentDataEXT if XrSpatialComponentPersistenceListEXT is in the next chain of XrSpatialComponentDataQueryResultEXT :: next but XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT is not included in XrSpatialComponentDataQueryConditionEXT :: componentTypes .

The runtime must return XR_ERROR_SIZE_INSUFFICIENT from xrQuerySpatialComponentDataEXT if persistDataCount is less than XrSpatialComponentDataQueryResultEXT :: entityIdCountOutput .

Unlike the other components, the runtime must set the data for XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT regardless of the XrSpatialEntityTrackingStateEXT .

Valid Usage (Implicit)

Configuration

If XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT is enumerated in XrSpatialCapabilityComponentTypesEXT :: componentTypes for some capability, the application can enable it by including the enum in the XrSpatialCapabilityConfigurationBaseHeaderEXT :: enabledComponents list. This component does not require any special configuration to be included in the next chain of XrSpatialCapabilityConfigurationBaseHeaderEXT . If the application is including XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT in the enabled component list, it must also include XrSpatialContextPersistenceConfigEXT in the next chain of XrSpatialContextCreateInfoEXT otherwise the runtime must return XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT from xrCreateSpatialContextAsyncEXT .

Configure spatial context with persistence contexts

The XrSpatialContextPersistenceConfigEXT structure is defined as:

typedef struct XrSpatialContextPersistenceConfigEXT {
    XrStructureType                          type;
    const void*                              next;
    uint32_t                                 persistenceContextCount;
    const XrSpatialPersistenceContextEXT*    persistenceContexts;
} XrSpatialContextPersistenceConfigEXT;

Member Descriptions

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
  • persistenceContextCount is a uint32_t describing the count of elements in the persistenceContexts array.
  • persistenceContexts is an an array of XrSpatialPersistenceContextEXT .

An application can add XrSpatialContextPersistenceConfigEXT to the next chain of XrSpatialContextCreateInfoEXT . This will configure the created XrSpatialContextEXT with persistenceContexts and allow the application to discover the spatial entities persisted in the storage represented by the XrSpatialPersistenceContextEXT handles in persistenceContexts .

Valid Usage (Implicit)

Create discovery snapshot

Discover entities with specific UUIDs

The XrSpatialDiscoveryPersistenceUuidFilterEXT structure is defined as:

typedef struct XrSpatialDiscoveryPersistenceUuidFilterEXT {
    XrStructureType    type;
    const void*        next;
    uint32_t           persistedUuidCount;
    const XrUuid*      persistedUuids;
} XrSpatialDiscoveryPersistenceUuidFilterEXT;

Member Descriptions

  • type is the XrStructureType of this structure.
  • next is NULL or a pointer to the next structure in a structure chain. No such structures are defined in core OpenXR or this extension.
  • persistedUuidCount is a uint32_t describing the count of elements in the persistedUuids array
  • persistedUuids is an array of XrUuid .

The application can use XrSpatialDiscoveryPersistenceUuidFilterEXT in the next chain of XrSpatialDiscoverySnapshotCreateInfoEXT to scope the discovery operation to just the entities whose persisted UUIDs are in the set of the UUIDs provided in persistedUuids .

If the application adds XrSpatialDiscoveryPersistenceUuidFilterEXT in the next chain of XrSpatialDiscoverySnapshotCreateInfoEXT but the xrCreateSpatialDiscoverySnapshotAsyncEXT :: spatialContext was not configured with any XrSpatialPersistenceContextEXT using XrSpatialContextPersistenceConfigEXT , the runtime must return XR_ERROR_VALIDATION_FAILURE from xrCreateSpatialDiscoverySnapshotAsyncEXT .

The runtime must treat the XrSpatialDiscoveryPersistenceUuidFilterEXT filter as an 'AND' condition with any other filters provided in XrSpatialDiscoverySnapshotCreateInfoEXT or its next chain. The runtime must treat the persistedUuids array itself as an 'OR' condition i.e. filter for entities that have any of the UUIDs provided in that array.

The runtime must include one entry in the created snapshot for each of the UUIDs in persistedUuids for which it was able to determine the XrSpatialPersistenceStateEXT state at this time.

  • If the runtime has successfully found the UUID in its storage, then -

  • If the runtime has determined that the UUID is not present in its storage (regardless of whether that UUID was never in the storage or has was present once but has since been unpersisted), then -

  • If the runtime was not able to determine if the UUID is present in its storage or not, it must not include it in the snapshot.

The application can also use XrSpatialDiscoveryPersistenceUuidFilterEXT in the next chain of XrSpatialComponentDataQueryConditionEXT to query for entities of specific UUIDs in existing snapshots. When used with XrSpatialComponentDataQueryConditionEXT , if XrSpatialDiscoveryPersistenceUuidFilterEXT :: persistedUuids contains any XrUuid that is not in the XrSpatialSnapshotEXT , the runtime must not include an entry for that XrUuid in the query result. Also, the order (sequence) of entities in the query result may not match the order of UUIDs provided in XrSpatialDiscoveryPersistenceUuidFilterEXT :: persistedUuids . Application should include XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT in XrSpatialComponentDataQueryConditionEXT :: componentTypes and XrSpatialComponentPersistenceListEXT in the next chain of XrSpatialComponentDataQueryResultEXT and then check the XrSpatialPersistenceDataEXT :: persistUuid of the each query result index to understand which UUID the current result index corresponds to.

Valid Usage (Implicit)

Discover all persisted entities

If the application uses xrCreateSpatialDiscoverySnapshotAsyncEXT without XrSpatialDiscoveryPersistenceUuidFilterEXT and with an XrSpatialContextEXT which has been configured with an XrSpatialPersistenceContextEXT , then the runtime must include those entities in the created snapshot that are persisted in the storage represented by XrSpatialPersistenceContextEXT and satisfy the filters provided in XrSpatialDiscoverySnapshotCreateInfoEXT . For those entities -

New Object Types

New Commands

New Structures

New Enums

New Enum Constants

  • XR_EXT_SPATIAL_PERSISTENCE_EXTENSION_NAME
  • XR_EXT_spatial_persistence_SPEC_VERSION
  • Extending XrObjectType :

    • XR_OBJECT_TYPE_SPATIAL_PERSISTENCE_CONTEXT_EXT
  • Extending XrResult :

    • XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT
  • Extending XrSpatialComponentTypeEXT :

    • XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT
  • Extending XrStructureType :

    • XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT
    • XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT
    • XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT
    • XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT
    • XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT

Example Code

Create Persistence Context

// Check if the required persistence scope supported
uint32_t scopeCount;
CHK_XR(xrEnumerateSpatialPersistenceScopesEXT(instance, systemId, 0, &scopeCount, nullptr));
std::vector<XrSpatialPersistenceScopeEXT> persistenceScopes(scopeCount);
CHK_XR(xrEnumerateSpatialPersistenceScopesEXT(instance, systemId, scopeCount, &scopeCount, persistenceScopes.data()));

if (std::find(persistenceScopes.begin(), persistenceScopes.end(), XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT) == persistenceScopes.end()) {
  return;
}

XrSpatialPersistenceContextEXT persistenceContext{};

XrSpatialPersistenceContextCreateInfoEXT persistenceContextCreateInfo{XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT};
persistenceContextCreateInfo.scope = XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT;
XrFutureEXT createContextFuture;
CHK_XR(xrCreateSpatialPersistenceContextAsyncEXT(session, &persistenceContextCreateInfo, &createContextFuture));

waitUntilReady(createContextFuture);

XrCreateSpatialPersistenceContextCompletionEXT completion{XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT};
CHK_XR(xrCreateSpatialPersistenceContextCompleteEXT(session, createContextFuture, &completion));
if (completion.futureResult != XR_SUCCESS || completion.createResult != XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT) {
  return;
}

persistenceContext = completion.persistenceContext;

// ...
// Connect persistence context to a spatial context and discover persisted entities.
// ...

CHK_XR(xrDestroySpatialPersistenceContextEXT(persistenceContext));

Connect Persistence Context to a Spatial Context

// Note: Anchor capability is just used as an example here. Persistence can be
// supported by other capabilities too. xrEnumerateSpatialCapabilityComponentTypesEXT() can
// be used to check if a certain capability supports persistence.
if (!isSpatialCapabilitySupported(instance, systemId, XR_SPATIAL_CAPABILITY_ANCHOR_EXT)) {
  return;
}

const bool supportsPersistenceComponent = isSpatialComponentSupported(instance, systemId, XR_SPATIAL_CAPABILITY_ANCHOR_EXT, XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT);

// Create a spatial spatial context
XrSpatialContextEXT spatialContext{};
{

  std::vector<XrSpatialComponentTypeEXT> enabledComponents = {
    XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT,
  };

  if (supportsPersistenceComponent) {
    enabledComponents.push_back(XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_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();

  XrSpatialContextPersistenceConfigEXT persistenceConfig{XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT};
  persistenceConfig.persistenceContextCount = 1;
  persistenceConfig.persistenceContexts = &persistenceContext;

  if (supportsPersistenceComponent) {
    spatialContextCreateInfo.next = &persistenceConfig;
  }

  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;
}

// ...
// Discover persisted anchors.
// ...

CHK_XR(xrDestroySpatialContextEXT(spatialContext));

Discover all persisted entities

XrFutureEXT future = XR_NULL_FUTURE_EXT;

// We want to look for entities that have the following components.
std::vector<XrSpatialComponentTypeEXT> snapshotComponents = {
  XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT,
};

auto discoverSpatialEntities = [&](XrSpatialContextEXT spatialContext, XrTime time) {
  XrSpatialDiscoverySnapshotCreateInfoEXT snapshotCreateInfo{XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT};
  snapshotCreateInfo.componentTypeCount = snapshotComponents.size();
  snapshotCreateInfo.componentTypes = snapshotComponents.data();
  CHK_XR(xrCreateSpatialDiscoverySnapshotAsyncEXT(spatialContext, &snapshotCreateInfo, &future));

  waitUntilReady(future);

  XrCreateSpatialDiscoverySnapshotCompletionInfoEXT completionInfo{XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT};
  completionInfo.baseSpace = localSpace;
  completionInfo.time = time;
  completionInfo.future = future;

  XrCreateSpatialDiscoverySnapshotCompletionEXT completion{XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT};
  CHK_XR(xrCreateSpatialDiscoverySnapshotCompleteEXT(spatialContext, &completionInfo, &completion));
  if (completion.futureResult == XR_SUCCESS) {

    // Query for the semantic label component data
    XrSpatialComponentDataQueryConditionEXT queryCond{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT};
    queryCond.componentTypeCount = snapshotComponents.size();
    queryCond.componentTypes = snapshotComponents.data();

    XrSpatialComponentDataQueryResultEXT queryResult{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT};
    CHK_XR(xrQuerySpatialComponentDataEXT(completion.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();

    std::vector<XrSpatialPersistenceDataEXT> persistenceData(queryResult.entityIdCountOutput);
    XrSpatialComponentPersistenceListEXT persistenceDataList{XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT};
    persistenceDataList.persistDataCount = persistenceData.size();
    persistenceDataList.persistData = persistenceData.data();
    queryResult.next = &persistenceDataList;

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

    for (int32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
      // persistenceData[i].persistUuid is the UUID of the persisted entity whose entity ID is entityIds[i].
      // The persistenceData array is essentially the uuids persisted in the scope that the current
      // XrSpatialPersistenceContextEXT is configured with.
    }

    CHK_XR(xrDestroySpatialSnapshotEXT(completion.snapshot));
  }
};

while (1) {
  // ...
  // For every frame in frame loop
  // ...

  XrFrameState frameState;  // previously returned from xrWaitFrame
  const XrTime time = frameState.predictedDisplayTime;

  // Poll for the XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT event
  XrEventDataBuffer event = {XR_TYPE_EVENT_DATA_BUFFER};
  XrResult result = xrPollEvent(instance, &event);
  if (result == XR_SUCCESS) {
      switch (event.type) {
          case XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT: {
              const XrEventDataSpatialDiscoveryRecommendedEXT& eventdata =
                  *reinterpret_cast<XrEventDataSpatialDiscoveryRecommendedEXT*>(&event);
              // Discover spatial entities for the context that we received the "discovery
              // recommended" event for.
              discoverSpatialEntities(eventdata.spatialContext, time);
              break;
          }
      }
  }

  // ...
  // Finish frame loop
  // ...
}

Discover entities with specific UUIDs

XrFutureEXT future = XR_NULL_FUTURE_EXT;

// Load up the uuids that the app has stored on its own i.e. the uuids it is interested in.
std::vector<XrUuid> uuidsStoredByApp = loadPersistedUuids();

// We want to look for entities that have the following components.
std::vector<XrSpatialComponentTypeEXT> snapshotComponents = {
  XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT,
};

auto discoverSpatialEntities = [&](XrSpatialContextEXT spatialContext, XrTime time) {
  XrSpatialDiscoveryPersistenceUuidFilterEXT persistenceFilter{XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT};
  persistenceFilter.persistedUuidCount = uuidsStoredByApp.size();
  persistenceFilter.persistedUuids = uuidsStoredByApp.data();

  XrSpatialDiscoverySnapshotCreateInfoEXT snapshotCreateInfo{XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT};
  snapshotCreateInfo.componentTypeCount = snapshotComponents.size();
  snapshotCreateInfo.componentTypes = snapshotComponents.data();
  snapshotCreateInfo.next = &persistenceFilter;

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

  waitUntilReady(future);

  XrCreateSpatialDiscoverySnapshotCompletionInfoEXT completionInfo{XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT};
  completionInfo.baseSpace = localSpace;
  completionInfo.time = time;
  completionInfo.future = future;

  XrCreateSpatialDiscoverySnapshotCompletionEXT completion{XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT};
  CHK_XR(xrCreateSpatialDiscoverySnapshotCompleteEXT(spatialContext, &completionInfo, &completion));
  if (completion.futureResult == XR_SUCCESS) {

    // Query for the semantic label component data
    XrSpatialComponentDataQueryConditionEXT queryCond{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT};
    queryCond.componentTypeCount = snapshotComponents.size();
    queryCond.componentTypes = snapshotComponents.data();

    XrSpatialComponentDataQueryResultEXT queryResult{XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT};
    CHK_XR(xrQuerySpatialComponentDataEXT(completion.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();

    std::vector<XrSpatialPersistenceDataEXT> persistenceData(queryResult.entityIdCountOutput);
    XrSpatialComponentPersistenceListEXT persistenceDataList{XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT};
    persistenceDataList.persistDataCount = persistenceData.size();
    persistenceDataList.persistData = persistenceData.data();
    queryResult.next = &persistenceDataList;

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

    for (int32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
      if (persistenceData[i].persistState == XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT) {
        // persistenceData[i].persistUuid, requested by the app, is present in the persistence scope
        // and its corresponding entity ID and state are entityIds[i] & entityStates[i] respectively.
      } else if (persistenceData[i].persistState == XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT) {
        // persistenceData[i].persistUuid, requested by the app, is NOT present in the persistence scope
        // and its corresponding entity ID (entityIds[i]) would be XR_NULL_SPATIAL_ENTITY_ID_EXT
        // and tracking state (entityStates[i]) would be XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT.
      }
    }

    CHK_XR(xrDestroySpatialSnapshotEXT(completion.snapshot));
  }
};

while (1) {
  // ...
  // For every frame in frame loop
  // ...

  XrFrameState frameState;  // previously returned from xrWaitFrame
  const XrTime time = frameState.predictedDisplayTime;

  // Poll for the XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT event
  XrEventDataBuffer event = {XR_TYPE_EVENT_DATA_BUFFER};
  XrResult result = xrPollEvent(instance, &event);
  if (result == XR_SUCCESS) {
      switch (event.type) {
          case XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT: {
              const XrEventDataSpatialDiscoveryRecommendedEXT& eventdata =
                  *reinterpret_cast<XrEventDataSpatialDiscoveryRecommendedEXT*>(&event);
              // Discover spatial entities for the context that we received the "discovery
              // recommended" event for.
              discoverSpatialEntities(eventdata.spatialContext, time);
              break;
          }
      }
  }

  // ...
  // Finish frame loop
  // ...
}

Issues

Version History

  • Revision 1, 2024-08-29 (Nihav Jain, Google)

    • Initial extension description