Bug 1466700 - Refactor gfxVROculus.cpp to use gfxVRExternal interface, r=daoshengmu
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Fri, 13 Jul 2018 17:16:35 -0700
changeset 499389 06020ce3e33a83bc1dd1514aac97beacdab1505f
parent 499388 7571ffba8492b3d03a40c17368ebc1fb0921f10c
child 499390 7807742373e10ebd64db9d8b50d4abf809816cc3
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaoshengmu
bugs1466700
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1466700 - Refactor gfxVROculus.cpp to use gfxVRExternal interface, r=daoshengmu Tags: #secure-revision Bug #: 1466700 Differential Revision: https://phabricator.services.mozilla.com/D8177
gfx/thebes/gfxPrefs.h
gfx/vr/VRManager.cpp
gfx/vr/external_api/moz_external_vr.h
gfx/vr/gfxVRExternal.cpp
gfx/vr/gfxVRExternal.h
gfx/vr/service/OSVRSession.cpp
gfx/vr/service/OSVRSession.h
gfx/vr/service/OculusSession.cpp
gfx/vr/service/OculusSession.h
gfx/vr/service/OpenVRSession.cpp
gfx/vr/service/OpenVRSession.h
gfx/vr/service/VRService.cpp
gfx/vr/service/VRService.h
gfx/vr/service/VRSession.cpp
gfx/vr/service/VRSession.h
gfx/vr/service/moz.build
modules/libpref/init/all.js
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -372,16 +372,18 @@ private:
 
   DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled",  PluginAsyncDrawingEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.meta-viewport.enabled",             MetaViewportEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.visualviewport.enabled",            VisualViewportEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.autoactivate.enabled",           VRAutoActivateEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.controller_trigger_threshold",   VRControllerTriggerThreshold, float, 0.1f);
   DECL_GFX_PREF(Once, "dom.vr.external.enabled",               VRExternalEnabled, bool, true);
+  DECL_GFX_PREF(Live, "dom.vr.external.notdetected.timeout",   VRExternalNotDetectedTimeout, int32_t, 60000);
+  DECL_GFX_PREF(Live, "dom.vr.external.quit.timeout",          VRExternalQuitTimeout, int32_t, 10000);
   DECL_GFX_PREF(Live, "dom.vr.navigation.timeout",             VRNavigationTimeout, int32_t, 1000);
   DECL_GFX_PREF(Once, "dom.vr.oculus.enabled",                 VROculusEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.oculus.invisible.enabled",       VROculusInvisibleEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.oculus.present.timeout",         VROculusPresentTimeout, int32_t, 500);
   DECL_GFX_PREF(Live, "dom.vr.oculus.quit.timeout",            VROculusQuitTimeout, int32_t, 10000);
   DECL_GFX_PREF(Once, "dom.vr.openvr.enabled",                 VROpenVREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.osvr.enabled",                   VROSVREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.controller.enumerate.interval",  VRControllerEnumerateInterval, int32_t, 1000);
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -511,16 +511,45 @@ VRManager::EnumerateVRDisplays()
 
   /**
    * If we get this far, don't try again until
    * the VRDisplayEnumerateInterval elapses
    */
   mLastDisplayEnumerationTime = TimeStamp::Now();
 
   /**
+   * We must start the VR Service thread
+   * and VR Process before enumeration.
+   * We don't want to start this until we will
+   * actualy enumerate, to avoid continuously
+   * re-launching the thread/process when
+   * no hardware is found or a VR software update
+   * is in progress
+   */
+#if !defined(MOZ_WIDGET_ANDROID)
+    // Tell VR process to start VR service.
+    if (gfxPrefs::VRProcessEnabled() && !mVRServiceStarted) {
+      RefPtr<Runnable> task = NS_NewRunnableFunction(
+        "VRGPUChild::SendStartVRService",
+        [] () -> void {
+          VRGPUChild* vrGPUChild = VRGPUChild::Get();
+          vrGPUChild->SendStartVRService();
+      });
+
+      NS_DispatchToMainThread(task.forget());
+      mVRServiceStarted = true;
+    } else if (!gfxPrefs::VRProcessEnabled()){
+      if (mVRService) {
+        mVRService->Start();
+        mVRServiceStarted = true;
+      }
+    }
+#endif
+
+  /**
    * VRSystemManagers are inserted into mManagers in
    * a strict order of priority.  The managers for the
    * most device-specialized API's will have a chance
    * to enumerate devices before the more generic
    * device-agnostic APIs.
    */
   for (const auto& manager : mManagers) {
     manager->Enumerate();
@@ -541,37 +570,23 @@ void
 VRManager::RefreshVRDisplays(bool aMustDispatch)
 {
   /**
   * If we aren't viewing WebVR content, don't enumerate
   * new hardware, as it will cause some devices to power on
   * or interrupt other VR activities.
   */
   if (mVRDisplaysRequested || aMustDispatch) {
-#if !defined(MOZ_WIDGET_ANDROID)
-    // Tell VR process to start VR service.
-    if (gfxPrefs::VRProcessEnabled() && !mVRServiceStarted) {
-      RefPtr<Runnable> task = NS_NewRunnableFunction(
-        "VRGPUChild::SendStartVRService",
-        [] () -> void {
-          VRGPUChild* vrGPUChild = VRGPUChild::Get();
-          vrGPUChild->SendStartVRService();
-      });
-
-      NS_DispatchToMainThread(task.forget());
-      mVRServiceStarted = true;
-    } else if (!gfxPrefs::VRProcessEnabled()){
-      if (mVRService) {
-        mVRService->Start();
-        mVRServiceStarted = true;
-      }
-    }
-#endif
     EnumerateVRDisplays();
   }
+#if !defined(MOZ_WIDGET_ANDROID)
+  if (mVRService) {
+    mVRService->Refresh();
+  }
+#endif
 
   /**
    * VRSystemManager::GetHMDs will not activate new hardware
    * or result in interruption of other VR activities.
    * We can call it even when suppressing enumeration to get
    * the already-enumerated displays.
    */
   nsTArray<RefPtr<gfx::VRDisplayHost> > displays;
--- a/gfx/vr/external_api/moz_external_vr.h
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -30,17 +30,17 @@ namespace mozilla {
 #ifdef MOZILLA_INTERNAL_API
 namespace dom {
   enum class GamepadHand : uint8_t;
   enum class GamepadCapabilityFlags : uint16_t;
 }
 #endif //  MOZILLA_INTERNAL_API
 namespace gfx {
 
-static const int32_t kVRExternalVersion = 4;
+static const int32_t kVRExternalVersion = 5;
 
 // We assign VR presentations to groups with a bitmask.
 // Currently, we will only display either content or chrome.
 // Later, we will have more groups to support VR home spaces and
 // multitasking environments.
 // These values are not exposed to regular content and only affect
 // chrome-only API's.  They may be changed at any time.
 static const uint32_t kVRGroupNone = 0;
@@ -262,19 +262,21 @@ struct VRFieldOfView {
 struct VRDisplayState
 {
   enum Eye {
     Eye_Left,
     Eye_Right,
     NumEyes
   };
 
-#if defined(__ANDROID__)
+  // When true, indicates that the VR service has shut down
   bool shutdown;
-#endif // defined(__ANDROID__)
+  // Minimum number of milliseconds to wait before attempting
+  // to start the VR service again
+  uint32_t mMinRestartInterval;
   char mDisplayName[kVRDisplayNameMaxLen];
   // eight byte character code identifier
   // LSB first, so "ABCDEFGH" -> ('H'<<56) + ('G'<<48) + ('F'<<40) + ('E'<<32) + ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A').
   uint64_t mEightCC;
   VRDisplayCapabilityFlags mCapabilityFlags;
   VRFieldOfView mEyeFOV[VRDisplayState::NumEyes];
   Point3D_POD mEyeTranslation[VRDisplayState::NumEyes];
   IntSize_POD mEyeResolution;
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -460,20 +460,20 @@ VRSystemManagerExternal::VRSystemManager
  , mSameProcess(aAPIShmem != nullptr)
 #endif
 {
 #if defined(XP_MACOSX)
   mShmemFD = 0;
 #elif defined(XP_WIN)
   mShmemFile = NULL;
 #elif defined(MOZ_WIDGET_ANDROID)
-  mDoShutdown = false;
   mExternalStructFailed = false;
   mEnumerationCompleted = false;
 #endif
+  mDoShutdown = false;
 
   if (!aAPIShmem) {
     OpenShmem();
   }
 }
 
 VRSystemManagerExternal::~VRSystemManagerExternal()
 {
@@ -572,27 +572,19 @@ VRSystemManagerExternal::OpenShmem()
   }
 #endif
   CheckForShutdown();
 }
 
 void
 VRSystemManagerExternal::CheckForShutdown()
 {
-#if defined(MOZ_WIDGET_ANDROID)
   if (mDoShutdown) {
     Shutdown();
   }
-#else
-  if (mExternalShmem) {
-    if (mExternalShmem->generationA == -1 && mExternalShmem->generationB == -1) {
-      Shutdown();
-    }
-  }
-#endif // defined(MOZ_WIDGET_ANDROID)
 }
 
 void
 VRSystemManagerExternal::CloseShmem()
 {
 #if !defined(MOZ_WIDGET_ANDROID)
   if (mSameProcess) {
     return;
@@ -650,19 +642,17 @@ VRSystemManagerExternal::Destroy()
 
 void
 VRSystemManagerExternal::Shutdown()
 {
   if (mDisplay) {
     mDisplay = nullptr;
   }
   CloseShmem();
-#if defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
-#endif
 }
 
 void
 VRSystemManagerExternal::Run100msTasks()
 {
   VRSystemManager::Run100msTasks();
   // 1ms and 10ms tasks will always be run before
   // the 100ms tasks, so no need to run them
@@ -704,16 +694,23 @@ VRSystemManagerExternal::Enumerate()
 }
 
 bool
 VRSystemManagerExternal::ShouldInhibitEnumeration()
 {
   if (VRSystemManager::ShouldInhibitEnumeration()) {
     return true;
   }
+  if (!mEarliestRestartTime.IsNull() && mEarliestRestartTime > TimeStamp::Now()) {
+    // When the VR Service shuts down it informs us of how long we
+    // must wait until we can re-start it.
+    // We must wait until mEarliestRestartTime before attempting
+    // to enumerate again.
+    return true;
+  }
   if (mDisplay) {
     // When we find an a VR device, don't
     // allow any further enumeration as it
     // may get picked up redundantly by other
     // API's.
     return true;
   }
   return false;
@@ -825,17 +822,23 @@ VRSystemManagerExternal::PullState(VRDis
         memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
         if (aSensorState) {
           memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
         }
         if (aControllerState) {
           memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
         }
         mEnumerationCompleted = mExternalShmem->state.enumerationCompleted;
-        mDoShutdown = aDisplayState->shutdown;
+        if (aDisplayState->shutdown) {
+          mDoShutdown = true;
+          TimeStamp now = TimeStamp::Now();
+          if (!mEarliestRestartTime.IsNull() && mEarliestRestartTime < now) {
+            mEarliestRestartTime = now + TimeDuration::FromMilliseconds((double)aDisplayState->mMinRestartInterval);
+          }
+        }
         if (!aWaitCondition || aWaitCondition()) {
           done = true;
           break;
         }
         // Block current thead using the condition variable until data changes
         pthread_cond_wait((pthread_cond_t*)&mExternalShmem->systemCond, (pthread_mutex_t*)&mExternalShmem->systemMutex);
       }
       pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
@@ -861,25 +864,31 @@ VRSystemManagerExternal::PullState(VRDis
     if (tmp.generationA == tmp.generationB && tmp.generationA != 0 && tmp.generationA != -1 && tmp.state.enumerationCompleted) {
       memcpy(aDisplayState, &tmp.state.displayState, sizeof(VRDisplayState));
       if (aSensorState) {
         memcpy(aSensorState, &tmp.state.sensorState, sizeof(VRHMDSensorState));
       }
       if (aControllerState) {
         memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
       }
+      if (aDisplayState->shutdown) {
+        mDoShutdown = true;
+        TimeStamp now = TimeStamp::Now();
+        if (!mEarliestRestartTime.IsNull() && mEarliestRestartTime < now) {
+          mEarliestRestartTime = now + TimeDuration::FromMilliseconds((double)aDisplayState->mMinRestartInterval);
+        }
+      }
       success = true;
     }
   }
 
   return success;
 }
 #endif // defined(MOZ_WIDGET_ANDROID)
 
-
 void
 VRSystemManagerExternal::PushState(VRBrowserState* aBrowserState, bool aNotifyCond)
 {
   MOZ_ASSERT(aBrowserState);
   MOZ_ASSERT(mExternalShmem);
   if (mExternalShmem) {
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -126,25 +126,27 @@ protected:
 private:
   // there can only be one
   RefPtr<impl::VRDisplayExternal> mDisplay;
 #if defined(XP_MACOSX)
   int mShmemFD;
 #elif defined(XP_WIN)
   base::ProcessHandle mShmemFile;
 #elif defined(MOZ_WIDGET_ANDROID)
-  bool mDoShutdown;
+  
   bool mExternalStructFailed;
   bool mEnumerationCompleted;
 #endif
+  bool mDoShutdown;
 
   volatile VRExternalShmem* mExternalShmem;
 #if !defined(MOZ_WIDGET_ANDROID)
   bool mSameProcess;
 #endif
+  TimeStamp mEarliestRestartTime;
 
   void OpenShmem();
   void CloseShmem();
   void CheckForShutdown();
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/service/OSVRSession.cpp
+++ b/gfx/vr/service/OSVRSession.cpp
@@ -493,40 +493,45 @@ OSVRSession::UpdateHeadsetPose(mozilla::
     result.pose.position[1] = position.data[1];
     result.pose.position[2] = position.data[2];
   }
 
   result.CalcViewMatrices(mHeadToEye);
 }
 
 bool
-OSVRSession::ShouldQuit() const
-{
-  return false;
-}
-
-bool
 OSVRSession::StartPresentation()
 {
   return false;
   // TODO Implement
 }
 
 void
 OSVRSession::StopPresentation()
 {
   // TODO Implement
 }
 
+#if defined(XP_WIN)
 bool
-OSVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer)
+OSVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                         ID3D11Texture2D* aTexture)
 {
   return false;
-   // TODO Implement
+  // TODO Implement
 }
+#elif defined(XP_MACOSX)
+bool
+OSVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                         MacIOSurface* aTexture)
+{
+  return false;
+  // TODO Implement
+}
+#endif
 
 void
 OSVRSession::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                            float aIntensity, float aDuration)
 {
 
 }
 
--- a/gfx/vr/service/OSVRSession.h
+++ b/gfx/vr/service/OSVRSession.h
@@ -30,25 +30,32 @@ class OSVRSession : public VRSession
 public:
   OSVRSession();
   virtual ~OSVRSession();
 
   bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
   void Shutdown() override;
   void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
   void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
-  bool ShouldQuit() const override;
   bool StartPresentation() override;
   void StopPresentation() override;
-  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) override;
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                     float aIntensity, float aDuration) override;
   void StopVibrateHaptic(uint32_t aControllerIdx) override;
   void StopAllHaptics() override;
 
+protected:
+#if defined(XP_WIN)
+  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                   ID3D11Texture2D* aTexture) override;
+#elif defined(XP_MACOSX)
+  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                   MacIOSurface* aTexture) override;
+#endif
+
 private:
   bool InitState(mozilla::gfx::VRSystemState& aSystemState);
   void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
   bool mRuntimeLoaded;
   bool mOSVRInitialized;
   bool mClientContextInitialized;
   bool mDisplayConfigInitialized;
   bool mInterfaceInitialized;
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/OculusSession.cpp
@@ -0,0 +1,1586 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef XP_WIN
+#error "Oculus support only available for Windows"
+#endif
+
+#include <math.h>
+#include <d3d11.h>
+
+#include "gfxPrefs.h"
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/dom/GamepadBinding.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/SharedLibrary.h"
+#include "OculusSession.h"
+
+/** XXX The DX11 objects and quad blitting could be encapsulated
+ *    into a separate object if either Oculus starts supporting
+ *     non-Windows platforms or the blit is needed by other HMD\
+ *     drivers.
+ *     Alternately, we could remove the extra blit for
+ *     Oculus as well with some more refactoring.
+ */
+
+// See CompositorD3D11Shaders.h
+namespace mozilla {
+namespace layers {
+struct ShaderBytes
+{
+  const void* mData;
+  size_t mLength;
+};
+extern ShaderBytes sRGBShader;
+extern ShaderBytes sLayerQuadVS;
+} // namespace layers
+} // namespace mozilla
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+namespace {
+
+static pfn_ovr_Initialize ovr_Initialize = nullptr;
+static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
+static pfn_ovr_GetLastErrorInfo ovr_GetLastErrorInfo = nullptr;
+static pfn_ovr_GetVersionString ovr_GetVersionString = nullptr;
+static pfn_ovr_TraceMessage ovr_TraceMessage = nullptr;
+static pfn_ovr_IdentifyClient ovr_IdentifyClient = nullptr;
+static pfn_ovr_GetHmdDesc ovr_GetHmdDesc = nullptr;
+static pfn_ovr_GetTrackerCount ovr_GetTrackerCount = nullptr;
+static pfn_ovr_GetTrackerDesc ovr_GetTrackerDesc = nullptr;
+static pfn_ovr_Create ovr_Create = nullptr;
+static pfn_ovr_Destroy ovr_Destroy = nullptr;
+static pfn_ovr_GetSessionStatus ovr_GetSessionStatus = nullptr;
+static pfn_ovr_IsExtensionSupported ovr_IsExtensionSupported = nullptr;
+static pfn_ovr_EnableExtension ovr_EnableExtension = nullptr;
+static pfn_ovr_SetTrackingOriginType ovr_SetTrackingOriginType = nullptr;
+static pfn_ovr_GetTrackingOriginType ovr_GetTrackingOriginType = nullptr;
+static pfn_ovr_RecenterTrackingOrigin ovr_RecenterTrackingOrigin = nullptr;
+static pfn_ovr_SpecifyTrackingOrigin ovr_SpecifyTrackingOrigin = nullptr;
+static pfn_ovr_ClearShouldRecenterFlag ovr_ClearShouldRecenterFlag = nullptr;
+static pfn_ovr_GetTrackingState ovr_GetTrackingState = nullptr;
+static pfn_ovr_GetDevicePoses ovr_GetDevicePoses = nullptr;
+static pfn_ovr_GetTrackerPose ovr_GetTrackerPose = nullptr;
+static pfn_ovr_GetInputState ovr_GetInputState = nullptr;
+static pfn_ovr_GetConnectedControllerTypes ovr_GetConnectedControllerTypes =
+  nullptr;
+static pfn_ovr_GetTouchHapticsDesc ovr_GetTouchHapticsDesc = nullptr;
+static pfn_ovr_SetControllerVibration ovr_SetControllerVibration = nullptr;
+static pfn_ovr_SubmitControllerVibration ovr_SubmitControllerVibration =
+  nullptr;
+static pfn_ovr_GetControllerVibrationState ovr_GetControllerVibrationState =
+  nullptr;
+static pfn_ovr_TestBoundary ovr_TestBoundary = nullptr;
+static pfn_ovr_TestBoundaryPoint ovr_TestBoundaryPoint = nullptr;
+static pfn_ovr_SetBoundaryLookAndFeel ovr_SetBoundaryLookAndFeel = nullptr;
+static pfn_ovr_ResetBoundaryLookAndFeel ovr_ResetBoundaryLookAndFeel = nullptr;
+static pfn_ovr_GetBoundaryGeometry ovr_GetBoundaryGeometry = nullptr;
+static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions = nullptr;
+static pfn_ovr_GetBoundaryVisible ovr_GetBoundaryVisible = nullptr;
+static pfn_ovr_RequestBoundaryVisible ovr_RequestBoundaryVisible = nullptr;
+static pfn_ovr_GetTextureSwapChainLength ovr_GetTextureSwapChainLength =
+  nullptr;
+static pfn_ovr_GetTextureSwapChainCurrentIndex
+  ovr_GetTextureSwapChainCurrentIndex = nullptr;
+static pfn_ovr_GetTextureSwapChainDesc ovr_GetTextureSwapChainDesc = nullptr;
+static pfn_ovr_CommitTextureSwapChain ovr_CommitTextureSwapChain = nullptr;
+static pfn_ovr_DestroyTextureSwapChain ovr_DestroyTextureSwapChain = nullptr;
+static pfn_ovr_DestroyMirrorTexture ovr_DestroyMirrorTexture = nullptr;
+static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize = nullptr;
+static pfn_ovr_GetRenderDesc2 ovr_GetRenderDesc2 = nullptr;
+static pfn_ovr_WaitToBeginFrame ovr_WaitToBeginFrame = nullptr;
+static pfn_ovr_BeginFrame ovr_BeginFrame = nullptr;
+static pfn_ovr_EndFrame ovr_EndFrame = nullptr;
+static pfn_ovr_SubmitFrame ovr_SubmitFrame = nullptr;
+static pfn_ovr_GetPerfStats ovr_GetPerfStats = nullptr;
+static pfn_ovr_ResetPerfStats ovr_ResetPerfStats = nullptr;
+static pfn_ovr_GetPredictedDisplayTime ovr_GetPredictedDisplayTime = nullptr;
+static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
+static pfn_ovr_GetBool ovr_GetBool = nullptr;
+static pfn_ovr_SetBool ovr_SetBool = nullptr;
+static pfn_ovr_GetInt ovr_GetInt = nullptr;
+static pfn_ovr_SetInt ovr_SetInt = nullptr;
+static pfn_ovr_GetFloat ovr_GetFloat = nullptr;
+static pfn_ovr_SetFloat ovr_SetFloat = nullptr;
+static pfn_ovr_GetFloatArray ovr_GetFloatArray = nullptr;
+static pfn_ovr_SetFloatArray ovr_SetFloatArray = nullptr;
+static pfn_ovr_GetString ovr_GetString = nullptr;
+static pfn_ovr_SetString ovr_SetString = nullptr;
+static pfn_ovr_GetExternalCameras ovr_GetExternalCameras = nullptr;
+static pfn_ovr_SetExternalCameraProperties ovr_SetExternalCameraProperties =
+  nullptr;
+
+#ifdef XP_WIN
+static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
+static pfn_ovr_GetTextureSwapChainBufferDX ovr_GetTextureSwapChainBufferDX =
+  nullptr;
+static pfn_ovr_CreateMirrorTextureDX ovr_CreateMirrorTextureDX = nullptr;
+static pfn_ovr_GetMirrorTextureBufferDX ovr_GetMirrorTextureBufferDX = nullptr;
+#endif
+
+static pfn_ovr_CreateTextureSwapChainGL ovr_CreateTextureSwapChainGL = nullptr;
+static pfn_ovr_GetTextureSwapChainBufferGL ovr_GetTextureSwapChainBufferGL =
+  nullptr;
+static pfn_ovr_CreateMirrorTextureGL ovr_CreateMirrorTextureGL = nullptr;
+static pfn_ovr_GetMirrorTextureBufferGL ovr_GetMirrorTextureBufferGL = nullptr;
+
+#ifdef HAVE_64BIT_BUILD
+#define BUILD_BITS 64
+#else
+#define BUILD_BITS 32
+#endif
+
+#define OVR_PRODUCT_VERSION 1
+#define OVR_MAJOR_VERSION 1
+#define OVR_MINOR_VERSION 19
+
+static const uint32_t kNumOculusButtons = 6;
+static const uint32_t kNumOculusHaptcs = 1;
+static const uint32_t kNumOculusAxes = 2;
+ovrControllerType OculusControllerTypes[2] = { ovrControllerType_LTouch,
+                                               ovrControllerType_RTouch };
+const char* OculusControllerNames[2] = { "Oculus Touch (Left)",
+                                         "Oculus Touch (Right)" };
+dom::GamepadHand OculusControllerHand[2] = { dom::GamepadHand::Left,
+                                             dom::GamepadHand::Right };
+ovrButton OculusControllerButtons[2][kNumOculusButtons] = {
+  { ovrButton_LThumb,
+    (ovrButton)0,
+    (ovrButton)0,
+    ovrButton_X,
+    ovrButton_Y,
+    (ovrButton)0 },
+  { ovrButton_RThumb,
+    (ovrButton)0,
+    (ovrButton)0,
+    ovrButton_A,
+    ovrButton_B,
+    (ovrButton)0 },
+};
+
+ovrTouch OculusControllerTouches[2][kNumOculusButtons] = {
+  { (ovrTouch)0,
+    ovrTouch_LIndexTrigger,
+    (ovrTouch)0,
+    (ovrTouch)0,
+    (ovrTouch)0,
+    ovrTouch_LThumbRest },
+  { (ovrTouch)0,
+    ovrTouch_RIndexTrigger,
+    (ovrTouch)0,
+    (ovrTouch)0,
+    (ovrTouch)0,
+    ovrTouch_RThumbRest },
+};
+
+void
+UpdateButton(const ovrInputState& aInputState,
+             uint32_t aHandIdx,
+             uint32_t aButtonIdx,
+             VRControllerState& aControllerState)
+{
+  if (aInputState.Buttons & OculusControllerButtons[aHandIdx][aButtonIdx]) {
+    aControllerState.buttonPressed |= ((uint64_t)1 << aButtonIdx);
+  }
+  if (aInputState.Touches & OculusControllerTouches[aHandIdx][aButtonIdx]) {
+    aControllerState.buttonTouched |= ((uint64_t)1 << aButtonIdx);
+  }
+}
+
+VRFieldOfView
+FromFovPort(const ovrFovPort& aFOV)
+{
+  VRFieldOfView fovInfo;
+  fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
+  fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
+  fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
+  fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
+  return fovInfo;
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace gfx {
+
+OculusSession::OculusSession()
+  : VRSession()
+  , mOvrLib(nullptr)
+  , mSession(nullptr)
+  , mInitFlags((ovrInitFlags)0)
+  , mTextureSet(nullptr)
+  , mQuadVS(nullptr)
+  , mQuadPS(nullptr)
+  , mLinearSamplerState(nullptr)
+  , mVSConstantBuffer(nullptr)
+  , mPSConstantBuffer(nullptr)
+  , mVertexBuffer(nullptr)
+  , mInputLayout(nullptr)
+  , mRemainingVibrateTime{}
+  , mHapticPulseIntensity{}
+  , mIsPresenting(false)
+{
+}
+
+OculusSession::~OculusSession()
+{
+  Shutdown();
+}
+
+bool
+OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState)
+{
+  if (!CreateD3DObjects()) {
+    return false;
+  }
+  if (!CreateShaders()) {
+    return false;
+  }
+
+  if (!LoadOvrLib()) {
+    return false;
+  }
+  // We start off with an invisible session, then re-initialize
+  // with visible session once WebVR content starts rendering.
+  if (!ChangeVisibility(false)) {
+    return false;
+  }
+  if (!InitState(aSystemState)) {
+    return false;
+  }
+
+  mPresentationSize =
+    IntSize(aSystemState.displayState.mEyeResolution.width * 2,
+            aSystemState.displayState.mEyeResolution.height);
+  return true;
+}
+
+void
+OculusSession::UpdateVisibility()
+{
+  // Do not immediately re-initialize with an invisible session after
+  // the end of a VR presentation.  Waiting for the configured duraction
+  // ensures that the user will not drop to Oculus Home during VR link
+  // traversal.
+  if (mIsPresenting) {
+    // We are currently rendering immersive content.
+    // Avoid interrupting the session
+    return;
+  }
+  if (mInitFlags & ovrInit_Invisible) {
+    // We are already invisible
+    return;
+  }
+  if (mLastPresentationEnd.IsNull()) {
+    // There has been no presentation yet
+    return;
+  }
+
+  TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd;
+  TimeDuration timeout = TimeDuration::FromMilliseconds(gfxPrefs::VROculusPresentTimeout());
+  if (timeout <= TimeDuration(0) || duration >= timeout) {
+    if (!ChangeVisibility(false)) {
+      gfxWarning() << "OculusSession::ChangeVisibility(false) failed";
+    }
+  }
+}
+
+void
+OculusSession::CoverTransitions()
+{
+  // While content is loading or during immersive-mode link
+  // traversal, we need to prevent the user from seeing the
+  // last rendered frame.
+  // We render black frames to cover up the transition.
+  MOZ_ASSERT(mSession);
+  if (mIsPresenting) {
+    // We are currently rendering immersive content.
+    // Avoid interrupting the session
+    return;
+  }
+
+  if (mInitFlags & ovrInit_Invisible) {
+    // We are invisible, nothing to cover up
+    return;
+  }
+
+  // Render a black frame
+  ovrLayerEyeFov layer;
+  memset(&layer, 0, sizeof(layer));
+  layer.Header.Type = ovrLayerType_Disabled;
+  ovrLayerHeader* layers = &layer.Header;
+  ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
+}
+
+bool
+OculusSession::ChangeVisibility(bool bVisible)
+{
+  ovrInitFlags flags =
+    (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering);
+  if (gfxPrefs::VROculusInvisibleEnabled() && !bVisible) {
+    flags = (ovrInitFlags)(flags | ovrInit_Invisible);
+  }
+  if (mInitFlags == flags) {
+    // The new state is the same, nothing to do
+    return true;
+  }
+
+  // Tear everything down
+  StopRendering();
+  StopSession();
+  StopLib();
+
+  // Start it back up
+  if (!StartLib(flags)) {
+    return false;
+  }
+  if (!StartSession()) {
+    return false;
+  }
+  return true;
+}
+
+void OculusSession::Shutdown()
+{
+  StopRendering();
+  StopSession();
+  StopLib();
+  UnloadOvrLib();
+  DestroyShaders();
+}
+
+void
+OculusSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState)
+{
+  if (!mSession) {
+    return;
+  }
+
+  ovrSessionStatus status;
+  if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) {
+    aSystemState.displayState.mIsConnected = status.HmdPresent;
+    aSystemState.displayState.mIsMounted = status.HmdMounted;
+    mShouldQuit = status.ShouldQuit;
+\
+  } else {
+    aSystemState.displayState.mIsConnected = false;
+    aSystemState.displayState.mIsMounted = false;
+  }
+  UpdateHaptics();
+  UpdateVisibility();
+  CoverTransitions();
+}
+
+void
+OculusSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState)
+{
+  UpdateHeadsetPose(aSystemState);
+  UpdateEyeParameters(aSystemState);
+  UpdateControllers(aSystemState);
+  UpdateTelemetry(aSystemState);
+  aSystemState.sensorState.inputFrameID++;
+}
+
+bool
+OculusSession::StartPresentation()
+{
+  /**
+   * XXX - We should resolve fail the promise returned by
+   *       VRDisplay.requestPresent() when the DX11 resources fail allocation
+   *       in VRDisplayOculus::StartPresentation().
+   *       Bailing out here prevents the crash but content should be aware
+   *       that frames are not being presented.
+   *       See Bug 1299309.
+   **/
+  if (!ChangeVisibility(true)) {
+    return false;
+  }
+  if (!StartRendering()) {
+    StopRendering();
+    return false;
+  }
+  mIsPresenting = true;
+  return true;
+}
+
+void
+OculusSession::StopPresentation()
+{
+  mLastPresentationEnd = TimeStamp::Now();
+  mIsPresenting = false;
+}
+
+bool
+OculusSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                           ID3D11Texture2D* aTexture)
+{
+  if (!IsPresentationReady()) {
+    return false;
+  }
+
+  D3D11_TEXTURE2D_DESC textureDesc = { 0 };
+  aTexture->GetDesc(&textureDesc);
+
+  int currentRenderTarget = 0;
+  ovrResult orv = ovr_GetTextureSwapChainCurrentIndex(
+    mSession, mTextureSet, &currentRenderTarget);
+  if (orv != ovrSuccess) {
+    NS_WARNING("ovr_GetTextureSwapChainCurrentIndex failed.");
+    return false;
+  }
+
+  ID3D11RenderTargetView* view = mRTView[currentRenderTarget];
+
+  float clear[] = { 0.0f, 0.0f, 0.0f, 1.0f };
+  mContext->ClearRenderTargetView(view, clear);
+  mContext->OMSetRenderTargets(1, &view, nullptr);
+
+  Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
+  viewMatrix.PreScale(2.0f / float(textureDesc.Width),
+                      2.0f / float(textureDesc.Height));
+  viewMatrix.PreScale(1.0f, -1.0f);
+  Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
+  projection._33 = 0.0f;
+
+  Matrix transform2d;
+  gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+  D3D11_VIEWPORT viewport;
+  viewport.MinDepth = 0.0f;
+  viewport.MaxDepth = 1.0f;
+  viewport.Width = textureDesc.Width;
+  viewport.Height = textureDesc.Height;
+  viewport.TopLeftX = 0;
+  viewport.TopLeftY = 0;
+
+  D3D11_RECT scissor;
+  scissor.left = 0;
+  scissor.right = textureDesc.Width;
+  scissor.top = 0;
+  scissor.bottom = textureDesc.Height;
+
+  memcpy(&mVSConstants.layerTransform,
+         &transform._11,
+         sizeof(mVSConstants.layerTransform));
+  memcpy(
+    &mVSConstants.projection, &projection._11, sizeof(mVSConstants.projection));
+  mVSConstants.renderTargetOffset[0] = 0.0f;
+  mVSConstants.renderTargetOffset[1] = 0.0f;
+  mVSConstants.layerQuad =
+    Rect(0.0f, 0.0f, textureDesc.Width, textureDesc.Height);
+  mVSConstants.textureCoords = Rect(0.0f, 1.0f, 1.0f, -1.0f);
+
+  mPSConstants.layerOpacity[0] = 1.0f;
+
+  ID3D11Buffer* vbuffer = mVertexBuffer;
+  UINT vsize = sizeof(Vertex);
+  UINT voffset = 0;
+  mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset);
+  mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
+  mContext->IASetInputLayout(mInputLayout);
+  mContext->RSSetViewports(1, &viewport);
+  mContext->RSSetScissorRects(1, &scissor);
+  mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+  mContext->VSSetShader(mQuadVS, nullptr, 0);
+  mContext->PSSetShader(mQuadPS, nullptr, 0);
+
+  RefPtr<ID3D11ShaderResourceView> srView;
+  HRESULT hr = mDevice->CreateShaderResourceView(
+    aTexture, nullptr, getter_AddRefs(srView));
+  if (FAILED(hr)) {
+    gfxWarning() << "Could not create shader resource view for Oculus: "
+                 << hexa(hr);
+    return false;
+  }
+  ID3D11ShaderResourceView* viewPtr = srView.get();
+  mContext->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &viewPtr);
+  // XXX Use Constant from TexSlot in CompositorD3D11.cpp?
+
+  ID3D11SamplerState* sampler = mLinearSamplerState;
+  mContext->PSSetSamplers(0, 1, &sampler);
+
+  if (!UpdateConstantBuffers()) {
+    NS_WARNING("Failed to update constant buffers for Oculus");
+    return false;
+  }
+
+  mContext->Draw(4, 0);
+
+  orv = ovr_CommitTextureSwapChain(mSession, mTextureSet);
+  if (orv != ovrSuccess) {
+    NS_WARNING("ovr_CommitTextureSwapChain failed.");
+    return false;
+  }
+
+  ovrLayerEyeFov layer;
+  memset(&layer, 0, sizeof(layer));
+  layer.Header.Type = ovrLayerType_EyeFov;
+  layer.Header.Flags = 0;
+  layer.ColorTexture[0] = mTextureSet;
+  layer.ColorTexture[1] = nullptr;
+  layer.Fov[0] = mFOVPort[0];
+  layer.Fov[1] = mFOVPort[1];
+  layer.Viewport[0].Pos.x = textureDesc.Width * aLayer.mLeftEyeRect.x;
+  layer.Viewport[0].Pos.y = textureDesc.Height * aLayer.mLeftEyeRect.y;
+  layer.Viewport[0].Size.w = textureDesc.Width * aLayer.mLeftEyeRect.width;
+  layer.Viewport[0].Size.h = textureDesc.Height * aLayer.mLeftEyeRect.height;
+  layer.Viewport[1].Pos.x = textureDesc.Width * aLayer.mRightEyeRect.x;
+  layer.Viewport[1].Pos.y = textureDesc.Height * aLayer.mRightEyeRect.y;
+  layer.Viewport[1].Size.w = textureDesc.Width * aLayer.mRightEyeRect.width;
+  layer.Viewport[1].Size.h = textureDesc.Height * aLayer.mRightEyeRect.height;
+
+  for (uint32_t i = 0; i < 2; ++i) {
+    layer.RenderPose[i].Orientation.x = mFrameStartPose[i].Orientation.x;
+    layer.RenderPose[i].Orientation.y = mFrameStartPose[i].Orientation.y;
+    layer.RenderPose[i].Orientation.z = mFrameStartPose[i].Orientation.z;
+    layer.RenderPose[i].Orientation.w = mFrameStartPose[i].Orientation.w;
+    layer.RenderPose[i].Position.x = mFrameStartPose[i].Position.x;
+    layer.RenderPose[i].Position.y = mFrameStartPose[i].Position.y;
+    layer.RenderPose[i].Position.z = mFrameStartPose[i].Position.z;
+  }
+
+  ovrLayerHeader* layers = &layer.Header;
+  orv = ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
+  // ovr_SubmitFrame will fail during the Oculus health and safety warning.
+  // and will start succeeding once the warning has been dismissed by the user.
+
+  if (!OVR_UNQUALIFIED_SUCCESS(orv)) {
+    /**
+     * We wish to throttle the framerate for any case that the rendered
+     * result is not visible.  In some cases, such as during the Oculus
+     * "health and safety warning", orv will be > 0 (OVR_SUCCESS but not
+     * OVR_UNQUALIFIED_SUCCESS) and ovr_SubmitFrame will not block.
+     * In this case, returning true would have resulted in an unthrottled
+     * render loop hiting excessive frame rates and consuming resources.
+     */
+    return false;
+  }
+
+  return true;
+}
+
+bool
+OculusSession::LoadOvrLib()
+{
+  if (mOvrLib) {
+    // Already loaded, early exit
+    return true;
+  }
+#if defined(_WIN32)
+  nsTArray<nsString> libSearchPaths;
+  nsString libName;
+  nsString searchPath;
+
+  static const char dirSep = '\\';
+  static const int pathLen = 260;
+  searchPath.SetCapacity(pathLen);
+  int realLen =
+    ::GetSystemDirectoryW(char16ptr_t(searchPath.BeginWriting()), pathLen);
+  if (realLen != 0 && realLen < pathLen) {
+    searchPath.SetLength(realLen);
+    libSearchPaths.AppendElement(searchPath);
+  }
+  libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION);
+
+  // search the path/module dir
+  libSearchPaths.InsertElementsAt(0, 1, EmptyString());
+
+  // If the env var is present, we override libName
+  if (_wgetenv(L"OVR_LIB_PATH")) {
+    searchPath = _wgetenv(L"OVR_LIB_PATH");
+    libSearchPaths.InsertElementsAt(0, 1, searchPath);
+  }
+
+  if (_wgetenv(L"OVR_LIB_NAME")) {
+    libName = _wgetenv(L"OVR_LIB_NAME");
+  }
+
+  for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
+    nsString& libPath = libSearchPaths[i];
+    nsString fullName;
+    if (libPath.Length() == 0) {
+      fullName.Assign(libName);
+    } else {
+      fullName.AppendPrintf("%s%c%s", libPath.get(), dirSep, libName.get());
+    }
+
+    mOvrLib = LoadLibraryWithFlags(fullName.get());
+    if (mOvrLib) {
+      break;
+    }
+  }
+#else
+#error "Unsupported platform!"
+#endif
+
+  if (!mOvrLib) {
+    return false;
+  }
+
+#define REQUIRE_FUNCTION(_x)                                                   \
+  do {                                                                         \
+    *(void**)&_x = (void*)PR_FindSymbol(mOvrLib, #_x);                         \
+    if (!_x) {                                                                 \
+      printf_stderr(#_x " symbol missing\n");                                  \
+      goto fail;                                                               \
+    }                                                                          \
+  } while (0)
+
+  REQUIRE_FUNCTION(ovr_Initialize);
+  REQUIRE_FUNCTION(ovr_Shutdown);
+  REQUIRE_FUNCTION(ovr_GetLastErrorInfo);
+  REQUIRE_FUNCTION(ovr_GetVersionString);
+  REQUIRE_FUNCTION(ovr_TraceMessage);
+  REQUIRE_FUNCTION(ovr_IdentifyClient);
+  REQUIRE_FUNCTION(ovr_GetHmdDesc);
+  REQUIRE_FUNCTION(ovr_GetTrackerCount);
+  REQUIRE_FUNCTION(ovr_GetTrackerDesc);
+  REQUIRE_FUNCTION(ovr_Create);
+  REQUIRE_FUNCTION(ovr_Destroy);
+  REQUIRE_FUNCTION(ovr_GetSessionStatus);
+  REQUIRE_FUNCTION(ovr_IsExtensionSupported);
+  REQUIRE_FUNCTION(ovr_EnableExtension);
+  REQUIRE_FUNCTION(ovr_SetTrackingOriginType);
+  REQUIRE_FUNCTION(ovr_GetTrackingOriginType);
+  REQUIRE_FUNCTION(ovr_RecenterTrackingOrigin);
+  REQUIRE_FUNCTION(ovr_SpecifyTrackingOrigin);
+  REQUIRE_FUNCTION(ovr_ClearShouldRecenterFlag);
+  REQUIRE_FUNCTION(ovr_GetTrackingState);
+  REQUIRE_FUNCTION(ovr_GetDevicePoses);
+  REQUIRE_FUNCTION(ovr_GetTrackerPose);
+  REQUIRE_FUNCTION(ovr_GetInputState);
+  REQUIRE_FUNCTION(ovr_GetConnectedControllerTypes);
+  REQUIRE_FUNCTION(ovr_GetTouchHapticsDesc);
+  REQUIRE_FUNCTION(ovr_SetControllerVibration);
+  REQUIRE_FUNCTION(ovr_SubmitControllerVibration);
+  REQUIRE_FUNCTION(ovr_GetControllerVibrationState);
+  REQUIRE_FUNCTION(ovr_TestBoundary);
+  REQUIRE_FUNCTION(ovr_TestBoundaryPoint);
+  REQUIRE_FUNCTION(ovr_SetBoundaryLookAndFeel);
+  REQUIRE_FUNCTION(ovr_ResetBoundaryLookAndFeel);
+  REQUIRE_FUNCTION(ovr_GetBoundaryGeometry);
+  REQUIRE_FUNCTION(ovr_GetBoundaryDimensions);
+  REQUIRE_FUNCTION(ovr_GetBoundaryVisible);
+  REQUIRE_FUNCTION(ovr_RequestBoundaryVisible);
+  REQUIRE_FUNCTION(ovr_GetTextureSwapChainLength);
+  REQUIRE_FUNCTION(ovr_GetTextureSwapChainCurrentIndex);
+  REQUIRE_FUNCTION(ovr_GetTextureSwapChainDesc);
+  REQUIRE_FUNCTION(ovr_CommitTextureSwapChain);
+  REQUIRE_FUNCTION(ovr_DestroyTextureSwapChain);
+  REQUIRE_FUNCTION(ovr_DestroyMirrorTexture);
+  REQUIRE_FUNCTION(ovr_GetFovTextureSize);
+  REQUIRE_FUNCTION(ovr_GetRenderDesc2);
+  REQUIRE_FUNCTION(ovr_WaitToBeginFrame);
+  REQUIRE_FUNCTION(ovr_BeginFrame);
+  REQUIRE_FUNCTION(ovr_EndFrame);
+  REQUIRE_FUNCTION(ovr_SubmitFrame);
+  REQUIRE_FUNCTION(ovr_GetPerfStats);
+  REQUIRE_FUNCTION(ovr_ResetPerfStats);
+  REQUIRE_FUNCTION(ovr_GetPredictedDisplayTime);
+  REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
+  REQUIRE_FUNCTION(ovr_GetBool);
+  REQUIRE_FUNCTION(ovr_SetBool);
+  REQUIRE_FUNCTION(ovr_GetInt);
+  REQUIRE_FUNCTION(ovr_SetInt);
+  REQUIRE_FUNCTION(ovr_GetFloat);
+  REQUIRE_FUNCTION(ovr_SetFloat);
+  REQUIRE_FUNCTION(ovr_GetFloatArray);
+  REQUIRE_FUNCTION(ovr_SetFloatArray);
+  REQUIRE_FUNCTION(ovr_GetString);
+  REQUIRE_FUNCTION(ovr_SetString);
+  REQUIRE_FUNCTION(ovr_GetExternalCameras);
+  REQUIRE_FUNCTION(ovr_SetExternalCameraProperties);
+
+#ifdef XP_WIN
+
+  REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX);
+  REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX);
+  REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX);
+  REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX);
+
+#endif
+
+  REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL);
+  REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL);
+  REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL);
+  REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL);
+
+#undef REQUIRE_FUNCTION
+
+  return true;
+
+fail:
+  ovr_Initialize = nullptr;
+  PR_UnloadLibrary(mOvrLib);
+  mOvrLib = nullptr;
+  return false;
+}
+
+void
+OculusSession::UnloadOvrLib()
+{
+  if (mOvrLib) {
+    PR_UnloadLibrary(mOvrLib);
+    mOvrLib = nullptr;
+  }
+}
+
+bool
+OculusSession::StartLib(ovrInitFlags aFlags)
+{
+  if (mInitFlags == 0) {
+    ovrInitParams params;
+    memset(&params, 0, sizeof(params));
+    params.Flags = aFlags;
+    params.RequestedMinorVersion = OVR_MINOR_VERSION;
+    params.LogCallback = nullptr;
+    params.ConnectionTimeoutMS = 0;
+
+    ovrResult orv = ovr_Initialize(&params);
+
+    if (orv == ovrSuccess) {
+      mInitFlags = aFlags;
+    } else {
+      return false;
+    }
+  }
+  MOZ_ASSERT(mInitFlags == aFlags);
+  return true;
+}
+
+void
+OculusSession::StopLib()
+{
+  if (mInitFlags) {
+    ovr_Shutdown();
+    mInitFlags = (ovrInitFlags)0;
+  }
+}
+
+bool
+OculusSession::StartSession()
+{
+  // ovr_Create can be slow when no HMD is present and we wish
+  // to keep the same oculus session when possible, so we detect
+  // presence of an HMD with ovr_GetHmdDesc before calling ovr_Create
+  ovrHmdDesc desc = ovr_GetHmdDesc(NULL);
+  if (desc.Type == ovrHmd_None) {
+    // No HMD connected, destroy any existing session
+    if (mSession) {
+      ovr_Destroy(mSession);
+      mSession = nullptr;
+    }
+    return false;
+  }
+  if (mSession != nullptr) {
+    // HMD Detected and we already have a session, let's keep using it.
+    return true;
+  }
+
+  // HMD Detected and we don't have a session yet,
+  // try to create a new session
+  ovrSession session;
+  ovrGraphicsLuid luid;
+  ovrResult orv = ovr_Create(&session, &luid);
+  if (orv == ovrSuccess) {
+    orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
+    if (orv != ovrSuccess) {
+      NS_WARNING("ovr_SetTrackingOriginType failed.\n");
+    }
+    mSession = session;
+    return true;
+  }
+
+  // Failed to create a session for the HMD
+  return false;
+}
+
+void
+OculusSession::StopSession()
+{
+  if (mSession) {
+    ovr_Destroy(mSession);
+    mSession = nullptr;
+  }
+}
+
+bool
+OculusSession::CreateD3DObjects()
+{
+  RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
+  if (!device) {
+    return false;
+  }
+  if (!CreateD3DContext(device)) {
+    return false;
+  }
+  return true;
+}
+
+bool
+OculusSession::CreateShaders()
+{
+  if (!mQuadVS) {
+    if (FAILED(mDevice->CreateVertexShader(
+          sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
+      NS_WARNING("Failed to create vertex shader for Oculus");
+      return false;
+    }
+  }
+
+  if (!mQuadPS) {
+    if (FAILED(mDevice->CreatePixelShader(
+          sRGBShader.mData, sRGBShader.mLength, nullptr, &mQuadPS))) {
+      NS_WARNING("Failed to create pixel shader for Oculus");
+      return false;
+    }
+  }
+
+  CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants),
+                                 D3D11_BIND_CONSTANT_BUFFER,
+                                 D3D11_USAGE_DYNAMIC,
+                                 D3D11_CPU_ACCESS_WRITE);
+
+  if (!mVSConstantBuffer) {
+    if (FAILED(mDevice->CreateBuffer(
+          &cBufferDesc, nullptr, getter_AddRefs(mVSConstantBuffer)))) {
+      NS_WARNING("Failed to vertex shader constant buffer for Oculus");
+      return false;
+    }
+  }
+
+  if (!mPSConstantBuffer) {
+    cBufferDesc.ByteWidth = sizeof(layers::PixelShaderConstants);
+    if (FAILED(mDevice->CreateBuffer(
+          &cBufferDesc, nullptr, getter_AddRefs(mPSConstantBuffer)))) {
+      NS_WARNING("Failed to pixel shader constant buffer for Oculus");
+      return false;
+    }
+  }
+
+  if (!mLinearSamplerState) {
+    CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
+    if (FAILED(mDevice->CreateSamplerState(
+          &samplerDesc, getter_AddRefs(mLinearSamplerState)))) {
+      NS_WARNING("Failed to create sampler state for Oculus");
+      return false;
+    }
+  }
+
+  if (!mInputLayout) {
+    D3D11_INPUT_ELEMENT_DESC layout[] = {
+      { "POSITION",
+        0,
+        DXGI_FORMAT_R32G32_FLOAT,
+        0,
+        0,
+        D3D11_INPUT_PER_VERTEX_DATA,
+        0 },
+    };
+
+    if (FAILED(mDevice->CreateInputLayout(layout,
+                                          sizeof(layout) /
+                                            sizeof(D3D11_INPUT_ELEMENT_DESC),
+                                          sLayerQuadVS.mData,
+                                          sLayerQuadVS.mLength,
+                                          getter_AddRefs(mInputLayout)))) {
+      NS_WARNING("Failed to create input layout for Oculus");
+      return false;
+    }
+  }
+
+  if (!mVertexBuffer) {
+    Vertex vertices[] = {
+      { { 0.0, 0.0 } }, { { 1.0, 0.0 } }, { { 0.0, 1.0 } }, { { 1.0, 1.0 } }
+    };
+    CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
+    D3D11_SUBRESOURCE_DATA data;
+    data.pSysMem = (void*)vertices;
+
+    if (FAILED(mDevice->CreateBuffer(
+          &bufferDesc, &data, getter_AddRefs(mVertexBuffer)))) {
+      NS_WARNING("Failed to create vertex buffer for Oculus");
+      return false;
+    }
+  }
+
+  memset(&mVSConstants, 0, sizeof(mVSConstants));
+  memset(&mPSConstants, 0, sizeof(mPSConstants));
+  return true;
+}
+
+void
+OculusSession::DestroyShaders()
+{
+}
+
+bool
+OculusSession::UpdateConstantBuffers()
+{
+  HRESULT hr;
+  D3D11_MAPPED_SUBRESOURCE resource;
+  resource.pData = nullptr;
+
+  hr =
+    mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+  if (FAILED(hr) || !resource.pData) {
+    return false;
+  }
+  *(VertexShaderConstants*)resource.pData = mVSConstants;
+  mContext->Unmap(mVSConstantBuffer, 0);
+  resource.pData = nullptr;
+
+  hr =
+    mContext->Map(mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+  if (FAILED(hr) || !resource.pData) {
+    return false;
+  }
+  *(PixelShaderConstants*)resource.pData = mPSConstants;
+  mContext->Unmap(mPSConstantBuffer, 0);
+
+  ID3D11Buffer* buffer = mVSConstantBuffer;
+  mContext->VSSetConstantBuffers(0, 1, &buffer);
+  buffer = mPSConstantBuffer;
+  mContext->PSSetConstantBuffers(0, 1, &buffer);
+  return true;
+}
+
+bool
+OculusSession::StartRendering()
+{
+  if (!mTextureSet) {
+    /**
+     * The presentation format is determined by content, which describes the
+     * left and right eye rectangles in the VRLayer.  The default, if no
+     * coordinates are passed is to place the left and right eye textures
+     * side-by-side within the buffer.
+     *
+     * XXX - An optimization would be to dynamically resize this buffer
+     *       to accomodate sites that are choosing to render in a lower
+     *       resolution or are using space outside of the left and right
+     *       eye textures for other purposes.  (Bug 1291443)
+     */
+
+    ovrTextureSwapChainDesc desc;
+    memset(&desc, 0, sizeof(desc));
+    desc.Type = ovrTexture_2D;
+    desc.ArraySize = 1;
+    desc.Format = OVR_FORMAT_B8G8R8A8_UNORM_SRGB;
+    desc.Width = mPresentationSize.width;
+    desc.Height = mPresentationSize.height;
+    desc.MipLevels = 1;
+    desc.SampleCount = 1;
+    desc.StaticImage = false;
+    desc.MiscFlags = ovrTextureMisc_DX_Typeless;
+    desc.BindFlags = ovrTextureBind_DX_RenderTarget;
+
+    ovrResult orv =
+      ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet);
+    if (orv != ovrSuccess) {
+      NS_WARNING("ovr_CreateTextureSwapChainDX failed");
+      return false;
+    }
+
+    int textureCount = 0;
+    orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
+    if (orv != ovrSuccess) {
+      NS_WARNING("ovr_GetTextureSwapChainLength failed");
+      return false;
+    }
+    mTexture.SetLength(textureCount);
+    mRTView.SetLength(textureCount);
+    mSRV.SetLength(textureCount);
+    for (int i = 0; i < textureCount; ++i) {
+
+      ID3D11Texture2D* texture = nullptr;
+      orv = ovr_GetTextureSwapChainBufferDX(
+        mSession, mTextureSet, i, IID_PPV_ARGS(&texture));
+      if (orv != ovrSuccess) {
+        NS_WARNING("Failed to create Oculus texture swap chain.");
+        return false;
+      }
+
+      RefPtr<ID3D11RenderTargetView> rtView;
+      CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D,
+                                             DXGI_FORMAT_B8G8R8A8_UNORM);
+      HRESULT hr = mDevice->CreateRenderTargetView(
+        texture, &rtvDesc, getter_AddRefs(rtView));
+      if (FAILED(hr)) {
+        NS_WARNING(
+          "Failed to create RenderTargetView for Oculus texture swap chain.");
+        texture->Release();
+        return false;
+      }
+
+      RefPtr<ID3D11ShaderResourceView> srv;
+      CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D,
+                                               DXGI_FORMAT_B8G8R8A8_UNORM);
+      hr = mDevice->CreateShaderResourceView(
+        texture, &srvDesc, getter_AddRefs(srv));
+      if (FAILED(hr)) {
+        NS_WARNING(
+          "Failed to create ShaderResourceView for Oculus texture swap chain.");
+        texture->Release();
+        return false;
+      }
+
+      mTexture[i] = texture;
+      mRTView[i] = rtView;
+      mSRV[i] = srv;
+      texture->Release();
+    }
+  }
+  return true;
+}
+
+bool
+OculusSession::IsPresentationReady() const
+{
+  return mTextureSet != nullptr;
+}
+
+void
+OculusSession::StopRendering()
+{
+  mSRV.Clear();
+  mRTView.Clear();
+  mTexture.Clear();
+
+  if (mTextureSet && mSession) {
+    ovr_DestroyTextureSwapChain(mSession, mTextureSet);
+  }
+  mTextureSet = nullptr;
+  mIsPresenting = false;
+}
+
+bool
+OculusSession::InitState(VRSystemState& aSystemState)
+{
+  VRDisplayState& state = aSystemState.displayState;
+  strncpy(state.mDisplayName, "Oculus VR HMD", kVRDisplayNameMaxLen);
+  state.mIsConnected = true;
+  state.mIsMounted = false;
+
+  ovrHmdDesc desc = ovr_GetHmdDesc(mSession);
+
+  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
+  if (desc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
+  }
+  if (desc.AvailableTrackingCaps & ovrTrackingCap_Position) {
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
+  }
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
+  state.mReportsDroppedFrames = true;
+
+  mFOVPort[VRDisplayState::Eye_Left] = desc.DefaultEyeFov[ovrEye_Left];
+  mFOVPort[VRDisplayState::Eye_Right] = desc.DefaultEyeFov[ovrEye_Right];
+
+  state.mEyeFOV[VRDisplayState::Eye_Left] =
+    FromFovPort(mFOVPort[VRDisplayState::Eye_Left]);
+  state.mEyeFOV[VRDisplayState::Eye_Right] =
+    FromFovPort(mFOVPort[VRDisplayState::Eye_Right]);
+
+  float pixelsPerDisplayPixel = 1.0;
+  ovrSizei texSize[2];
+
+  // get eye texture sizes
+  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
+    texSize[eye] = ovr_GetFovTextureSize(
+      mSession, (ovrEyeType)eye, mFOVPort[eye], pixelsPerDisplayPixel);
+  }
+
+  // take the max of both for eye resolution
+  state.mEyeResolution.width = std::max(texSize[VRDisplayState::Eye_Left].w,
+                                        texSize[VRDisplayState::Eye_Right].w);
+  state.mEyeResolution.height = std::max(texSize[VRDisplayState::Eye_Left].h,
+                                         texSize[VRDisplayState::Eye_Right].h);
+
+  // default to an identity quaternion
+  aSystemState.sensorState.pose.orientation[3] = 1.0f;
+
+  UpdateStageParameters(state);
+  UpdateEyeParameters(aSystemState);
+
+  VRHMDSensorState& sensorState = aSystemState.sensorState;
+  sensorState.flags =
+    (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_Orientation |
+                               (int)VRDisplayCapabilityFlags::Cap_Position);
+  sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
+
+  return true;
+}
+
+void
+OculusSession::UpdateStageParameters(VRDisplayState& aState)
+{
+  ovrVector3f playArea;
+  ovrResult res =
+    ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
+  if (res == ovrSuccess) {
+    aState.mStageSize.width = playArea.x;
+    aState.mStageSize.height = playArea.z;
+  } else {
+    // If we fail, fall back to reasonable defaults.
+    // 1m x 1m space
+    aState.mStageSize.width = 1.0f;
+    aState.mStageSize.height = 1.0f;
+  }
+
+  float eyeHeight =
+    ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+
+  aState.mSittingToStandingTransform[0] = 1.0f;
+  aState.mSittingToStandingTransform[1] = 0.0f;
+  aState.mSittingToStandingTransform[2] = 0.0f;
+  aState.mSittingToStandingTransform[3] = 0.0f;
+
+  aState.mSittingToStandingTransform[4] = 0.0f;
+  aState.mSittingToStandingTransform[5] = 1.0f;
+  aState.mSittingToStandingTransform[6] = 0.0f;
+  aState.mSittingToStandingTransform[7] = 0.0f;
+
+  aState.mSittingToStandingTransform[8] = 0.0f;
+  aState.mSittingToStandingTransform[9] = 0.0f;
+  aState.mSittingToStandingTransform[10] = 1.0f;
+  aState.mSittingToStandingTransform[11] = 0.0f;
+
+  aState.mSittingToStandingTransform[12] = 0.0f;
+  aState.mSittingToStandingTransform[13] = eyeHeight;
+  aState.mSittingToStandingTransform[14] = 0.0f;
+  aState.mSittingToStandingTransform[15] = 1.0f;
+}
+
+void
+OculusSession::UpdateEyeParameters(VRSystemState& aState)
+{
+  if (!mSession) {
+    return;
+  }
+  // This must be called every frame in order to
+  // account for continuous adjustments to ipd.
+  gfx::Matrix4x4 headToEyeTransforms[2];
+  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
+    // As of Oculus 1.17 SDK, we must use the ovr_GetRenderDesc2 function to
+    // return the updated version of ovrEyeRenderDesc.  This is normally done by
+    // the Oculus static lib shim, but we need to do this explicitly as we are
+    // loading the Oculus runtime dll directly.
+    ovrEyeRenderDesc renderDesc =
+      ovr_GetRenderDesc2(mSession, (ovrEyeType)eye, mFOVPort[eye]);
+    aState.displayState.mEyeTranslation[eye].x =
+      renderDesc.HmdToEyePose.Position.x;
+    aState.displayState.mEyeTranslation[eye].y =
+      renderDesc.HmdToEyePose.Position.y;
+    aState.displayState.mEyeTranslation[eye].z =
+      renderDesc.HmdToEyePose.Position.z;
+
+    Matrix4x4 pose;
+    pose.SetRotationFromQuaternion(
+      gfx::Quaternion(renderDesc.HmdToEyePose.Orientation.x,
+                      renderDesc.HmdToEyePose.Orientation.y,
+                      renderDesc.HmdToEyePose.Orientation.z,
+                      renderDesc.HmdToEyePose.Orientation.w));
+    pose.PreTranslate(renderDesc.HmdToEyePose.Position.x,
+                      renderDesc.HmdToEyePose.Position.y,
+                      renderDesc.HmdToEyePose.Position.z);
+    pose.Invert();
+    headToEyeTransforms[eye] = pose;
+  }
+  aState.sensorState.CalcViewMatrices(headToEyeTransforms);
+
+  Matrix4x4 matView[2];
+  memcpy(matView[0].components,
+         aState.sensorState.leftViewMatrix,
+         sizeof(float) * 16);
+  memcpy(matView[1].components,
+         aState.sensorState.rightViewMatrix,
+         sizeof(float) * 16);
+
+  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
+    Point3D eyeTranslation;
+    Quaternion eyeRotation;
+    Point3D eyeScale;
+    if (!matView[eye].Decompose(eyeTranslation, eyeRotation, eyeScale)) {
+      NS_WARNING("Failed to decompose eye pose matrix for Oculus");
+    }
+
+    mFrameStartPose[eye].Orientation.x = eyeRotation.x;
+    mFrameStartPose[eye].Orientation.y = eyeRotation.y;
+    mFrameStartPose[eye].Orientation.z = eyeRotation.z;
+    mFrameStartPose[eye].Orientation.w = eyeRotation.w;
+    mFrameStartPose[eye].Position.x = eyeTranslation.x;
+    mFrameStartPose[eye].Position.y = eyeTranslation.y;
+    mFrameStartPose[eye].Position.z = eyeTranslation.z;
+  }
+}
+
+void
+OculusSession::UpdateHeadsetPose(VRSystemState& aState)
+{
+  if (!mSession) {
+    return;
+  }
+  double predictedFrameTime = 0.0f;
+  if (gfxPrefs::VRPosePredictionEnabled()) {
+    // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't
+    // use the result. If we don't call it, the Oculus driver will spew out many
+    // warnings...
+    predictedFrameTime = ovr_GetPredictedDisplayTime(mSession, 0);
+  }
+  ovrTrackingState trackingState =
+    ovr_GetTrackingState(mSession, predictedFrameTime, true);
+  ovrPoseStatef& pose(trackingState.HeadPose);
+
+  aState.sensorState.timestamp = pose.TimeInSeconds;
+
+  if (trackingState.StatusFlags & ovrStatus_OrientationTracked) {
+    aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
+
+    aState.sensorState.pose.orientation[0] = pose.ThePose.Orientation.x;
+    aState.sensorState.pose.orientation[1] = pose.ThePose.Orientation.y;
+    aState.sensorState.pose.orientation[2] = pose.ThePose.Orientation.z;
+    aState.sensorState.pose.orientation[3] = pose.ThePose.Orientation.w;
+
+    aState.sensorState.pose.angularVelocity[0] = pose.AngularVelocity.x;
+    aState.sensorState.pose.angularVelocity[1] = pose.AngularVelocity.y;
+    aState.sensorState.pose.angularVelocity[2] = pose.AngularVelocity.z;
+
+    aState.sensorState.flags |=
+      VRDisplayCapabilityFlags::Cap_AngularAcceleration;
+
+    aState.sensorState.pose.angularAcceleration[0] = pose.AngularAcceleration.x;
+    aState.sensorState.pose.angularAcceleration[1] = pose.AngularAcceleration.y;
+    aState.sensorState.pose.angularAcceleration[2] = pose.AngularAcceleration.z;
+  } else {
+    // default to an identity quaternion
+    aState.sensorState.pose.orientation[3] = 1.0f;
+  }
+
+  if (trackingState.StatusFlags & ovrStatus_PositionTracked) {
+    float eyeHeight =
+      ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+    aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;
+
+    aState.sensorState.pose.position[0] = pose.ThePose.Position.x;
+    aState.sensorState.pose.position[1] = pose.ThePose.Position.y - eyeHeight;
+    aState.sensorState.pose.position[2] = pose.ThePose.Position.z;
+
+    aState.sensorState.pose.linearVelocity[0] = pose.LinearVelocity.x;
+    aState.sensorState.pose.linearVelocity[1] = pose.LinearVelocity.y;
+    aState.sensorState.pose.linearVelocity[2] = pose.LinearVelocity.z;
+
+    aState.sensorState.flags |=
+      VRDisplayCapabilityFlags::Cap_LinearAcceleration;
+
+    aState.sensorState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
+    aState.sensorState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
+    aState.sensorState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
+  }
+  aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_External;
+  aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
+  aState.sensorState.flags |= VRDisplayCapabilityFlags::Cap_Present;
+}
+
+void
+OculusSession::UpdateControllers(VRSystemState& aState)
+{
+  if (!mSession) {
+    return;
+  }
+
+  ovrInputState inputState;
+  bool hasInputState =
+    ovr_GetInputState(mSession, ovrControllerType_Touch, &inputState) ==
+    ovrSuccess;
+
+  if (!hasInputState) {
+    return;
+  }
+
+  EnumerateControllers(aState, inputState);
+  UpdateControllerInputs(aState, inputState);
+  UpdateControllerPose(aState, inputState);
+}
+
+void
+OculusSession::UpdateControllerPose(VRSystemState& aState,
+                                    const ovrInputState& aInputState)
+{
+  ovrTrackingState trackingState = ovr_GetTrackingState(mSession, 0.0, false);
+  for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
+    // Left Touch Controller will always be at index 0 and
+    // and Right Touch Controller will always be at index 1
+    VRControllerState& controllerState = aState.controllerState[handIdx];
+    if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
+      ovrPoseStatef& pose = trackingState.HandPoses[handIdx];
+      bool bNewController =
+        !(controllerState.flags & dom::GamepadCapabilityFlags::Cap_Orientation);
+      if (bNewController) {
+        controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Orientation;
+        controllerState.flags |= dom::GamepadCapabilityFlags::Cap_Position;
+        controllerState.flags |=
+          dom::GamepadCapabilityFlags::Cap_AngularAcceleration;
+        controllerState.flags |=
+          dom::GamepadCapabilityFlags::Cap_LinearAcceleration;
+      }
+
+      if (bNewController || trackingState.HandStatusFlags[handIdx] &
+                              ovrStatus_OrientationTracked) {
+        controllerState.pose.orientation[0] = pose.ThePose.Orientation.x;
+        controllerState.pose.orientation[1] = pose.ThePose.Orientation.y;
+        controllerState.pose.orientation[2] = pose.ThePose.Orientation.z;
+        controllerState.pose.orientation[3] = pose.ThePose.Orientation.w;
+        controllerState.pose.angularVelocity[0] = pose.AngularVelocity.x;
+        controllerState.pose.angularVelocity[1] = pose.AngularVelocity.y;
+        controllerState.pose.angularVelocity[2] = pose.AngularVelocity.z;
+        controllerState.pose.angularAcceleration[0] =
+          pose.AngularAcceleration.x;
+        controllerState.pose.angularAcceleration[1] =
+          pose.AngularAcceleration.y;
+        controllerState.pose.angularAcceleration[2] =
+          pose.AngularAcceleration.z;
+        controllerState.isOrientationValid = true;
+      } else {
+        controllerState.isOrientationValid = false;
+      }
+      if (bNewController ||
+          trackingState.HandStatusFlags[handIdx] & ovrStatus_PositionTracked) {
+        controllerState.pose.position[0] = pose.ThePose.Position.x;
+        controllerState.pose.position[1] = pose.ThePose.Position.y;
+        controllerState.pose.position[2] = pose.ThePose.Position.z;
+        controllerState.pose.linearVelocity[0] = pose.LinearVelocity.x;
+        controllerState.pose.linearVelocity[1] = pose.LinearVelocity.y;
+        controllerState.pose.linearVelocity[2] = pose.LinearVelocity.z;
+        controllerState.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
+        controllerState.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
+        controllerState.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
+
+        float eyeHeight =
+          ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+        controllerState.pose.position[1] -= eyeHeight;
+        controllerState.isPositionValid = true;
+      } else {
+        controllerState.isPositionValid = false;
+      }
+    }
+  }
+}
+
+void
+OculusSession::EnumerateControllers(VRSystemState& aState,
+                                    const ovrInputState& aInputState)
+{
+  for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
+    // Left Touch Controller will always be at index 0 and
+    // and Right Touch Controller will always be at index 1
+    VRControllerState& controllerState = aState.controllerState[handIdx];
+    if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
+      bool bNewController = false;
+      // Left Touch Controller detected
+      if (controllerState.controllerName[0] == '\0') {
+        // Controller has been just enumerated
+        strncpy(controllerState.controllerName,
+                OculusControllerNames[handIdx],
+                kVRControllerNameMaxLen);
+        controllerState.hand = OculusControllerHand[handIdx];
+        controllerState.numButtons = kNumOculusButtons;
+        controllerState.numAxes = kNumOculusAxes;
+        controllerState.numHaptics = kNumOculusHaptcs;
+        bNewController = true;
+      }
+    } else {
+      // Left Touch Controller not detected
+      if (controllerState.controllerName[0] != '\0') {
+        // Clear any newly disconnected ontrollers
+        memset(&controllerState, 0, sizeof(VRControllerState));
+      }
+    }
+  }
+}
+
+void
+OculusSession::UpdateControllerInputs(VRSystemState& aState,
+                                      const ovrInputState& aInputState)
+{
+  const float triggerThreshold = gfxPrefs::VRControllerTriggerThreshold();
+
+  for (uint32_t handIdx = 0; handIdx < 2; handIdx++) {
+    // Left Touch Controller will always be at index 0 and
+    // and Right Touch Controller will always be at index 1
+    VRControllerState& controllerState = aState.controllerState[handIdx];
+    if (aInputState.ControllerType & OculusControllerTypes[handIdx]) {
+      // Update Button States
+      controllerState.buttonPressed = 0;
+      controllerState.buttonTouched = 0;
+      uint32_t buttonIdx = 0;
+
+      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+      buttonIdx++;
+      UpdateTrigger(controllerState,
+                    buttonIdx,
+                    aInputState.IndexTrigger[handIdx],
+                    triggerThreshold);
+      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+      buttonIdx++;
+      UpdateTrigger(controllerState,
+                    buttonIdx,
+                    aInputState.HandTrigger[handIdx],
+                    triggerThreshold);
+      buttonIdx++;
+      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+      buttonIdx++;
+      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+      buttonIdx++;
+      UpdateButton(aInputState, handIdx, buttonIdx, controllerState);
+      buttonIdx++;
+
+      MOZ_ASSERT(buttonIdx == kNumOculusButtons);
+
+      // Update Thumbstick axis
+      uint32_t axisIdx = 0;
+      float axisValue = aInputState.Thumbstick[handIdx].x;
+      if (abs(axisValue) < 0.0000009f) {
+        axisValue = 0.0f; // Clear noise signal
+      }
+      controllerState.axisValue[axisIdx] = axisValue;
+      axisIdx++;
+
+      // Note that y axis is intentionally inverted!
+      axisValue = -aInputState.Thumbstick[handIdx].y;
+      if (abs(axisValue) < 0.0000009f) {
+        axisValue = 0.0f; // Clear noise signal
+      }
+      controllerState.axisValue[axisIdx] = axisValue;
+      axisIdx++;
+
+      MOZ_ASSERT(axisIdx == kNumOculusAxes);
+    }
+  }
+}
+
+void
+OculusSession::UpdateTelemetry(VRSystemState& aSystemState)
+{
+  if (!mSession) {
+    return;
+  }
+  ovrPerfStats perfStats;
+  if (ovr_GetPerfStats(mSession, &perfStats) == ovrSuccess) {
+    if (perfStats.FrameStatsCount) {
+      aSystemState.displayState.mDroppedFrameCount =
+        perfStats.FrameStats[0].AppDroppedFrameCount;
+    }
+  }
+}
+
+void
+OculusSession::VibrateHaptic(uint32_t aControllerIdx,
+                             uint32_t aHapticIndex,
+                             float aIntensity,
+                             float aDuration)
+{
+  if (!mSession) {
+    return;
+  }
+
+  if (aDuration <= 0.0f) {
+    StopVibrateHaptic(aControllerIdx);
+    return;
+  }
+
+  // Vibration amplitude in the [0.0, 1.0] range
+  MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
+  mHapticPulseIntensity[aControllerIdx] = aIntensity > 1.0 ? 1.0 : aIntensity;
+  mRemainingVibrateTime[aControllerIdx] = aDuration;
+  ovrControllerType hand = OculusControllerTypes[aControllerIdx];
+
+  // The gamepad extensions API does not yet have independent control
+  // of frequency and amplitude.  We are always sending 0.0f (160hz)
+  // to the frequency argument.
+  ovrResult result = ovr_SetControllerVibration(
+    mSession, hand, 0.0f, mHapticPulseIntensity[aControllerIdx]);
+  if (result != ovrSuccess) {
+    // This may happen if called when not presenting.
+    gfxWarning() << "ovr_SetControllerVibration failed.";
+  }
+}
+
+void
+OculusSession::StopVibrateHaptic(uint32_t aControllerIdx)
+{
+  if (!mSession) {
+    return;
+  }
+  MOZ_ASSERT(aControllerIdx >= 0 && aControllerIdx <= 1);
+  ovrControllerType hand = OculusControllerTypes[aControllerIdx];
+  mRemainingVibrateTime[aControllerIdx] = 0.0f;
+  mHapticPulseIntensity[aControllerIdx] = 0.0f;
+
+  ovrResult result = ovr_SetControllerVibration(mSession, hand, 0.0f, 0.0f);
+  if (result != ovrSuccess) {
+    // This may happen if called when not presenting.
+    gfxWarning() << "ovr_SetControllerVibration failed.";
+  }
+}
+
+void
+OculusSession::StopAllHaptics()
+{
+  // Left Oculus Touch
+  StopVibrateHaptic(0);
+  // Right Oculus Touch
+  StopVibrateHaptic(1);
+}
+
+void
+OculusSession::UpdateHaptics()
+{
+  if (!mSession) {
+    return;
+  }
+  // The Oculus API and hardware takes at least 33ms to respond
+  // to haptic state changes, so it is not beneficial to create
+  // a dedicated haptic feedback thread and update multiple
+  // times per frame.
+  // If we wish to support more accurate effects with sub-frame timing,
+  // we should use the buffered haptic feedback API's.
+
+  TimeStamp now = TimeStamp::Now();
+  if (mLastHapticUpdate.IsNull()) {
+    mLastHapticUpdate = now;
+    return;
+  }
+  float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
+  mLastHapticUpdate = now;
+  for (int i = 0; i < 2; i++) {
+    if (mRemainingVibrateTime[i] <= 0.0f) {
+      continue;
+    }
+    mRemainingVibrateTime[i] -= deltaTime;
+    ovrControllerType hand = OculusControllerTypes[i];
+    if (mRemainingVibrateTime[i] > 0.0f) {
+      ovrResult result = ovr_SetControllerVibration(
+        mSession, hand, 0.0f, mHapticPulseIntensity[i]);
+      if (result != ovrSuccess) {
+        // This may happen if called when not presenting.
+        gfxWarning() << "ovr_SetControllerVibration failed.";
+      }
+    } else {
+      StopVibrateHaptic(i);
+    }
+  }
+}
+
+} // namespace mozilla
+} // namespace gfx
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/OculusSession.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_SERVICE_OCULUSSESSION_H
+#define GFX_VR_SERVICE_OCULUSSESSION_H
+
+#include "VRSession.h"
+
+#include "mozilla/gfx/2D.h"
+#include "moz_external_vr.h"
+#include "nsTArray.h"
+#include "../ovr_capi_dynamic.h"
+#include "prlink.h"
+#include "ShaderDefinitionsD3D11.h" // for VertexShaderConstants and PixelShaderConstants
+
+struct ID3D11Device;
+
+namespace mozilla {
+namespace layers {
+struct VertexShaderConstants;
+struct PixelShaderConstants;
+}
+namespace gfx {
+
+class OculusSession : public VRSession
+{
+public:
+  OculusSession();
+  virtual ~OculusSession();
+
+  bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
+  void Shutdown() override;
+  void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
+  void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
+  bool StartPresentation() override;
+  void StopPresentation() override;
+  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                   ID3D11Texture2D* aTexture) override;
+  void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+                    float aIntensity, float aDuration) override;
+  void StopVibrateHaptic(uint32_t aControllerIdx) override;
+  void StopAllHaptics() override;
+
+private:
+  bool LoadOvrLib();
+  void UnloadOvrLib();
+  bool StartLib(ovrInitFlags aFlags);
+  void StopLib();
+  bool StartSession();
+  void StopSession();
+  bool StartRendering();
+  void StopRendering();
+  bool CreateD3DObjects();
+  bool CreateShaders();
+  void DestroyShaders();
+  void CoverTransitions();
+  void UpdateVisibility();
+  bool ChangeVisibility(bool bVisible);
+  bool InitState(mozilla::gfx::VRSystemState& aSystemState);
+  void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState);
+  void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState);
+  void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
+  void UpdateControllers(VRSystemState& aState);
+  void UpdateControllerInputs(VRSystemState& aState,
+                              const ovrInputState& aInputState);
+  void UpdateHaptics();
+  void EnumerateControllers(VRSystemState& aState,
+                            const ovrInputState& aInputState);
+  void UpdateControllerPose(VRSystemState& aState,
+                            const ovrInputState& aInputState);
+  void UpdateTelemetry(VRSystemState& aSystemState);
+  bool IsPresentationReady() const;
+  bool UpdateConstantBuffers();
+
+  PRLibrary* mOvrLib;
+  ovrSession mSession;
+  ovrInitFlags mInitFlags;
+  ovrTextureSwapChain mTextureSet;
+  nsTArray<RefPtr<ID3D11RenderTargetView>> mRTView;
+  nsTArray<RefPtr<ID3D11Texture2D>> mTexture;
+  nsTArray<RefPtr<ID3D11ShaderResourceView>> mSRV;
+
+  ID3D11VertexShader* mQuadVS;
+  ID3D11PixelShader* mQuadPS;
+  RefPtr<ID3D11SamplerState> mLinearSamplerState;
+  layers::VertexShaderConstants mVSConstants;
+  layers::PixelShaderConstants mPSConstants;
+  RefPtr<ID3D11Buffer> mVSConstantBuffer;
+  RefPtr<ID3D11Buffer> mPSConstantBuffer;
+  RefPtr<ID3D11Buffer> mVertexBuffer;
+  RefPtr<ID3D11InputLayout> mInputLayout;
+
+  IntSize mPresentationSize;
+  ovrFovPort mFOVPort[2];
+
+  // Most recent HMD eye poses, from start of frame
+  ovrPosef mFrameStartPose[2];
+
+  float mRemainingVibrateTime[2];
+  float mHapticPulseIntensity[2];
+  TimeStamp mLastHapticUpdate;
+
+  // The timestamp of the last ending presentation
+  TimeStamp mLastPresentationEnd;
+  bool mIsPresenting;
+};
+
+} // namespace mozilla
+} // namespace gfx
+
+#endif // GFX_VR_SERVICE_OCULUSSESSION_H
--- a/gfx/vr/service/OpenVRSession.cpp
+++ b/gfx/vr/service/OpenVRSession.cpp
@@ -74,45 +74,26 @@ UpdateButton(VRControllerState& aState, 
     // not touched
     aState.buttonTouched &= ~mask;
   } else {
     // touched
     aState.buttonTouched |= mask;
   }
 }
 
