Meta’s OVRPlugin

Meta releases a plugin for Unity and Unreal Engine, internally called OVRPlugin (sometimes called Oculus XR Plugin in their public documentation). This plugin claims to implement OpenXR, an open standard that promotes cross-vendor and cross-plaform.

However, the OVRPlugin takes intentional precautions to exclude non-Meta platforms. This means that content developed with OVRPlugin will only work with Quest Link, and it will not work with any other runtime.

  • This includes blocking applications from running with Virtual Desktop, SteamLink or ALVR, even on a Meta Quest headset.
  • This includes blocking applications from running on non-Meta headsets such as Pimax, Pico, Varjo, Vive, etc.

The way OVRPlugin blocks non-Meta runtimes is by cleverly enforcing several conditions that will only be true when using Quest Link. OVRPlugin will fail to load (and preclude your application from using OpenXR) when:

  • The name of the OpenXR runtime (as returned by xrGetInstanceProperties()) is not “Oculus”.
  • The OpenXR runtime does not advertise non-standard extension XR_META_headset_id.
  • The plugin fails to detect legacy OVR API support through ovr_Detect().

In addition to the checks above, Meta has crafted a user experience in the game engines that forces developers to select OVRPlugin while making it difficult to implement fallbacks to standard OpenXR support. In Unity for example, a developer selecting OpenXR support after having enabled OVRPlugin will be prompted with a message declaring “incompatibility” and reverting the selection of plugin to OVRPlugin exclusively.

Meta’s OVRPlugin breaks the contract and assumptions built by OpenXR:

  • A developer using an “OpenXR” middleware expects cross-platform and cross-vendor compatibility. Meta’s OVRPlugin prevents this from happening. Meta benefits from this situation by quietly training content developers to develop uniquely for their platform and ignore all other platforms.
  • A platform vendors (or runtime developer) implementing the OpenXR standard and seeking (paying) for OpenXR conformance expects OpenXR content to work on their platform. Meta’s OVRPlugin is defining their own “conformance” and undermining the efforts of all other vendors in the industry who have proudly earned the OpenXR logo for their products.
  • OpenXR conformance (CTS) contractors and contributors are implementing a test suite to help vendors create high-quality runtimes. Meta and their OVRPlugin is discrediting their work and making competitor’s OpenXR implementation look incapable of running OpenXR content, while presenting their OpenXR runtime as the only adequate solution.

This is not an accident: this concern was reported to Meta early in 2024 via official means in the Khronos group. Meta acknowledged purposedly blocking other platforms from running OpenXR content at that time.

This is not a technical limitation: some runtimes (VDXR) have made great efforts to implement “compatibility” modes. There are counter-measures to unblock the content on any platform, but they are very expensive to investigate and implement.

This is not a short-coming of OpenXR: as proven with many applications using OVRPlugin with counter-measures enabled, these applications can run on a conformant OpenXR implementation.

For the past several years, Khronos has come up with best practices and solutions to develop OpenXR applications and maximize cross-vendor and cross-platform interoperability. Khronos has asked XR developers all over the world to follow these best practices, however - Meta - the largest vendor in Khronos is refusing to follow these best practices.

Instead of building on the work of each other to achieve success with OpenXR, Meta is reverting many of the improvements to the developers and users ecosystem that Khronos has spent time, money, energy into solving for the past 7 years.

Unfortunately, since 2024, Khronos has refused to take actions to stop Meta’s OVRPlugin destructive initiative towards the PCVR ecosystem. By not taking any actions to stop Meta’s targeted attacks, Khronos is sending the message that OpenXR has given up on cross-vendor and cross-platform support, that passing the CTS and being conformant mean nothing (conformant runtimes are precluded from running OpenXR apps), and that the OpenXR logo and trademark have lost their credibility in the PCVR ecosystem.

As a developer, you must take immediate action to end Meta’s attack.

  • DO NOT USE OVRPLUGIN (AKA “OCULUS XR” PLUGIN) FOR ANY CONTENT THAT YOU DEVELOP.
  • REACH OUT TO KHRONOS TECHNICAL SUPPORT TEAMS TO PROTEST INACTION AGAINST OVRPLUGIN.

OVRPlugin Compatibility mode

This section is listed for reference to any platform developer who wishes to defeat OVRPlugin’s malicious platform restrictions.

  • Return “Oculus” as the runtime name. You can simply make your runtime always return “Oculus” in xrGetInstanceProperties(). However, it is just plain annoying to not being able to advertise YOUR work and YOUR brand due to Meta’s actions. A better technique is to detect OVRPlugin, as shown here

    In your xrCreateInstance() implementation, check for the application name passed in the XrInstanceCreateInfo and also check whether the DLL “OVRPlugin.dll” is loaded in the process’ memory.

    m_isOculusXrPlugin =
        m_applicationName.find("Oculus VR Plugin") == 0 ||
        GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, "OVRPlugin.dll", &ovrPlugin);
    
  • Advertise the XR_META_headset_id extension. This extension does not do anything useful, and it is just used by Meta to facilitate locking down applications to their platform. You can find an implementation of this extension here.

  • Impersonate LibOVR to make ovr_Detect() pass. This one is tricky and it requires to fake the existence and state of the named event OculusHMDConnected. Because creating the event ourselves can be problematic (permissions, but also importantly the Oculus Services might be running in background and force the event to a specific state), you may use a Detours or hook to redirect the named event when ovr_Detect() (which is often statically linked into the application) calls OpenEventW(). You can find an implementation of this strategy here.

  • Do not allow cube map swapchain unless your runtime supports XR_KHR_composition_layer_cube. The OVRPlugin does not check properly for this extension. If your runtime sees an xrCreateSwapchain() with a faceCount of 6, you must fail to create the swapchain.

  • Advertise and implement XR_KHR_vulkan_enable. Even when OVRPlugin will use Direct3D, the plugin will request Vulkan support for no reason, and not actually use Vulkan.