Develop
Develop
Select your platform

Space Sharing Native

Updated: May 19, 2025

Overview

The OpenXR Space Sharing API enables a user to share their XR Scene with their guest players, creating a shared playspace. By sharing their space, the app benefits from shared scene understanding and shared coordinates among all the players. Scene Understanding allows the placement of virtual content that adapts to the geometry and semantics of the user’s environment. For example, a game can adapt its content to the size of the walls and location of furniture so the players can have a shared MR experience. See Space Sharing Overview to learn more about Scene Understanding use cases, and how to utilize it for a mixed reality experience.
Space Sharing uses the same extensions and APIs that enable Group Sharing of Spatial Anchors. It enhances the functions in the existing extensions by allowing sharing rooms - XrSpace with RoomLayout component. Similar to sharing Spatial Anchors to Groups, the following extensions are needed to use Space Sharing
  • XR_META_spatial_entity_sharing
  • XR_META_spatial_entity_group_sharing
  • XR_FB_spatial_entity_query
A host device can use xrShareSpacesMETA in the XR_META_spatial_entity_sharing extension to share rooms to a group. This is done by providing a list of XrSpace with RoomLayout component in its input struct XrShareSpacesInfoMETA, and an UUID of a group. Guest devices can use the xrQuerySpacesFB API in the XR_FB_spatial_entity_query extension to query the shared rooms with a Group UUID filter. Upon receiving the results of the shared room, guest devices can then fetch all the scene data in the shared rooms, such as the walls, floor, ceiling, and furniture.
  • To understand room (aka Scene Model) in OpenXR, and the API to query them, see OpenXR Scene API Reference.
  • As the host, sharing the room to others is not available via xrShareSpacesFB, more details can be found in User Sharing API. User Sharing API is where apps can share spatial entities with specific Oculus User IDs.
  • As a guest querying for a room shared by the host is not available via discovery API

Prerequisites

To check your version of the Meta Horizon OS:
  • In the headset, go to Settings > System > Software Update.
  • Check the version.
  • If the version is not v74 or higher, update the software to the latest available version

Setup and Implementation

Follow the instructions here to set up your project for OpenXR.
To get started with Space Sharing, you will need to set up your app for required Meta Quest permissions, and then follow the Space Sharing flow in your implementation.
Setup
  • Declare permission to the Android manifest
  • Add extension headers
  • Enable extensions
Implementation
  • Acquire function pointers
  • Host app query rooms, shares them to a group and sends group UUID to guests
  • Guest app gets the group UUID and query the shared room by group UUID

Declare permission in the Android manifest

You can find the general specifications for your app’s AndroidManifest.xml file in Android Manifest Settings in the native development guide. Space Sharing requires three Android permissions to work.
Space Sharing requires three Android permissions to work. Add the permissions to your app’s AndroidManifest.xml:
<uses-permission android:name="com.oculus.permission.USE_ANCHOR_API"/>
<uses-permission android:name="com.oculus.permission.IMPORT_EXPORT_IOT_MAP_DATA"/>
<uses-permission android:name="com.oculus.permission.USE_SCENE"/>
See Android Manifest for Spatial Anchor about the USE_ANCHOR_API and IMPORT_EXPORT_IOT_MAP_DATA permissions, and Spatial Data Permission for the USE_SCENE permission

Creating an Instance and Session

To use the extension APIs, you must create an OpenXR instance and session. For details on creating OpenXR instances, read Creating Instances and Sessions.

Enable extensions

Enable the following extensions:
const char* const requiredExtensionNames[] = {
    XR_FB_SPATIAL_ENTITY_EXTENSION_NAME,
    XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME,
    XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME,
    XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME,
    XR_META_SPATIAL_ENTITY_SHARING_EXTENSION_NAME,
    XR_META_SPATIAL_ENTITY_GROUP_SHARING_EXTENSION_NAME,
    XR_FB_SCENE_EXTENSION_NAME,
    XR_META_SPATIAL_ENTITY_MESH_EXTENSION_NAME
  };

Acquire function pointers

The code below demonstrates how to set up function pointers to the OpenXR APIs required space sharing. Define their function pointers:
struct ovrExtensionFunctionPointers {
  PFN_xrEnumerateSpaceSupportedComponentsFB xrEnumerateSpaceSupportedComponentsFB = nullptr;
  PFN_xrGetSpaceComponentStatusFB xrGetSpaceComponentStatusFB = nullptr;
  PFN_xrSetSpaceComponentStatusFB xrSetSpaceComponentStatusFB = nullptr;
  PFN_xrGetSpaceUuidFB xrGetSpaceUuidFB = nullptr;
  PFN_xrQuerySpacesFB xrQuerySpacesFB = nullptr;
  PFN_xrRetrieveSpaceQueryResultsFB xrRetrieveSpaceQueryResultsFB = nullptr;

