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:
Create XrInstance.
Use XrInstance to get XrSystemId.
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:
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:
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.
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.
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.