-void
-UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex, float aValue, float aThreshold)
-{
-  // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
-  // We prefer to let developers to set their own threshold for the adjustment.
-  // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
-  // we just check the button value is larger than the threshold value or not.
-  uint64_t mask = (1ULL << aButtonIndex);
-  aState.triggerValue[aButtonIndex] = aValue;
-  if (aValue > aThreshold) {
-    aState.buttonPressed |= mask;
-    aState.buttonTouched |= mask;
-  } else {
-    aState.buttonPressed &= ~mask;
-    aState.buttonTouched &= ~mask;
-  }
-}
-
 }; // anonymous namespace
 
 OpenVRSession::OpenVRSession()
   : VRSession()
   , mVRSystem(nullptr)
   , mVRChaperone(nullptr)
   , mVRCompositor(nullptr)
   , mControllerDeviceIndex{}
   , mHapticPulseRemaining{}
   , mHapticPulseIntensity{}
-  , mShouldQuit(false)
   , mIsWindowsMR(false)
   , mControllerHapticStateMutex("OpenVRSession::mControllerHapticStateMutex")
 {
 }
 
 OpenVRSession::~OpenVRSession()
 {
   Shutdown();
@@ -779,22 +760,16 @@ OpenVRSession::StartFrame(mozilla::gfx::
   UpdateHeadsetPose(aSystemState);
   UpdateEyeParameters(aSystemState);
   EnumerateControllers(aSystemState);
   UpdateControllerButtons(aSystemState);
   UpdateControllerPoses(aSystemState);
   UpdateTelemetry(aSystemState);
 }
 
-bool
-OpenVRSession::ShouldQuit() const
-{
-  return mShouldQuit;
-}
-
 void
 OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState)
 {
   bool isHmdPresent = ::vr::VR_IsHmdPresent();
   if (!isHmdPresent) {
     mShouldQuit = true;
   }
 
@@ -830,75 +805,37 @@ OpenVRSession::ProcessEvents(mozilla::gf
         break;
       default:
         // ignore
         break;
     }
   }
 }
 