  PFN_xrShareSpacesMETA xrShareSpacesMETA = nullptr;

  PFN_xrGetSpaceBoundingBox2DFB xrGetSpaceBoundingBox2DFB = nullptr;
  PFN_xrGetSpaceBoundingBox3DFB xrGetSpaceBoundingBox3DFB = nullptr;
  PFN_xrGetSpaceSemanticLabelsFB xrGetSpaceSemanticLabelsFB = nullptr;
  PFN_xrGetSpaceBoundary2DFB xrGetSpaceBoundary2DFB = nullptr;
  PFN_xrGetSpaceRoomLayoutFB xrGetSpaceRoomLayoutFB = nullptr;
  PFN_xrGetSpaceContainerFB xrGetSpaceContainerFB = nullptr;
  PFN_xrGetSpaceTriangleMeshMETA xrGetSpaceTriangleMeshMETA = nullptr;
};

/// Hook up extensions. Following shows one example
  OXR(xrGetInstanceProcAddr(
    instance,
    "xrEnumerateSpaceSupportedComponentsFB",
   (PFN_xrVoidFunction*)(&app.FunPtrs.xrEnumerateSpaceSupportedComponentsFB)));

/// Hook up more here

Space sharing flow

Overview

A typical Space Sharing flow requires the following steps between a host device and guest devices:
  • The host device uses xrQuerySpacesFB API to query one or more rooms using the xrLayoutComponent filter. If no room is found, the app can use the xrRequestSceneCaptureFB API to start Scene capture.
  • The host device sets the Sharable Component on the room (xrSpace) found with a xrRoomLayoutFB component and uses xrShareSpacesMETA API to share the rooms to a group.
  • The host sends the group’s UUID to the guest devices via an app managed networking. Upon receiving the group’s UUID, guest devices use xrQuerySpacesFB API with a group UUID filter to retrieve the Shared Rooms and their Scene data
In the following code example, the app is implemented in the ovrApp class, similar to a struct defined in the XRSamples\XRSpatialAnchor sample app.

The Host Query Rooms

Before sharing a room, the host first needs to query a captured room. See Scene Model/Spatial Entity Query for information about querying spatial entities. The following code example uses the RoomLayout component filter and xrQuerySpacesFB API to query the room.
XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = {
       XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB, nullptr, XR_SPACE_STORAGE_LOCATION_LOCAL_FB};

// Build a room layout component filter to query XrSpace of type room only
XrSpaceComponentFilterInfoFB componentFilterInfo = {
       XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB, &storageLocationFilterInfo, componentType};

// Build a query info struct with the room layout filter
XrSpaceQueryInfoFB queryInfo = {
       XR_TYPE_SPACE_QUERY_INFO_FB,
       nullptr,
       XR_SPACE_QUERY_ACTION_LOAD_FB,
       MAX_PERSISTENT_SPACES,
       0,
       (XrSpaceFilterInfoBaseHeaderFB*)&componentFilterInfo,
       nullptr};

XrAsyncRequestIdFB requestId;

