Develop
Develop
Select your platform

Creating Instances and Sessions

XR devices vary in how they handle instances and sessions. On a PC with multiple connected headsets, apps use an OpenXR instance to communicate with the runtime. A session is tied to a specific VR system, which must be picked before starting the session.
To create a session, you must follow this order of operations:
  1. Create XrInstance.
  2. Use XrInstance to get XrSystemId.
  3. Create a session using XrSystemId.
Running OpenXR on a device requires setting up the Graphics API and receiving platform information. Various platform and graphics API extensions require you to define these flags before including openxr.h.
For Meta Quest headsets, use these flags:
  • Graphics API Header Control: XR_USE_GRAPHICS_API_OPENGL_ES or XR_USE_GRAPHICS_API_VULKAN
  • Window System Header Control: XR_USE_PLATFORM_ANDROID
For Rift, Rift S, and Meta Quest headsets that use Link, use these flags:
  • Graphics API Header Control: XR_USE_GRAPHICS_API_D3D11, XR_USE_GRAPHICS_API_D3D12, XR_USE_GRAPHICS_API_OPENGL or XR_USE_GRAPHICS_API_VULKAN
  • Window System Header Control: XR_USE_PLATFORM_WIN32
The hello_xr sample app wraps the platform-specific instance creation parameters in a struct that implements an abstract class, named IPlatformPlugin. This is not required for OpenXR apps but is an implementation-specific detail of hello_xr to support multiple platforms.
This instance creation helper struct for Android platforms is defined as:
struct AndroidPlatformPlugin : public IPlatformPlugin {
    AndroidPlatformPlugin(const std::shared_ptr<Options>& /*unused*/, const std::shared_ptr<PlatformData>& data) {
        instanceCreateInfoAndroid = {XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR};
        instanceCreateInfoAndroid.applicationVM = data->applicationVM;
        instanceCreateInfoAndroid.applicationActivity = data->applicationActivity;
    }

    std::vector<std::string> GetInstanceExtensions() const override { return {XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME}; }

    XrBaseInStructure* GetInstanceCreateExtension() const override { return (XrBaseInStructure*)&instanceCreateInfoAndroid; }

    XrInstanceCreateInfoAndroidKHR instanceCreateInfoAndroid;
};
The XrBaseInStructure struct pointer helps to return an arbitrary OpenXR struct. Because structs always start with type, you can safely infer the actual type later.
The instanceCreateInfoAndroid member variable stores Android-specific information for calling xrCreateInstance and is returned by GetInstanceCreateExtension.
The GetInstanceExtensions method returns a vector of OpenXR instance-level extension names required by the specified platform (implementation-specific).
Note: Initialize the loader with platform-specific parameters before loading. These parameters are specified in the KHR loader extensions, XR_KHR_loader_init and XR_KHR_loader_init_android. Apps must first get a function pointer for xrInitializeLoaderKHR through xrGetInstanceProcAddress with a null instance pointer, and then call xrInitializeLoaderKHR with the XrLoaderInitInfoAndroidKHR struct defined in XR_KHR_loader_init_android.

API Layers and Extensions

Both API layers and extensions add functionality to OpenXR. The key difference is that API layers cannot add new OpenXR functions or change the shape of the API. API layers provide additional functionality by intercepting OpenXR functions from the layer above and then performing different operations than would otherwise be performed without the layer. Extensions can add new functions and modify existing ones. API layers and extensions are enabled when creating an instance.
The hello_xr sample app enumerates available instance extension properties by calling xrEnumerateInstanceExtensionProperties for a given API layer:
// Write out extension properties for a given layer.
const auto logExtensions = [](const char* layerName, int indent = 0) {
    uint32_t instanceExtensionCount;
    CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, 0, &instanceExtensionCount, nullptr));

    std::vector<XrExtensionProperties> extensions(instanceExtensionCount, {XR_TYPE_EXTENSION_PROPERTIES});

    CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, (uint32_t)extensions.size(), &instanceExtensionCount, extensions.data()));

    ...
};
The app loops through and outputs these for debugging purposes:
const std::string indentStr(indent, ' ');
Log::Write(Log::Level::Verbose, Fmt("%sAvailable Extensions: (%d)", indentStr.c_str(), instanceExtensionCount));
for (const XrExtensionProperties& extension : extensions) {
    Log::Write(Log::Level::Verbose, Fmt("%s  Name=%s SpecVersion=%d", indentStr.c_str(), extension.extensionName, extension.extensionVersion));
}
OpenXR provides the xrEnumerateApiLayerProperties function and the XrApiLayerProperties struct to determine available API layers and their properties.
uint32_t layerCount;
CHECK_XRCMD(xrEnumerateApiLayerProperties(0, &layerCount, nullptr));