+#if defined(XP_WIN)
 bool
-OpenVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer)
+OpenVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                           ID3D11Texture2D* aTexture)
 {
-#if defined(XP_WIN)
-
-  if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor) {
-      RefPtr<ID3D11Texture2D> dxTexture;
-      HRESULT hr = mDevice->OpenSharedResource((HANDLE)aLayer.mTextureHandle,
-        __uuidof(ID3D11Texture2D),
-        (void**)(ID3D11Texture2D**)getter_AddRefs(dxTexture));
-      if (FAILED(hr) || !dxTexture) {
-        NS_WARNING("Failed to open shared texture");
-        return false;
-      }
-
-      // Similar to LockD3DTexture in TextureD3D11.cpp
-      RefPtr<IDXGIKeyedMutex> mutex;
-      dxTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
-      if (mutex) {
-        HRESULT hr = mutex->AcquireSync(0, 1000);
-        if (hr == WAIT_TIMEOUT) {
-          gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
-        }
-        else if (hr == WAIT_ABANDONED) {
-          gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
-        }
-        if (FAILED(hr)) {
-          NS_WARNING("Failed to lock the texture");
-          return false;
-        }
-      }
-      bool success = SubmitFrame((void *)dxTexture,
+  return SubmitFrame((void *)aTexture,
                      ::vr::ETextureType::TextureType_DirectX,
                      aLayer.mLeftEyeRect, aLayer.mRightEyeRect);
-      if (mutex) {
-        HRESULT hr = mutex->ReleaseSync(0);
-        if (FAILED(hr)) {
-          NS_WARNING("Failed to unlock the texture");
-        }
-      }
-      if (!success) {
-        return false;
-      }
-      return true;
-  }
+}
 
 #elif defined(XP_MACOSX)
-
-  if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_MacIOSurface) {
-    return SubmitFrame(aLayer.mTextureHandle,
-                       ::vr::ETextureType::TextureType_IOSurface,
-                       aLayer.mLeftEyeRect, aLayer.mRightEyeRect);
-  }
-
+bool
+OpenVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                           MacIOSurface* aTexture)
+{
+  return SubmitFrame((void *)aTexture,
+                     ::vr::ETextureType::TextureType_IOSurface,
+                     aLayer.mLeftEyeRect, aLayer.mRightEyeRect);
+}
 #endif
 
-  return false;
-}
-
 bool
 OpenVRSession::SubmitFrame(void* aTextureHandle,
                            ::vr::ETextureType aTextureType,
                            const VRLayerEyeRect& aLeftEyeRect,
                            const VRLayerEyeRect& aRightEyeRect)
 {
   ::vr::Texture_t tex;
   tex.handle = aTextureHandle;
@@ -933,21 +870,16 @@ OpenVRSession::SubmitFrame(void* aTextur
 
 void
 OpenVRSession::StopPresentation()
 {
   mVRCompositor->ClearLastSubmittedFrame();
 
   ::vr::Compositor_CumulativeStats stats;
   mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
-  // TODO - Need to send telemetry back to browser.
-  // Bug 1473398 will refactor this original gfxVROpenVR code:
-  //   const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
-  //                                      mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
-  // Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
 }
 
 bool
 OpenVRSession::StartPresentation()
 {
   return true;
 }
 
--- a/gfx/vr/service/OpenVRSession.h
+++ b/gfx/vr/service/OpenVRSession.h
@@ -32,34 +32,40 @@ class OpenVRSession : public VRSession
 public:
   OpenVRSession();
   virtual ~OpenVRSession();
 
   bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
   void Shutdown() override;
   void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
   void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
-  bool ShouldQuit() const override;
   bool StartPresentation() override;
   void StopPresentation() override;
-  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) override;
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                     float aIntensity, float aDuration) override;
   void StopVibrateHaptic(uint32_t aControllerIdx) override;
   void StopAllHaptics() override;
 
+protected:
+#if defined(XP_WIN)
+  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                   ID3D11Texture2D* aTexture) override;
+#elif defined(XP_MACOSX)
+  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                   MacIOSurface* aTexture) override;
+#endif
+
 private:
   // OpenVR State
   ::vr::IVRSystem* mVRSystem = nullptr;
   ::vr::IVRChaperone* mVRChaperone = nullptr;
   ::vr::IVRCompositor* mVRCompositor = nullptr;
   ::vr::TrackedDeviceIndex_t mControllerDeviceIndex[kVRControllerMaxCount];
   float mHapticPulseRemaining[kVRControllerMaxCount][kNumOpenVRHaptics];
   float mHapticPulseIntensity[kVRControllerMaxCount][kNumOpenVRHaptics];
-  bool mShouldQuit;
   bool mIsWindowsMR;
   TimeStamp mLastHapticUpdate;
 
   bool InitState(mozilla::gfx::VRSystemState& aSystemState);
   void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState);
   void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState);
   void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
   void EnumerateControllers(VRSystemState& aState);
--- a/gfx/vr/service/VRService.cpp
+++ b/gfx/vr/service/VRService.cpp
@@ -1,48 +1,53 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VRService.h"
 #include "gfxPrefs.h"
-#include "base/thread.h"                // for Thread
-#include <cstring>                      // for memcmp
+#include "base/thread.h" // for Thread
+#include <cstring>       // for memcmp
 
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if defined(XP_WIN)
+#include "OculusSession.h"
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX) ||                                   \
+  (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
 #include "OpenVRSession.h"
 #endif
 #if !defined(MOZ_WIDGET_ANDROID)
 #include "OSVRSession.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace std;
 
 namespace {
 
 int64_t
 FrameIDFromBrowserState(const mozilla::gfx::VRBrowserState& aState)
 {
-  for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) {
+  for (int iLayer = 0; iLayer < kVRLayerMaxCount; iLayer++) {
     const VRLayerState& layer = aState.layerState[iLayer];
     if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
       return layer.layer_stereo_immersive.mFrameId;
     }
   }
   return 0;
 }
 
 bool
 IsImmersiveContentActive(const mozilla::gfx::VRBrowserState& aState)
 {
-  for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) {
+  for (int iLayer = 0; iLayer < kVRLayerMaxCount; iLayer++) {
     const VRLayerState& layer = aState.layerState[iLayer];
     if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
       return true;
     }
   }
   return false;
 }
 