// call query API to get the room
OXR(app.FunPtrs.xrQuerySpacesFB(
       app.Session, (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId));

The Host polls events to get the shared room

Set up OpenXR events handling and poll events to handle the XrEventDataSpaceQueryResultsAvailableFB event and use xrRetrieveSpaceQueryResultsFB API to retrieve the room. Since we are only querying rooms using the RoomLayout component filter above, we will only receive room XrSpace in the results.
void ovrApp::HandleXrEvents() {
XrEventDataBuffer eventDataBuffer = {}

// Poll for events
for (;;) {
   		XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer);
   baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER;
   		baseEventHeader->next = NULL;
   XrResult r;
   		OXR(r = xrPollEvent(instance, &eventDataBuffer));
   if (r != XR_SUCCESS) {
   		   break;
    	}

switch (baseEventHeader->type) {
case XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB: {
        const auto resultsAvailable =
(XrEventDataSpaceQueryResultsAvailableFB*)baseEventHeader;
        XrResult res = XR_SUCCESS;
        XrSpaceQueryResultsFB queryResults{XR_TYPE_SPACE_QUERY_RESULTS_FB};
        queryResults.resultCapacityInput = 0;
        queryResults.resultCountOutput = 0;
        queryResults.results = nullptr;

        // First call to get the resultCountOutput
 res = FunPtrs.xrRetrieveSpaceQueryResultsFB(Session, resultsAvailable->requestId, &queryResults);
       if (res != XR_SUCCESS) {
break;
}

       // Second call to get the results data
       std::vector<XrSpaceQueryResultFB> results(queryResults.resultCountOutput);
queryResults.resultCapacityInput = results.size();
       queryResults.resultCountOutput = 0;
       queryResults.results = results.data();

       res = FunPtrs.xrRetrieveSpaceQueryResultsFB(
                   Session, resultsAvailable->requestId, &queryResults);
if (res != XR_SUCCESS) {
break;
}

        for (uint32_t i = 0; i < queryResults.resultCountOutput; ++i) {
          auto& result = results[i];
			 // result.space is the room, enable shareale component
          SetSharebleComponent(result.space);
        }
}

Host sets Sharable Component on the room space

After we get the XrSpace representing the room, we need to set its Sharable Component so that it can be shared to other users
void ovrApp::SetSharableComponent(XrSpace space) {
if (IsComponentSupported(space, XR_SPACE_COMPONENT_TYPE_SHARABLE_FB)) {
    XrSpaceComponentStatusSetInfoFB request = {
         XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB,
         nullptr,
  XR_SPACE_COMPONENT_TYPE_SHARABLE_FB,
         XR_TRUE,
  0
};

XrAsyncRequestIdFB requestId;
        FunPtrs.xrSetSpaceComponentStatusFB(space, &request, &requestId);
}
}

Host shares the room to a group

After we set the Sharable Component to the room using the example code above, we can use xrShareSpacesMETA API to share it to a group. Note that xrShareSpacesMETA is also used to share Spatial Anchors (see details in Shared Spatial Anchors). We can use the same function to share one room or multiple rooms. The following example shares a vector of rooms to a group:
XrAsyncRequestIdFB ShareSpaces(
  ovrApp& app,
  const std::vector<XrSpace>& spaces) {
  for (const XrSpace space : spaces) {
    if (!app.IsComponentEnabled(space, XR_SPACE_COMPONENT_TYPE_ Shareble_FB)) {
      return kInvalidRequestId;
    }
  }

  XrUuidEXT groupuuid = GenerateRandomGroupUUID();

  XrShareSpacesRecipientGroupsMETA recipientGroupInfo = {
       XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META, nullptr};
   recipientGroupInfo.groupCount = 1;
   recipientGroupInfo.groups = &groupuuid;

   XrShareSpacesInfoMETA info = {XR_TYPE_SHARE_SPACES_INFO_META};
   info.spaceCount = spaces.size();
   info.spaces = const_cast<XrSpace*>(spaces.data());

   info.recipientInfo = (const XrShareSpacesRecipientBaseHeaderMETA*)&recipientGroupInfo;
   XrAsyncRequestIdFB shareRequestId;

   XrResult r;
   OXR(r = app.FunPtrs.xrShareSpacesMETA(app.Session, &info, &shareRequestId));
   if (r == XR_SUCCESS) {
       return shareRequestId;
   } else {
       return kInvalidRequestId;
   }
}

XrUuidEXT GenerateRandomGroupUUID() {
   XrUuidEXT groupuuid;

   // Seed the random number generator with the current time, rand()
   srand((unsigned int)time(nullptr));

   for (uint8_t i = 0; i < XR_UUID_SIZE; i++) {
       groupuuid.data[i] = rand();
   }

   return groupuuid;
}
  • To share the room and all the scene anchors in it, we only need to share the XrSpace representing the room. We do not need to call the Share API on each individual Scene Anchor. Calling xrShareSpacesMETA on individual Scene Anchors will result in an error of XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB.

The Host sends the group UUID to guest devices

After the Host successfully shares the room to the group via the xrShareSpacesMETA API, it can send the group UUID to guest devices via app managed networking. It is up to the app to choose the network library suitable to send the group UUID to guest devices. The app can also use the Colocation Discovery OpenXR API to do so.

The Guest Query the Shared Room

When a guest device receives the group UUID the host shared via the app managed networking, it can use the xrQuerySpacesFB API with a group UUID filter to get the Shared Room. Note that the first time a guest calls xrQuerySpacesFB, the API will trigger a network call to download the Shared Room and the Scene Anchors from the cloud, which can have networking latency. A second call on the same group UUID will get a faster response because it reads from the disk cache.
A Enhanced Spatial Service (ESS) permission dialog may pop-up if the permission has not been granted before. If the permission is not granted, the call will fail. After the user accepts the permission, call xrQuerySpacesFB again to get the results.
QuerySpaceByGroupUuid(ovrApp& app, XrUuidEXT groupUuid) {
   // Specify the locationFilter to query from CLOUD
   XrSpaceStorageLocationFilterInfoFB locationFilterInfo = {
       XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB, nullptr, XR_SPACE_STORAGE_LOCATION_CLOUD_FB};

   // Specify the group UUID in the group UUID filter
   XrSpaceGroupUuidFilterInfoMETA filterInfo = {XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META};
   filterInfo.groupUuid = groupUuid;
   filterInfo.next = &locationFilterInfo;

   // Construct the query info struct
   XrSpaceQueryInfoFB info = {
       XR_TYPE_SPACE_QUERY_INFO_FB,
       nullptr,
       XR_SPACE_QUERY_ACTION_LOAD_FB,
       MAX_PERSISTENT_SPACES,
       0,
       (XrSpaceFilterInfoBaseHeaderFB*)&filterInfo,
       nullptr};

   XrAsyncRequestIdFB requestId;
   XrResult r;
   OXR(r = app.FunPtrs.xrQuerySpacesFB(
           app.Session, (XrSpaceQueryInfoBaseHeaderFB*)&info, &requestId));
   return true;
}
  • Shared Rooms cannot be retrieved using the xrDiscoverSpacesMETA. They can only be retrieved using the xrQuerySpacesFB with a group UUID filter.
  • After the Shared Room is retrieved, the app can use xrGetSpaceContainerFB to get the space container associated with the room, retrieve all the UUIDs of Scene Anchors in the room from the space container, and use xrQuerySpacesFB with a UUID filter again to get all the shared Scene Anchors in the room.

Troubleshooting

Space Sharing uses the common API and error codes with Shared Spatial Anchors. Many troubleshooting techniques for Shared Spatial Anchors also apply to Space Sharing.

Ensuring Enhanced Spatial Services is enabled

The device setting ‘Enhanced Spatial Services’ must be enabled for Space Sharing to work. Users can find it under Settings > Privacy > Device Permissions > Spatial Data. Your app can detect when this setting is disabled and inform users to turn it on. Your app will receive the error code XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB upon sharing rooms when this setting is disabled. Your app should check for this error and inform users that enabling Enhanced Spatial Services is required for Space Sharing to work. For all sharing errors and troubleshooting, refer to Shared Spatial Anchors Troubleshooting.

Known Issues

The automatic prompting for the Enhanced Spatial Services permission does not work reliably in some versions of the OS. Please ensure all users enable this permission manually ahead of running the sample.

Shared Space Querying and Alignment

This document outlines two known issues that may occur when querying or aligning shared scenes in the Space Sharing API. It provides repro steps and mitigation strategies to help resolve these issues.
Issue 1 - Guest Device Unable to Query Shared Space
  • Repro Steps
    • Host device: Calls xrShareSpacesMETA with an XrSpace having RoomLayout component and a group UUID
    • Guest device: Calls xrQuerySpacesFB with the group UUID filter
    • Guest device: May observe that xrQuerySpacesFB does not return the XrSpace shared from the host
  • Mitigation To increase the chances of the guest device being able to query the shared space, follow these steps:
    • On the host side, before calling xrShareSpacesMETA, try to have the device move around more in the area where the space is captured.
    • On the guest side, before calling xrQuerySpacesFB, also try to have the device move around more in the area where the host device captured the space.
    By doing so, both devices will map a more complete and accurate spatial data around the same physical area where the space is captured, which can improve the chances of successful space querying.
Issue 2 - Misaligned space on guest device
  • Repro Steps
    • Host device: Calls xrShareSpacesMETA with an XrSpace having RoomLayout component and a group UUID
    • Guest device: Calls xrQuerySpacesFB with the group UUID filter, and can successfully get the xrSpace shared by the host.
    • Guest device: Observes that the geometry of the space is misaligned with respect to the physical space, or the location of the space is shifted to a different location compared to the space captured by the host.
  • Mitigation To resolve the misalignment issue, follow these steps on the guest device:
    • On the guest device, go to Settings > Privacy > Device Permissions > Clear Physical Space History
    • On the guest device, restart the app and call xrQuerySpacesFB again. If the space cannot be loaded in this step, try to restart the guest device, start the app and call xrQuerySpacesFB again.
    By clearing the physical space history and restarting device, you can ensure that the guest device reloads the shared space with the correct alignment.
Did you find this page helpful?
Thumbs up icon
Thumbs down icon