std::vector<XrApiLayerProperties> layers(layerCount, {XR_TYPE_API_LAYER_PROPERTIES});

CHECK_XRCMD(xrEnumerateApiLayerProperties((uint32_t)layers.size(), &layerCount, layers.data()));

Create Instance

The hello_xr app ensures the instance is not initialized before proceeding.
CHECK(m_instance == XR_NULL_HANDLE);
Initially, m_instance is defined as:
XrInstance m_instance{XR_NULL_HANDLE};
It receives available extensions based on the platform and graphics plugins and creates a union of platform and graphics-related extensions in a vector called extensions (implementation-specific). To create an instance, use a XrInstanceCreateInfo struct that stores the following:
  • enabled API Layer count
  • enabled API Layer names
  • enabled extension count
  • enabled extension names
Pass this information and the instance handle to the xrCreateInstance function. This call creates and enables an XrInstance and then initializes global API layers and extensions.
XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO};
createInfo.next = m_platformPlugin->GetInstanceCreateExtension();
createInfo.enabledExtensionCount = (uint32_t)extensions.size();
createInfo.enabledExtensionNames = extensions.data();

strcpy(createInfo.applicationInfo.applicationName, "HelloXR");
createInfo.applicationInfo.apiVersion = XR_API_VERSION_1_0;

CHECK_XRCMD(xrCreateInstance(&createInfo, &m_instance));

System Initialization

This occurs after you retrieve the form factor. You can achieve this by having a valid instance and an initially null system ID, checked with:
CHECK(m_instance != XR_NULL_HANDLE);
CHECK(m_systemId == XR_NULL_SYSTEM_ID);
The app calls xrGetSystem to retrieve a system ID.
XrSystemGetInfo systemInfo{XR_TYPE_SYSTEM_GET_INFO};
systemInfo.formFactor = m_formFactor;
CHECK_XRCMD(xrGetSystem(m_instance, &systemInfo, &m_systemId));
The graphics plugin initializes the device, as seen in the graphicsplugin_opengles.cpp file of the hello-xr app in the InitializeDevice function.

Session Creation

An XrSession is a graphics interaction with a particular XrSystem using a particular graphics binding.
Create sessions by calling the xrCreateSession function. It requires a valid XrSystemId from xrGetSystem and a valid XrSessionCreateInfo struct, returning a handle to the session.
XrSessionCreateInfo createInfo{XR_TYPE_SESSION_CREATE_INFO};
createInfo.next = m_graphicsPlugin->GetGraphicsBinding();
createInfo.systemId = m_systemId;
CHECK_XRCMD(xrCreateSession(m_instance, &createInfo, &m_session));
The GetGraphicsBinding function is specific to the implementation. It returns a pointer to an XrBaseInStructure struct that receives the graphics binding header for session creation. For example, for a Vulkan-based XrSession, the app chains a pointer to XrGraphicsBindingVulkan2KHR with the XrSessionCreateInfo parameter of xrCreateSession.
All code snippets in this document belong to hello_xr sample app, which is developed by The Khronos Group Inc. and licensed under the Apache License, Version 2.0.
Did you find this page helpful?