@@ -57,47 +62,60 @@ VRService::Create()
     return nullptr;
   }
 
   RefPtr<VRService> service = new VRService();
   return service.forget();
 }
 
 VRService::VRService()
- : mSystemState{}
- , mBrowserState{}
- , mBrowserGeneration(0)
- , mServiceThread(nullptr)
- , mShutdownRequested(false)
- , mAPIShmem(nullptr)
- , mTargetShmemFile(0)
- , mLastHapticState{}
- , mFrameStartTime{}
+  : mSystemState{}
+  , mBrowserState{}
+  , mBrowserGeneration(0)
+  , mServiceThread(nullptr)
+  , mShutdownRequested(false)
+  , mAPIShmem(nullptr)
+  , mTargetShmemFile(0)
+  , mLastHapticState{}
+  , mFrameStartTime{}
+  , mVRProcessEnabled(gfxPrefs::VRProcessEnabled())
 {
   // When we have the VR process, we map the memory
   // of mAPIShmem from GPU process.
   // If we don't have the VR process, we will instantiate
   // mAPIShmem in VRService.
-  if (!gfxPrefs::VRProcessEnabled()) {
+  if (!mVRProcessEnabled) {
     mAPIShmem = new VRExternalShmem();
     memset(mAPIShmem, 0, sizeof(VRExternalShmem));
   }
 }
 
 VRService::~VRService()
 {
   Stop();
 
-  if (!gfxPrefs::VRProcessEnabled() && mAPIShmem) {
+  if (!mVRProcessEnabled && mAPIShmem) {
     delete mAPIShmem;
     mAPIShmem = nullptr;
   }
 }
 
 void
+VRService::Refresh()
+{
+  if (!mAPIShmem) {
+    return;
+  }
+
+  if (mAPIShmem->state.displayState.shutdown) {
+    Stop();
+  }
+}
+
+void
 VRService::Start()
 {
   if (!mServiceThread) {
     /**
      * We must ensure that any time the service is re-started, that
      * the VRSystemState is reset, including mSystemState.enumerationCompleted
      * This must happen before VRService::Start returns to the caller, in order
      * to prevent the WebVR/WebXR promises from being resolved before the
@@ -108,30 +126,30 @@ VRService::Start()
 
     mServiceThread = new base::Thread("VRService");
     base::Thread::Options options;
     /* Timeout values are powers-of-two to enable us get better data.
        128ms is chosen for transient hangs because 8Hz should be the minimally
        acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
     options.transient_hang_timeout = 128; // milliseconds
     /* 2048ms is chosen for permanent hangs because it's longer than most
-     * Compositor hangs seen in the wild, but is short enough to not miss getting
-     * native hang stacks. */
+     * Compositor hangs seen in the wild, but is short enough to not miss
+     * getting native hang stacks. */
     options.permanent_hang_timeout = 2048; // milliseconds
 
     if (!mServiceThread->StartWithOptions(options)) {
       delete mServiceThread;
       mServiceThread = nullptr;
       return;
     }
 
-    mServiceThread->message_loop()->PostTask(NewRunnableMethod(
-      "gfx::VRService::ServiceInitialize",
-      this, &VRService::ServiceInitialize
-    ));
+    mServiceThread->message_loop()->PostTask(
+      NewRunnableMethod("gfx::VRService::ServiceInitialize",
+                        this,
+                        &VRService::ServiceInitialize));
   }
 }
 
 void
 VRService::Stop()
 {
   if (mServiceThread) {
     mShutdownRequested = true;
@@ -139,69 +157,70 @@ VRService::Stop()
     mServiceThread = nullptr;
   }
   if (mTargetShmemFile) {
 #if defined(XP_WIN)
     CloseHandle(mTargetShmemFile);
 #endif
     mTargetShmemFile = 0;
   }
-  if (gfxPrefs::VRProcessEnabled() && mAPIShmem) {
+  if (mVRProcessEnabled && mAPIShmem) {
 #if defined(XP_WIN)
-    UnmapViewOfFile((void *)mAPIShmem);
+    UnmapViewOfFile((void*)mAPIShmem);
 #endif
     mAPIShmem = nullptr;
   }
   mSession = nullptr;
 }
 
 bool
 VRService::InitShmem()
 {
-  if (!gfxPrefs::VRProcessEnabled()) {
+  if (!mVRProcessEnabled) {
     return true;
   }
 
 #if defined(XP_WIN)
   const char* kShmemName = "moz.gecko.vr_ext.0.0.1";
   base::ProcessHandle targetHandle = 0;
 
   // Opening a file-mapping object by name
-  targetHandle = OpenFileMappingA(
-                  FILE_MAP_ALL_ACCESS,   // read/write access
-                  FALSE,                 // do not inherit the name
-                  kShmemName);           // name of mapping object
+  targetHandle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, // read/write access
+                                  FALSE,       // do not inherit the name
+                                  kShmemName); // name of mapping object
 
   MOZ_ASSERT(GetLastError() == 0);
 
   LARGE_INTEGER length;
   length.QuadPart = sizeof(VRExternalShmem);
-  mAPIShmem = (VRExternalShmem *)MapViewOfFile(reinterpret_cast<base::ProcessHandle>(targetHandle), // handle to map object
-                                               FILE_MAP_ALL_ACCESS,  // read/write permission
-                                               0,
-                                               0,
-                                               length.QuadPart);
+  mAPIShmem = (VRExternalShmem*)MapViewOfFile(
+    reinterpret_cast<base::ProcessHandle>(targetHandle), // handle to map object
+    FILE_MAP_ALL_ACCESS, // read/write permission
+    0,
+    0,
+    length.QuadPart);
   MOZ_ASSERT(GetLastError() == 0);
   // TODO - Implement logging
   mTargetShmemFile = targetHandle;
   if (!mAPIShmem) {
     MOZ_ASSERT(mAPIShmem);
     return false;
   }
 #else
-  // TODO: Implement shmem for other platforms.
+    // TODO: Implement shmem for other platforms.
 #endif
 
- return true;
+  return true;
 }
 
 bool
 VRService::IsInServiceThread()
 {
-  return mServiceThread && mServiceThread->thread_id() == PlatformThread::CurrentId();
+  return (mServiceThread != nullptr) &&
+         mServiceThread->thread_id() == PlatformThread::CurrentId();
 }
 
 void
 VRService::ServiceInitialize()
 {
   MOZ_ASSERT(IsInServiceThread());
 
   if (!InitShmem()) {
@@ -209,17 +228,29 @@ VRService::ServiceInitialize()
   }
 
   mShutdownRequested = false;
   memset(&mBrowserState, 0, sizeof(mBrowserState));
 
   // Try to start a VRSession
   UniquePtr<VRSession> session;
 
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  // We try Oculus first to ensure we use Oculus
+  // devices trough the most native interface
+  // when possible.
+#if defined(XP_WIN)
+  // Try Oculus
+  session = MakeUnique<OculusSession>();
+  if (!session->Initialize(mSystemState)) {
+    session = nullptr;
+  }
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX) ||                                   \
+  (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   // Try OpenVR
   if (!session) {
     session = MakeUnique<OpenVRSession>();
     if (!session->Initialize(mSystemState)) {
       session = nullptr;
     }
   }
 #endif
@@ -236,79 +267,84 @@ VRService::ServiceInitialize()
   if (session) {
     mSession = std::move(session);
     // Setting enumerationCompleted to true indicates to the browser
     // that it should resolve any promises in the WebVR/WebXR API
     // waiting for hardware detection.
     mSystemState.enumerationCompleted = true;
     PushState(mSystemState);
 
-    MessageLoop::current()->PostTask(NewRunnableMethod(
-      "gfx::VRService::ServiceWaitForImmersive",
-      this, &VRService::ServiceWaitForImmersive
-    ));
+    MessageLoop::current()->PostTask(
+      NewRunnableMethod("gfx::VRService::ServiceWaitForImmersive",
+                        this,
+                        &VRService::ServiceWaitForImmersive));
   } else {
     // VR hardware was not detected.
     // We must inform the browser of the failure so it may try again
     // later and resolve WebVR promises.  A failure or shutdown is
     // indicated by enumerationCompleted being set to true, with all
     // other fields remaining zeroed out.
     memset(&mSystemState, 0, sizeof(mSystemState));
     mSystemState.enumerationCompleted = true;
+    mSystemState.displayState.mMinRestartInterval =
+      gfxPrefs::VRExternalNotDetectedTimeout();
+    mSystemState.displayState.shutdown = true;
     PushState(mSystemState);
   }
 }
 
 void
 VRService::ServiceShutdown()
 {
   MOZ_ASSERT(IsInServiceThread());
 
-  mSession = nullptr;
-
   // Notify the browser that we have shut down.
   // This is indicated by enumerationCompleted being set
   // to true, with all other fields remaining zeroed out.
   memset(&mSystemState, 0, sizeof(mSystemState));
   mSystemState.enumerationCompleted = true;
+  mSystemState.displayState.shutdown = true;
+  if (mSession && mSession->ShouldQuit()) {
+    mSystemState.displayState.mMinRestartInterval =
+      gfxPrefs::VRExternalQuitTimeout();
+  }
   PushState(mSystemState);
+  mSession = nullptr;
 }
 
 void
 VRService::ServiceWaitForImmersive()
 {
   MOZ_ASSERT(IsInServiceThread());
   MOZ_ASSERT(mSession);
 
   mSession->ProcessEvents(mSystemState);
   PushState(mSystemState);
   PullState(mBrowserState);
 
   if (mSession->ShouldQuit() || mShutdownRequested) {
     // Shut down
     MessageLoop::current()->PostTask(NewRunnableMethod(
-      "gfx::VRService::ServiceShutdown",
-      this, &VRService::ServiceShutdown
-    ));
+      "gfx::VRService::ServiceShutdown", this, &VRService::ServiceShutdown));
   } else if (IsImmersiveContentActive(mBrowserState)) {
     // Enter Immersive Mode
     mSession->StartPresentation();
     mSession->StartFrame(mSystemState);
     PushState(mSystemState);
 
-    MessageLoop::current()->PostTask(NewRunnableMethod(
-      "gfx::VRService::ServiceImmersiveMode",
-      this, &VRService::ServiceImmersiveMode
-    ));
+    MessageLoop::current()->PostTask(
+      NewRunnableMethod("gfx::VRService::ServiceImmersiveMode",
+                        this,
+                        &VRService::ServiceImmersiveMode));
   } else {
     // Continue waiting for immersive mode
-    MessageLoop::current()->PostTask(NewRunnableMethod(
-      "gfx::VRService::ServiceWaitForImmersive",
-      this, &VRService::ServiceWaitForImmersive
-    ));
+    MessageLoop::current()->PostTask(
+      NewRunnableMethod("gfx::VRService::ServiceWaitForImmersive",
+                        this,
+                        &VRService::ServiceWaitForImmersive));
   }
 }
 
 void
 VRService::ServiceImmersiveMode()
 {
   MOZ_ASSERT(IsInServiceThread());
   MOZ_ASSERT(mSession);
@@ -316,37 +352,35 @@ VRService::ServiceImmersiveMode()
   mSession->ProcessEvents(mSystemState);
   UpdateHaptics();
   PushState(mSystemState);
   PullState(mBrowserState);
 
   if (mSession->ShouldQuit() || mShutdownRequested) {
     // Shut down
     MessageLoop::current()->PostTask(NewRunnableMethod(
-      "gfx::VRService::ServiceShutdown",
-      this, &VRService::ServiceShutdown
-    ));
+      "gfx::VRService::ServiceShutdown", this, &VRService::ServiceShutdown));
     return;
   } else if (!IsImmersiveContentActive(mBrowserState)) {
     // Exit immersive mode
     mSession->StopAllHaptics();
     mSession->StopPresentation();
-    MessageLoop::current()->PostTask(NewRunnableMethod(
-      "gfx::VRService::ServiceWaitForImmersive",
-      this, &VRService::ServiceWaitForImmersive
-    ));
+    MessageLoop::current()->PostTask(
+      NewRunnableMethod("gfx::VRService::ServiceWaitForImmersive",
+                        this,
+                        &VRService::ServiceWaitForImmersive));
     return;
   }
 
   uint64_t newFrameId = FrameIDFromBrowserState(mBrowserState);
   if (newFrameId != mSystemState.displayState.mLastSubmittedFrameId) {
     // A new immersive frame has been received.
     // Submit the textures to the VR system compositor.
     bool success = false;
-    for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) {
+    for (int iLayer = 0; iLayer < kVRLayerMaxCount; iLayer++) {
       const VRLayerState& layer = mBrowserState.layerState[iLayer];
       if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
         // SubmitFrame may block in order to control the timing for
         // the next frame start
         success = mSession->SubmitFrame(layer.layer_stereo_immersive);
         break;
       }
     }
@@ -356,34 +390,35 @@ VRService::ServiceImmersiveMode()
     // used for rendering, such as headset pose, must be pushed
     // atomically to the browser.
     mSystemState.displayState.mLastSubmittedFrameId = newFrameId;
     mSystemState.displayState.mLastSubmittedFrameSuccessful = success;
 
     // StartFrame may block to control the timing for the next frame start
     mSession->StartFrame(mSystemState);
     mSystemState.sensorState.inputFrameID++;
-    size_t historyIndex = mSystemState.sensorState.inputFrameID % ArrayLength(mFrameStartTime);
+    size_t historyIndex =
+      mSystemState.sensorState.inputFrameID % ArrayLength(mFrameStartTime);
     mFrameStartTime[historyIndex] = TimeStamp::Now();
     PushState(mSystemState);
   }
 
   // Continue immersive mode
-  MessageLoop::current()->PostTask(NewRunnableMethod(
-    "gfx::VRService::ServiceImmersiveMode",
-    this, &VRService::ServiceImmersiveMode
-  ));
+  MessageLoop::current()->PostTask(
+    NewRunnableMethod("gfx::VRService::ServiceImmersiveMode",
+                      this,
+                      &VRService::ServiceImmersiveMode));
 }
 
 void
 VRService::UpdateHaptics()
 {
   MOZ_ASSERT(IsInServiceThread());
   MOZ_ASSERT(mSession);
-  
+
   for (size_t i = 0; i < ArrayLength(mBrowserState.hapticState); i++) {
     VRHapticState& state = mBrowserState.hapticState[i];
     VRHapticState& lastState = mLastHapticState[i];
     // Note that VRHapticState is asserted to be a POD type, thus memcmp is safe
     if (memcmp(&state, &lastState, sizeof(VRHapticState)) == 0) {
       // No change since the last update
       continue;
     }
@@ -395,23 +430,25 @@ VRService::UpdateHaptics()
       if (now.IsNull()) {
         // TimeStamp::Now() is expensive, so we
         // must call it only when needed and save the
         // output for further loop iterations.
         now = TimeStamp::Now();
       }
       // This is a new haptic pulse, or we are overriding a prior one
       size_t historyIndex = state.inputFrameID % ArrayLength(mFrameStartTime);
-      float startOffset = (float)(now - mFrameStartTime[historyIndex]).ToSeconds();
+      float startOffset =
+        (float)(now - mFrameStartTime[historyIndex]).ToSeconds();
 
       // state.pulseStart is guaranteed never to be in the future
       mSession->VibrateHaptic(state.controllerIndex,
                               state.hapticIndex,
                               state.pulseIntensity,
-                              state.pulseDuration + state.pulseStart - startOffset);
+                              state.pulseDuration + state.pulseStart -
+                                startOffset);
     }
     // Record the state for comparison in the next run
     memcpy(&lastState, &state, sizeof(VRHapticState));
   }
 }
 
 void
 VRService::PushState(const mozilla::gfx::VRSystemState& aState)
@@ -420,23 +457,24 @@ VRService::PushState(const mozilla::gfx:
     return;
   }
   // Copying the VR service state to the shmem is atomic, infallable,
   // and non-blocking on x86/x64 architectures.  Arm requires a mutex
   // that is locked for the duration of the memcpy to and from shmem on
   // both sides.
 
 #if defined(MOZ_WIDGET_ANDROID)
-    if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
-      memcpy((void *)&mAPIShmem->state, &aState, sizeof(VRSystemState));
-      pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
-    }
+  if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
+      0) {
+    memcpy((void*)&mAPIShmem->state, &aState, sizeof(VRSystemState));
+    pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+  }
 #else
   mAPIShmem->generationA++;
-  memcpy((void *)&mAPIShmem->state, &aState, sizeof(VRSystemState));
+  memcpy((void*)&mAPIShmem->state, &aState, sizeof(VRSystemState));
   mAPIShmem->generationB++;
 #endif
 }
 
 void
 VRService::PullState(mozilla::gfx::VRBrowserState& aState)
 {
   if (!mAPIShmem) {
@@ -447,25 +485,27 @@ VRService::PullState(mozilla::gfx::VRBro
   // locked for the duration of the memcpy to and from shmem on
   // both sides.
   // On x86/x64 It is fallable -- If a dirty copy is detected by
   // a mismatch of browserGenerationA and browserGenerationB,
   // the copy is discarded and will not replace the last known
   // browser state.
 
 #if defined(MOZ_WIDGET_ANDROID)
-    if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
-      memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
-      pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
-    }
+  if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) ==
+      0) {
+    memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
+    pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
+  }
 #else
   VRExternalShmem tmp;
   if (mAPIShmem->browserGenerationA != mBrowserGeneration) {
     memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem));
-    if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) {
+    if (tmp.browserGenerationA == tmp.browserGenerationB &&
+        tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) {
       memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
       mBrowserGeneration = tmp.browserGenerationA;
     }
   }
 #endif
 }
 
 VRExternalShmem*
--- a/gfx/vr/service/VRService.h
+++ b/gfx/vr/service/VRService.h
@@ -23,16 +23,17 @@ class VRSession;
 static const int kVRFrameTimingHistoryDepth = 100;
 
 class VRService
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRService)
   static already_AddRefed<VRService> Create();
 
+  void Refresh();
   void Start();
   void Stop();
   VRExternalShmem* GetAPIShmem();
 
 private:
   VRService();
   ~VRService();
   
@@ -59,16 +60,21 @@ private:
   UniquePtr<VRSession> mSession;
   base::Thread* mServiceThread;
   bool mShutdownRequested;
 
   VRExternalShmem* MOZ_OWNING_REF mAPIShmem;
   base::ProcessHandle mTargetShmemFile;
   VRHapticState mLastHapticState[kVRHapticsMaxCount];
   TimeStamp mFrameStartTime[kVRFrameTimingHistoryDepth];
+  // We store the value of gfxPrefs::VRProcessEnabled() in mVRProcessEnabled.
+  // This allows us to read the value in the VRService destructor, after
+  // gfxPrefs has been shut down.  We should investigate why gfxPrefs
+  // is shutting down earlier - See bug xxx
+  bool mVRProcessEnabled;
 
   bool IsInServiceThread();
   void UpdateHaptics();
 
   /**
    * The VR Service thread is a state machine that always has one
    * task queued depending on the state.
    *
--- a/gfx/vr/service/VRSession.cpp
+++ b/gfx/vr/service/VRSession.cpp
@@ -4,43 +4,44 @@
 
 #if defined(XP_WIN)
 #include <d3d11.h>
 #endif // defined(XP_WIN)
 
 using namespace mozilla::gfx;
 
 VRSession::VRSession()
+  : mShouldQuit(false)
 {
 
 }
 
 VRSession::~VRSession()
 {
 
 }
 
 #if defined(XP_WIN)
 bool
 VRSession::CreateD3DContext(RefPtr<ID3D11Device> aDevice)
 {
   if (!mDevice) {
     if (!aDevice) {
-      NS_WARNING("OpenVRSession::CreateD3DObjects failed to get a D3D11Device");
+      NS_WARNING("VRSession::CreateD3DObjects failed to get a D3D11Device");
       return false;
     }
     if (FAILED(aDevice->QueryInterface(__uuidof(ID3D11Device1), getter_AddRefs(mDevice)))) {
-      NS_WARNING("OpenVRSession::CreateD3DObjects failed to get a D3D11Device1");
+      NS_WARNING("VRSession::CreateD3DObjects failed to get a D3D11Device1");
       return false;
     }
   }
   if (!mContext) {
     mDevice->GetImmediateContext1(getter_AddRefs(mContext));
     if (!mContext) {
-      NS_WARNING("OpenVRSession::CreateD3DObjects failed to get an immediate context");
+      NS_WARNING("VRSession::CreateD3DObjects failed to get an immediate context");
       return false;
     }
   }
   if (!mDeviceContextState) {
     D3D_FEATURE_LEVEL featureLevels[] {
       D3D_FEATURE_LEVEL_11_1,
       D3D_FEATURE_LEVEL_11_0
     };
@@ -48,17 +49,17 @@ VRSession::CreateD3DContext(RefPtr<ID3D1
                                       featureLevels,
                                       2,
                                       D3D11_SDK_VERSION,
                                       __uuidof(ID3D11Device1),
                                       nullptr,
                                       getter_AddRefs(mDeviceContextState));
   }
   if (!mDeviceContextState) {
-    NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11DeviceContextState");
+    NS_WARNING("VRSession::CreateD3DObjects failed to get a D3D11DeviceContextState");
     return false;
   }
   return true;
 }
 
 ID3D11Device1*
 VRSession::GetD3DDevice()
 {
@@ -73,8 +74,87 @@ VRSession::GetD3DDeviceContext()
 
 ID3DDeviceContextState*
 VRSession::GetD3DDeviceContextState()
 {
   return mDeviceContextState;
 }
 
 #endif // defined(XP_WIN)
+
+bool
+VRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer)
+{
+#if defined(XP_WIN)
+
+  if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor) {
+      RefPtr<ID3D11Texture2D> dxTexture;
+      HRESULT hr = mDevice->OpenSharedResource((HANDLE)aLayer.mTextureHandle,
+        __uuidof(ID3D11Texture2D),
+        (void**)(ID3D11Texture2D**)getter_AddRefs(dxTexture));
+      if (FAILED(hr) || !dxTexture) {
+        NS_WARNING("Failed to open shared texture");
+        return false;
+      }
+
+      // Similar to LockD3DTexture in TextureD3D11.cpp
+      RefPtr<IDXGIKeyedMutex> mutex;
+      dxTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+      if (mutex) {
+        HRESULT hr = mutex->AcquireSync(0, 1000);
+        if (hr == WAIT_TIMEOUT) {
+          gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
+        }
+        else if (hr == WAIT_ABANDONED) {
+          gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
+        }
+        if (FAILED(hr)) {
+          NS_WARNING("Failed to lock the texture");
+          return false;
+        }
+      }
+      bool success = SubmitFrame(aLayer, dxTexture);
+      if (mutex) {
+        HRESULT hr = mutex->ReleaseSync(0);
+        if (FAILED(hr)) {
+          NS_WARNING("Failed to unlock the texture");
+        }
+      }
+      if (!success) {
+        return false;
+      }
+      return true;
+  }
+
+#elif defined(XP_MACOSX)
+
+  if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_MacIOSurface) {
+    return SubmitFrame(aLayer, (MacIOSurface*)aLayer.mTextureHandle);
+  }
+
+#endif
+
+  return false;
+}
+
+void
+VRSession::UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex, float aValue, float aThreshold)
+{
+  // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
+  // We prefer to let developers to set their own threshold for the adjustment.
+  // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
+  // we just check the button value is larger than the threshold value or not.
+  uint64_t mask = (1ULL << aButtonIndex);
+  aState.triggerValue[aButtonIndex] = aValue;
+  if (aValue > aThreshold) {
+    aState.buttonPressed |= mask;
+    aState.buttonTouched |= mask;
+  } else {
+    aState.buttonPressed &= ~mask;
+    aState.buttonTouched &= ~mask;
+  }
+}
+
+bool
+VRSession::ShouldQuit() const
+{
+  return mShouldQuit;
+}
--- a/gfx/vr/service/VRSession.h
+++ b/gfx/vr/service/VRSession.h
@@ -25,33 +25,40 @@ class VRSession
 public:
   VRSession();
   virtual ~VRSession();
 
   virtual bool Initialize(mozilla::gfx::VRSystemState& aSystemState) = 0;
   virtual void Shutdown() = 0;
   virtual void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) = 0;
   virtual void StartFrame(mozilla::gfx::VRSystemState& aSystemState) = 0;
-  virtual bool ShouldQuit() const = 0;
   virtual bool StartPresentation() = 0;
   virtual void StopPresentation() = 0;
-  virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) = 0;
   virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                              float aIntensity, float aDuration) = 0;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) = 0;
   virtual void StopAllHaptics() = 0;
+  bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer);
+  bool ShouldQuit() const;
 
+protected:
+  bool mShouldQuit;
 #if defined(XP_WIN)
-protected:
+  virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                           ID3D11Texture2D* aTexture) = 0;
   bool CreateD3DContext(RefPtr<ID3D11Device> aDevice);
   RefPtr<ID3D11Device1> mDevice;
   RefPtr<ID3D11DeviceContext1> mContext;
   ID3D11Device1* GetD3DDevice();
   ID3D11DeviceContext1* GetD3DDeviceContext();
   ID3DDeviceContextState* GetD3DDeviceContextState();
   RefPtr<ID3DDeviceContextState> mDeviceContextState;
+#elif defined(XP_MACOSX)
+  virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
+                           MacIOSurface* aTexture) = 0;
 #endif
+  void UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex, float aValue, float aThreshold);
 };
 
 } // namespace mozilla
 } // namespace gfx
 
 #endif // GFX_VR_SERVICE_VRSESSION_H
--- a/gfx/vr/service/moz.build
+++ b/gfx/vr/service/moz.build
@@ -1,14 +1,20 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# Build Oculus support on Windows only
+if CONFIG['OS_TARGET'] == 'WINNT':
+    SOURCES += [
+        'OculusSession.cpp',
+    ]
+
 # Build OSVR on all platforms except Android
 if CONFIG['OS_TARGET'] != 'Android':
     UNIFIED_SOURCES += [
         'OSVRSession.cpp',
         'VRService.cpp',
         'VRSession.cpp',
     ]
     include('/ipc/chromium/chromium-config.mozbuild')
@@ -20,8 +26,14 @@ if CONFIG['OS_TARGET'] in ('WINNT', 'Lin
     ]
     LOCAL_INCLUDES += [
         '/dom/base',
         '/gfx/layers/d3d11',
         '/gfx/thebes',
     ]
 
 FINAL_LIBRARY = 'xul'
+
+# This is intended as a temporary hack to enable VS2015 builds.
+if CONFIG['CC_TYPE'] in ('msvc', 'clang-cl'):
+    # ovr_capi_dynamic.h '<unnamed-tag>': Alignment specifier is less than
+    # actual alignment (8), and will be ignored
+    CXXFLAGS += ['-wd4359']
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5299,16 +5299,25 @@ pref("dom.vr.enabled", false);
 // when the user is using it outside the browser or inadvertent start of
 // presentation due to the high sensitivity of the proximity sensor in some
 // headsets, so it is off by default.
 pref("dom.vr.autoactivate.enabled", false);
 // The threshold value of trigger inputs for VR controllers
 pref("dom.vr.controller_trigger_threshold", "0.1");
 // Enable external XR API integrations
 pref("dom.vr.external.enabled", false);
+// Minimum number of milliseconds the browser will wait before attempting
+// to re-start the VR service after an enumeration returned no devices.
+pref("dom.vr.external.notdetected.timeout", 60000);
+// Minimum number of milliseconds the browser will wait before attempting
+// to re-start the VR service after a VR API (eg, OpenVR or Oculus)
+// requests that we shutdown and unload its libraries.
+// To ensure that we don't interfere with VR runtime software auto-updates,
+// we will not attempt to re-load the service until this timeout has elapsed.
+pref("dom.vr.external.quit.timeout", 10000);
 // Maximum number of milliseconds the browser will wait for content to call
 // VRDisplay.requestPresent after emitting vrdisplayactivate during VR
 // link traversal.  This prevents a long running event handler for
 // vrdisplayactivate from later calling VRDisplay.requestPresent, which would
 // result in a non-responsive browser in the VR headset.
 pref("dom.vr.navigation.timeout", 5000);
 // Oculus device
 #if defined(HAVE_64BIT_BUILD)