Copyright (c) 2017-2023, The Khronos Group Inc. All Rights reserved.
This document contains the necessary information for understanding how to develop for, and interact with, the OpenXR loader. Intended use of this document is as a detailed design document and a tool for learning general OpenXR loader behavior.
In the event of any discrepancies between this document and the (OpenXR specification), that document is authoritative.
The key words must, may, can, cannot, should, required, recommend, and optional in this document are to be interpreted as described in RFC 2119.
OpenXR is a layered architecture, made up of the following elements:
The general concepts in this document are applicable to the loaders available for Windows and Linux based systems.
First, let’s look at the OpenXR environment as a whole. The OpenXR application is at the start of the execution chain, and interfaces directly with the OpenXR loader. The loader, in turn, detects, loads, and interacts with any number of OpenXR runtimes and API layers. Each OpenXR runtime controls a complete VR/XR/MR system that an application can choose to interact with. The loader may inject any number of optional API layers between the application and the runtime to augment behavior. As a result, any OpenXR command may involve executing code in a number of different modules, including the loader, API layers, and runtimes.
2.1. Who Should Read This Document
While this document is primarily targeted at developers of OpenXR runtimes or API layers, and those wishing to contribute to the OpenXR loader; the information contained in it may be useful to anyone wanting a better understanding of OpenXR.
The OpenXR API Specification should be used as the primary means of understanding the OpenXR API and all care has been made to not conflict with any element of that document. In any case where this document and the OpenXR API Specification differ, behavior as defined by the OpenXR specification must be considered the correct standard.
2.2. OpenXR Loader
The loader is critical to detecting, exposing, and possibly loading any available OpenXR runtime or API layer on the system. Once setup, the loader is also responsible with managing the proper dispatching of OpenXR commands to each of these components.
This document is intended to provide an overview of the necessary interfaces between the loader and:
In addition, this document also covers various internal design elements of the OpenXR loader.
2.2.1. Goals of the Loader
The loader was designed with the following goals in mind.
It must support one or more OpenXR-capable runtimes on a user’s computer system.
It must support OpenXR API layers (optional modules that can be enabled by an application, developer, or standard system settings).
It must strive to reduce its overall memory and performance impact to an OpenXR application.
2.3. OpenXR API Layers
API layers are optional components that augment the OpenXR system.
They may intercept, evaluate, modify, and insert existing OpenXR commands on
their way from the application down to the runtime.
API layers are implemented as libraries that are enabled in a variety of
ways (including by application request).
All API layers are enabled in an OpenXR system during the
Each API layer may choose to hook (intercept) any OpenXR command which in
turn may be ignored or augmented.
An API layer does not need to intercept all OpenXR commands but only
intercept those commands it desires.
Some examples of features that API layers may expose include:
Validating API usage
Adding the ability to perform API tracing and debugging
Intercept and filter information between the application and the runtime
Because API layers are optional, you may choose to enable API layers for debugging your application, but then disable any API layer usage when you release your product.
For more information on how the OpenXR interacts with API layers, refer to the "API Layer Interaction" section of this document.
2.4. OpenXR Runtimes
OpenXR works with multiple runtimes each supporting one or more devices.
Each OpenXR runtime controls a complete VR/AR/MR system.
Multiple runtimes may be installed on a system, with one being active at
any given time.
The loader discovers the active runtime on the system and loads it.
XrInstance is created during
xrCreateInstance, the active
runtime is associated with the instance.
For more information on how the OpenXR interacts with runtimes, refer to the "Runtime Interaction" section of this document.
2.5. OpenXR Objects
OpenXR uses an object model to control the scope of a particular action or operation. The object to be acted on is usually the first parameter of an OpenXR call. Under the covers, this object is a unique handle to either a class or structure. The contents of each class/structure vary, but each has a means of looking up a dispatch table which is used to direct the functional flow through any enabled API layers and into the appropriate runtime.
Some OpenXR commands don’t actually take an object, such as
2.6. OpenXR Call Chains
When an (optional) API layer is loaded, the loader links together a call
chain that includes each function selected by the layer.
xrCreateInstance time the loader initializes all enabled API layers and
creates call chains for each OpenXR command, with each entry of the
resulting dispatch table pointing to the first element of that chain.
The loader builds an individual dispatch table for the
XrInstance that is
When an application calls an OpenXR command, this typically will first hit a trampoline function in the loader. These trampoline functions are small, simple functions that jump to the appropriate dispatch table entry for the object they are given. Some functions also require an additional loader function called a terminator, which is called after all enabled API layers to process data before proceeding to the appropriate runtime.
The loader only allows a single outstanding
XrInstance and uses the
generated dispatch table for that
XrInstance for all OpenXR API commands.
3. Application Interaction
An application requests a specific version of the OpenXR API when creating
an instance by writing to the "apiVersion" member of the
structure which, in turn, is passed into the
If either the loader or the active runtime do not support the requested
version, they may return an error and fail instance creation.
Refer to the OpenXR API Specification documentation on
xrCreateInstance for more information about this.
3.1. Interfacing with OpenXR Functions
There are two ways an application can choose interface with OpenXR functions through the loader:
Directly linking to the core OpenXR commands exposed and exported by the loader.
Creating an application-managed dispatch table of OpenXR commands by querying the loader for function pointers via
xrGetInstanceProcAddr. This method supports core OpenXR commands, commands of (enabled) OpenXR extensions supported by the runtime, and any commands exposed by enabled API layers.
3.1.1. OpenXR Direct Exports
The loader library on Windows and Linux will directly export all core OpenXR commands for the OpenXR version it supports. When an application links directly to the loader library in this way, the OpenXR calls are simple trampoline functions that jump to the appropriate dispatch table entry for the object provided.
The specific list, in alphabetical order, of OpenXR commands directly exported by the loader for API version 1.0 are:
When an OpenXR application chooses to use one of the exports directly exposed from the OpenXR loader, the call chain will look like one of the following (if the user has enabled two API layers):
The "Special Instance" call chain is used in several places where the loader
has to perform some work before and after any API layers, but prior to
calling the enabled runtime.
three of the main commands that fall into this group.
Most commands do not require a terminator and will use the second call
3.1.2. OpenXR Indirect Linking
With loader indirect linking an application dynamically generates its own
dispatch table of OpenXR commands.
This method allows an application to fail gracefully if the loader cannot be
found, or supports an older version of the API than the application.
To do this, the application uses the appropriate platform specific dynamic
symbol lookup (such as dlsym()) on the loader library to discover the
address of the
Once discovered, this command can be used to query the addresses of all
other OpenXR commands (such as
Then, the application would use its table of function pointers to make the
call into the OpenXR API.
One benefit when an application generates its own dispatch table is the removal of the loader trampoline call from most commands in a call chain, which could potentially increase performance. In that case, most commands would use the following call chain:
3.1.3. OpenXR Loader Library Name
The OpenXR loader’s dynamic library name is dependent on the user’s underlying operating system. The following table shows the loader library names for common OSs:
|Operating System||Loader Library Name|
<major> and <minor> in the above table refer to the major and minor API versions of OpenXR.
3.2. Application API Layer Usage
Applications desiring OpenXR functionality beyond what the core API offers may use various API layers or extensions. An API layer cannot introduce new OpenXR core API commands, but may introduce new extension-specific OpenXR commands that can be queried through the extension interface.
A common use of API layers is for validation which can be enabled by loading the API layer during application development, but not loading the API layer for application release. The overhead cost of validation is completely eliminated when the layer is not loaded.
An application can discover which API layers are available to it with
This will report all API layers that have been discovered by the loader.
The loader looks in various locations to find API layers on the system.
For more information see the API Layer discovery
An API layer or layers may be enabled by an application by passing a list of
names in the
enabledApiLayerNames field of the
xrCreateInstance, the loader constructs a call chain that includes the application specified (enabled) API layers.
Order is important in the
enabledApiLayerNames array; array element 0 is
the topmost (closest to the application) API layer inserted in the chain and
the last array element is closest to the runtime.
See the Overall API Layer Ordering section
for more information on API layer ordering.
Some API layers interact with other API layers in order to perform their tasks. Consult the appropriate API layer documentation when enabling an API layer to ensure that you are using it properly.
3.2.1. Implicit vs Explicit API Layers
Explicit API layers are API layers which are enabled by an application (e.g.
xrCreateInstance function) or by setting a
loader environment variable.
Implicit API layers are API layers which are enabled simply because they exist. An implicit layer found by the loader will be loaded by default, unless specifically disabled.
Some implicit API layer examples include:
A performance monitoring API layer which is enabled while using an external tool.
An application environment API layer (e.g. Steam or a game console) that enables additional features/tools for applications.
A tracing API layer designed to record API commands for future playback.
Because implicit API layers are enabled automatically, they have an
additional requirement over explicit API layers in that they must specify a
"disable environment variable" in the API layer’s
JSON manifest file, with key name
This environment variable when present will disable the implicit API layer.
Implicit API layers are not otherwise visible to or controllable by
Optionally, an implicit API layers may specify an "enable environment variable", in which case the loader will load the implicit API layer only when the enable environment variable is defined in the user’s execution environment. If both enable and disable environment variables are present in the environment, the layer will be disabled.
Both the enable and disable environment variables are specified in the API layer’s JSON manifest file, which is created by the API layer developer.
Discovery of system-installed implicit and explicit API layers is described later in the API Layer Discovery Section. For now, simply know that what distinguishes an API layer as implicit or explicit is dependent on the operating system, as shown in the table below.
|Operating System||Implicit API Layer Identification Method|
Implicit API Layers are located in the Windows Registry under the "ApiLayers\Implicit" key.
Implicit API Layers are located in the "api_layers/implicit.d" folder under any of the common API layer paths.
3.2.2. Forcing API Layer Folders (Desktop Only)
Desktop developers may also override the way the loader searches for API layers. If the developer/user would like to find API layers in a non-standard location, they may define the "XR_API_LAYER_PATH" environmental variable. For more information on using "XR_API_LAYER_PATH", see the Overriding the Default API Layer Paths section under "API Layer Interaction".
3.2.3. Forced Loading of API Layers (Desktop Only)
Desktop developers may want to enable API layers that are not enabled by the
given application they are using.
On Linux and Windows, the environment variable "XR_ENABLE_API_LAYERS" can be
used to enable additional API layers which are not specified (enabled) by
the application at
"XR_ENABLE_API_LAYERS" is a colon (Linux)/semi-colon (Windows) separated
list of API layer names to enable.
Order is relevant with the first API layer in the list being the top-most
API layer (closest to the application) and the last API layer in the list
being the bottom-most API layer (closest to the driver).
See the Overall API Layer Ordering section
for more information.
Application specified API layers and user specified API layers (via environment variables) are aggregated and duplicates removed by the loader when enabling API layers. API layers specified via environment variable are top-most (closest to the application) while API layers specified by the application are bottom-most. If an API layer name appears multiple times in the list of API layers to enable, all occurrences after the first are ignored.
XR_ENABLE_API_LAYERS, simply define the environment variable with a
properly delimited list of API layer names.
This is not the file name, but the name the API layers use when normally
being enabled inside of
This usually takes the form of:
An OpenXR API layer prefix string: "XR_APILAYER_"
The name of the vendor developing the API layer: e.g. "LUNARG_"
A short descriptive name of the API layer: e.g. "test"
This would produce the name of: XR_APILAYER_LUNARG_test
3.2.4. Overall API Layer Ordering
The overall ordering of all API layers by the loader based on the above looks as follows:
As shown in the above image, any implicit API layers will first intercept OpenXR commands, followed by any explicit API layers enabled by environmental variables, finally being intercepted by any explicit API layers directly enabled by the application. Whether or not the API layers continue into a loader terminator call, or directly into a runtime depends on the call chain.
Ordering may also be important internal to the list of explicit API layers. Some API layers may be dependent on other behavior being implemented before or after the loader calls it.
3.3. Application Usage of Extensions
Extensions are optional functionality provided by the loader, an API layer,
or a runtime.
Extensions can modify the behavior of the OpenXR API and need to be
specified and registered with Khronos.
These extensions can be created by a runtime vendor to expose new runtime
and/or XR hardware functionality, or by an API layer writer to expose some
internal feature, or by the loader to add additional loader functionality.
Information about various extensions can be found in the
OpenXR API Specification, and
openxr.h header file.
To use extension functionality, the application should call
xrEnumerateInstanceExtensionProperties to determine if the extension is
Then it must enable the extension in
If an application fails to determine whether an extension is available, and
xrCreateInstance with that extension name in the list, the
xrCreateInstance call will fail if it’s not supported.
xrEnumerateInstanceExtensionProperties with NULL passed in
for "layerName", the loader discovers and aggregates all extensions from
implicit API layers (if allowable by OS), runtimes, and itself before
reporting them to the application.
If "layerName" is not NULL, and "layerName" is equal to a discovered API
layer module name (which may belong to either an implicit or explicit API
layer) then only extensions from that API layer are enumerated.
Duplicate extensions (e.g. an implicit API layer and runtime might report support for the same extension) are eliminated by the loader. For duplicates, the API layer version is reported and the runtime version is culled according to the loader ordering - an extension name is removed from the extension request list after loading the layer.
If an application fails to enable the extension using the
"enabledExtensionNames" field in the
XrInstanceCreateInfo structure passed
xrCreateInstance and then attempts to use that extension, it may
result in undefined behavior.
For example, querying an extension command using
might appear to succeed without having the extension enabled, only to then
crash when calling that command.
4. API Layer Interaction
This section details how the loader interacts with API layers.
4.1. API Layer Call Chains and Distributed Dispatch
Remember, an API layer does not need to intercept all commands, instead, it can choose to intercept only a subset. Normally, when an API layer intercepts a given OpenXR command, it will call down the call chain as needed. The loader and all enabled API layers that participate in a call chain cooperate to ensure the correct sequencing of calls from one entity to the next. This group effort for call chain sequencing is hereinafter referred to as distributed dispatch.
If an API layer does not wish to intercept a command, it must forward the
request made to its
xrGetInstanceProcAddr implementation (provided through
getInstanceProcAddr) down to the next
implementation in the call chain (provided to the API layer through
In distributed dispatch each API layer is responsible for properly calling the next entity in the call chain. This means that a dispatch mechanism is required for all OpenXR commands that an API layer intercepts.
For example, if the enabled API layers intercepted only certain functions, the call chain would look as follows: