Bug 1287944 - Improve interaction with Oculus Home draft
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Tue, 04 Jul 2017 13:28:27 -0700
changeset 607021 5bef22f10ae1231d73655d79e5db151020334370
parent 606958 6fec4855b5345eb63fef57089e61829b88f5f4eb
child 636917 6ddecb6e14418956d4fe498465c4bd90e6486f27
push id67865
push userkgilbert@mozilla.com
push dateTue, 11 Jul 2017 18:57:39 +0000
bugs1287944
milestone56.0a1
Bug 1287944 - Improve interaction with Oculus Home - Now destroying and re-creating Oculus sessions when switching between magic window and immersive WebVR (BeginPresent / ExitPresent) - Now sending flags to Oculus ovr_initilize to specify if Firefox will be presenting to the VR display or just using tracking - Now coordinating oculus session shutdown and restart between the VR controllers and the VR display with reference counting. - Now able to return to Oculus home after using WebVR - Magic window / non-exclusive sessions no longer take over the VR headset causing it to display a message that Firefox.exe is not responding. MozReview-Commit-ID: EnRsxt6ZSzg
gfx/thebes/gfxPrefs.h
gfx/vr/VRManager.cpp
gfx/vr/gfxVR.h
gfx/vr/gfxVROSVR.cpp
gfx/vr/gfxVROSVR.h
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROculus.h
gfx/vr/gfxVROpenVR.cpp
gfx/vr/gfxVROpenVR.h
gfx/vr/gfxVRPuppet.cpp
gfx/vr/gfxVRPuppet.h
gfx/vr/ipc/VRLayerParent.cpp
gfx/vr/ipc/VRManagerParent.cpp
modules/libpref/init/all.js
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -352,16 +352,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(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(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.present.timeout",         VROculusPresentTimeout, int32_t, 10000);
+  DECL_GFX_PREF(Live, "dom.vr.oculus.quit.timeout",            VROculusQuitTimeout, int32_t, 30000);
   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.poseprediction.enabled",         VRPosePredictionEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.require-gesture",                VRRequireGesture, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.puppet.enabled",                 VRPuppetEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.puppet.submitframe",             VRPuppetSubmitFrame, uint32_t, 0);
   DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled",        PointerEventsEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.w3c_touch_events.enabled",          TouchEventsEnabled, int32_t, 0);
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -259,48 +259,55 @@ VRManager::RefreshVRDisplays(bool aMustD
    * so stop as soon as we get a display.
    * It is still possible to get multiple displays from a single manager,
    * but do not wish to mix-and-match for risk of reporting a duplicate.
    *
    * XXX - Perhaps there will be a better way to detect duplicate displays
    *       in the future.
    */
   for (uint32_t i = 0; i < mManagers.Length() && displays.Length() == 0; ++i) {
-    mManagers[i]->GetHMDs(displays);
+    if (mManagers[i]->GetHMDs(displays)) {
+      // GetHMDs returns true to indicate that no further enumeration from
+      // other managers should be performed.  This prevents erraneous
+      // redundant enumeration of the same HMD by multiple managers.
+      break;
+    }
   }
 
   bool displayInfoChanged = false;
+  bool displaySetChanged = false;
 
   if (displays.Length() != mVRDisplays.Count()) {
     // Catch cases where a VR display has been removed
-    displayInfoChanged = true;
+    displaySetChanged = true;
   }
 
   for (const auto& display: displays) {
     if (!GetDisplay(display->GetDisplayInfo().GetDisplayID())) {
       // This is a new display
-      displayInfoChanged = true;
+      displaySetChanged = true;
       break;
     }
 
     if (display->CheckClearDisplayInfoDirty()) {
       // This display's info has changed
       displayInfoChanged = true;
       break;
     }
   }
 
-  if (displayInfoChanged) {
+  // Rebuild the HashMap if there are additions or removals
+  if (displaySetChanged) {
     mVRDisplays.Clear();
     for (const auto& display: displays) {
       mVRDisplays.Put(display->GetDisplayInfo().GetDisplayID(), display);
     }
   }
 
-  if (displayInfoChanged || aMustDispatch) {
+  if (displayInfoChanged || displaySetChanged || aMustDispatch) {
     DispatchVRDisplayInfoUpdate();
   }
 }
 
 void
 VRManager::DispatchVRDisplayInfoUpdate()
 {
   nsTArray<VRDisplayInfo> update;
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -315,17 +315,17 @@ public:
 protected:
   static Atomic<uint32_t> sDisplayBase;
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRSystemManager)
 
   virtual void Destroy() = 0;
   virtual void Shutdown() = 0;
-  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) = 0;
+  virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) = 0;
   virtual bool GetIsPresenting() = 0;
   virtual void HandleInput() = 0;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0;
   virtual void ScanForControllers() = 0;
   virtual void RemoveControllers() = 0;
   virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                              double aIntensity, double aDuration, uint32_t aPromiseID) = 0;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) = 0;
--- a/gfx/vr/gfxVROSVR.cpp
+++ b/gfx/vr/gfxVROSVR.cpp
@@ -509,31 +509,33 @@ VRSystemManagerOSVR::Shutdown()
   if (m_ctx) {
     osvr_ClientFreeDisplay(m_display);
   }
   // osvr checks that m_ctx or m_iface are not null
   osvr_ClientFreeInterface(m_ctx, m_iface);
   osvr_ClientShutdown(m_ctx);
 }
 
-void
+bool
 VRSystemManagerOSVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
 {
   // make sure context, interface and display are initialized
   CheckOSVRStatus();
 
   if (!Init()) {
-    return;
+    return false;
   }
 
   mHMDInfo = new VRDisplayOSVR(&m_ctx, &m_iface, &m_display);
 
   if (mHMDInfo) {
     aHMDResult.AppendElement(mHMDInfo);
+    return true;
   }
+  return false;
 }
 
 bool
 VRSystemManagerOSVR::GetIsPresenting()
 {
   if (mHMDInfo) {
     VRDisplayInfo displayInfo(mHMDInfo->GetDisplayInfo());
     return displayInfo.GetPresentingGroups() != kVRGroupNone;
--- a/gfx/vr/gfxVROSVR.h
+++ b/gfx/vr/gfxVROSVR.h
@@ -60,17 +60,17 @@ protected:
 } // namespace impl
 
 class VRSystemManagerOSVR : public VRSystemManager
 {
 public:
   static already_AddRefed<VRSystemManagerOSVR> Create();
   virtual void Destroy() override;
   virtual void Shutdown() override;
-  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
+  virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
   virtual bool GetIsPresenting() override;
   virtual void HandleInput() override;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
                               aControllerResult) override;
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                              double aIntensity, double aDuration, uint32_t aPromiseID) override;
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -186,69 +186,390 @@ FromFovPort(const ovrFovPort& aFOV)
   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;
 }
 
 } // namespace
 
+VROculusSession::VROculusSession()
+  : mOvrLib(nullptr)
+  , mSession(nullptr)
+  , mInitFlags((ovrInitFlags)0)
+  , mTextureSet(nullptr)
+  , mPresenting(false)
+{
+}
+
+ovrSession
+VROculusSession::Get()
+{
+  MOZ_ASSERT(mSession);
+  return mSession;
+}
+
 bool
-VRSystemManagerOculus::LoadOvrLib()
+VROculusSession::IsTrackingReady() const
+{
+  return mSession != nullptr;
+}
+
+bool
+VROculusSession::IsRenderReady() const
+{
+  return !mRenderTargets.IsEmpty();
+}
+
+void
+VROculusSession::StopTracking()
+{
+  Uninitialize(true);
+}
+
+void
+VROculusSession::StartPresentation(const IntSize& aSize)
+{
+  if (!mPresenting) {
+    mPresenting = true;
+    mPresentationSize = aSize;
+    Refresh();
+  }
+}
+
+void
+VROculusSession::StopPresentation()
+{
+  if (mPresenting) {
+    mLastPresentationEnd = TimeStamp::Now();
+    mPresenting = false;
+    Refresh();
+  }
+}
+
+VROculusSession::~VROculusSession()
+{
+  Uninitialize(true);
+}
+
+void
+VROculusSession::Uninitialize(bool aUnloadLib)
+{
+  StopRendering();
+  StopSession();
+  StopLib();
+  if (aUnloadLib) {
+    UnloadOvrLib();
+  }
+}
+
+void
+VROculusSession::StopRendering()
+{
+  if (!mRenderTargets.IsEmpty()) {
+    mRenderTargets.Clear();
+  }
+  if (mTextureSet && mSession) {
+    ovr_DestroyTextureSwapChain(mSession, mTextureSet);
+  }
+  mTextureSet = nullptr;
+  mDevice = nullptr;
+}
+
+void
+VROculusSession::StopSession()
+{
+  if (mSession) {
+    ovr_Destroy(mSession);
+    mSession = nullptr;
+  }
+}
+
+void
+VROculusSession::StopLib()
+{
+  if (mInitFlags) {
+    ovr_Shutdown();
+    mInitFlags = (ovrInitFlags)0;
+  }
+}
+
+void
+VROculusSession::Refresh()
+{
+  ovrInitFlags flags = (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering);
+  bool bInvisible = true;
+  if (mPresenting) {
+    bInvisible = false;
+  } else if (!mLastPresentationEnd.IsNull()) {
+    TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd;
+    TimeDuration timeout = TimeDuration::FromMilliseconds(gfxPrefs::VROculusPresentTimeout());
+    if (timeout > TimeDuration(0) && duration < timeout) {
+      // 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.
+      bInvisible = false;
+
+      // While we are waiting for either the timeout or a new presentation,
+      // fill the HMD with black / no layers.
+      if (mSession && mTextureSet) {
+        ovrLayerEyeFov layer;
+        memset(&layer, 0, sizeof(layer));
+        layer.Header.Type = ovrLayerType_Disabled;
+        ovrLayerHeader *layers = &layer.Header;
+        ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
+      }
+    }
+  }
+  if (bInvisible) {
+    flags = (ovrInitFlags)(flags | ovrInit_Invisible);
+  }
+
+  if (mInitFlags != flags) {
+    Uninitialize(false);
+  }
+
+  Initialize(flags);
+
+  if (mSession) {
+    ovrSessionStatus status;
+    if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) {
+      if (status.ShouldQuit) {
+        mLastShouldQuit = TimeStamp::Now();
+        Uninitialize(true);
+      }
+    }
+  }
+}
+
+bool
+VROculusSession::IsQuitTimeoutActive()
 {
-  if (!mOvrLib) {
-    nsTArray<nsCString> libSearchPaths;
-    nsCString libName;
-    nsCString searchPath;
+  // If Oculus asked us to quit our session, do not try to initialize again
+  // immediately.
+  if (!mLastShouldQuit.IsNull()) {
+    TimeDuration duration = TimeStamp::Now() - mLastShouldQuit;
+    TimeDuration timeout = TimeDuration::FromMilliseconds(gfxPrefs::VROculusQuitTimeout());
+    if (timeout > TimeDuration(0) && duration < timeout) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
+VROculusSession::Initialize(ovrInitFlags aFlags)
+{
+  if (IsQuitTimeoutActive()) {
+    return false;
+  }
+
+  if (!LoadOvrLib()) {
+    return false;
+  }
+  if (!StartLib(aFlags)) {
+    return false;
+  }
+  if (!StartSession()) {
+    return false;
+  }
+  if (!StartRendering()) {
+    return false;
+  }
+  return true;
+}
+
+bool
+VROculusSession::StartRendering()
+{
+  if (!mPresenting) {
+    // Nothing to do if we aren't presenting
+    return true;
+  }
+  if (!mDevice) {
+    mDevice = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+    if (!mDevice) {
+      NS_WARNING("Failed to get a D3D11Device for Oculus");
+      return false;
+    }
+  }
+
+  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;
+    }
+  }
+
+  if (mTextureSet && mRenderTargets.IsEmpty()) {
+    int textureCount = 0;
+    ovrResult orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
+    if (orv != ovrSuccess) {
+      NS_WARNING("ovr_GetTextureSwapChainLength failed");
+      return false;
+    }
+    mRenderTargets.SetLength(textureCount);
+    for (int i = 0; i < textureCount; ++i) {
+      RefPtr<CompositingRenderTargetD3D11> rt;
+      ID3D11Texture2D* texture = nullptr;
+      orv = ovr_GetTextureSwapChainBufferDX(mSession, mTextureSet, i, IID_PPV_ARGS(&texture));
+      MOZ_ASSERT(orv == ovrSuccess, "ovr_GetTextureSwapChainBufferDX failed.");
+      rt = new CompositingRenderTargetD3D11(texture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM);
+      rt->SetSize(mPresentationSize);
+      mRenderTargets[i] = rt;
+      texture->Release();
+    }
+  }
+  return true;
+}
+
+bool
+VROculusSession::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;
+}
+
+bool
+VROculusSession::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;
+}
+
+bool
+VROculusSession::LoadOvrLib()
+{
+  if (mOvrLib) {
+    // Already loaded, early exit
+    return true;
+  }
+  nsTArray<nsCString> libSearchPaths;
+  nsCString libName;
+  nsCString searchPath;
 
 #if defined(_WIN32)
-    static const char dirSep = '\\';
-    static const int pathLen = 260;
-    searchPath.SetCapacity(pathLen);
-    int realLen = ::GetSystemDirectoryA(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);
+  static const char dirSep = '\\';
+  static const int pathLen = 260;
+  searchPath.SetCapacity(pathLen);
+  int realLen = ::GetSystemDirectoryA(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);
 #else
 #error "Unsupported platform!"
 #endif
 
-    // search the path/module dir
-    libSearchPaths.InsertElementsAt(0, 1, nsCString());
+  // search the path/module dir
+  libSearchPaths.InsertElementsAt(0, 1, nsCString());
+
+  // If the env var is present, we override libName
+  if (PR_GetEnv("OVR_LIB_PATH")) {
+    searchPath = PR_GetEnv("OVR_LIB_PATH");
+    libSearchPaths.InsertElementsAt(0, 1, searchPath);
+  }
 
-    // If the env var is present, we override libName
-    if (PR_GetEnv("OVR_LIB_PATH")) {
-      searchPath = PR_GetEnv("OVR_LIB_PATH");
-      libSearchPaths.InsertElementsAt(0, 1, searchPath);
-    }
+  if (PR_GetEnv("OVR_LIB_NAME")) {
+    libName = PR_GetEnv("OVR_LIB_NAME");
+  }
 
-    if (PR_GetEnv("OVR_LIB_NAME")) {
-      libName = PR_GetEnv("OVR_LIB_NAME");
+  for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
+    nsCString& libPath = libSearchPaths[i];
+    nsCString fullName;
+    if (libPath.Length() == 0) {
+      fullName.Assign(libName);
+    } else {
+      fullName.AppendPrintf("%s%c%s", libPath.BeginReading(), dirSep, libName.BeginReading());
     }
 
-    for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
-      nsCString& libPath = libSearchPaths[i];
-      nsCString fullName;
-      if (libPath.Length() == 0) {
-        fullName.Assign(libName);
-      } else {
-        fullName.AppendPrintf("%s%c%s", libPath.BeginReading(), dirSep, libName.BeginReading());
-      }
+    mOvrLib = PR_LoadLibrary(fullName.BeginReading());
+    if (mOvrLib) {
+      break;
+    }
+  }
 
-      mOvrLib = PR_LoadLibrary(fullName.BeginReading());
-      if (mOvrLib) {
-        break;
-      }
-    }
-
-    if (!mOvrLib) {
-      return false;
-    }
+  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);
@@ -331,46 +652,63 @@ VRSystemManagerOculus::LoadOvrLib()
 
  fail:
   ovr_Initialize = nullptr;
   PR_UnloadLibrary(mOvrLib);
   mOvrLib = nullptr;
   return false;
 }
 
+already_AddRefed<CompositingRenderTargetD3D11>
+VROculusSession::GetNextRenderTarget()
+{
+  int currentRenderTarget = 0;
+  DebugOnly<ovrResult> orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet, &currentRenderTarget);
+  MOZ_ASSERT(orv == ovrSuccess, "ovr_GetTextureSwapChainCurrentIndex failed.");
+
+  mRenderTargets[currentRenderTarget]->ClearOnBind();
+  RefPtr<CompositingRenderTargetD3D11> rt = mRenderTargets[currentRenderTarget];
+  return rt.forget();
+}
+
+ovrTextureSwapChain
+VROculusSession::GetSwapChain()
+{
+  MOZ_ASSERT(mTextureSet);
+  return mTextureSet;
+}
+
 void
-VRSystemManagerOculus::UnloadOvrLib()
+VROculusSession::UnloadOvrLib()
 {
   if (mOvrLib) {
     PR_UnloadLibrary(mOvrLib);
     mOvrLib = nullptr;
   }
 }
 
-VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
+VRDisplayOculus::VRDisplayOculus(VROculusSession* aSession)
   : VRDisplayHost(VRDeviceType::Oculus)
   , mSession(aSession)
-  , mTextureSet(nullptr)
   , mQuadVS(nullptr)
   , mQuadPS(nullptr)
   , mLinearSamplerState(nullptr)
   , mVSConstantBuffer(nullptr)
   , mPSConstantBuffer(nullptr)
   , mVertexBuffer(nullptr)
   , mInputLayout(nullptr)
-  , mIsPresenting(false)
   , mEyeHeight(OVR_DEFAULT_EYE_HEIGHT)
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
 
   mDisplayInfo.mDisplayName.AssignLiteral("Oculus VR HMD");
   mDisplayInfo.mIsConnected = true;
   mDisplayInfo.mIsMounted = false;
 
-  mDesc = ovr_GetHmdDesc(aSession);
+  mDesc = ovr_GetHmdDesc(aSession->Get());
 
   mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
   if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
     mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
     mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
   }
   if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
     mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
@@ -388,62 +726,62 @@ VRDisplayOculus::VRDisplayOculus(ovrSess
   mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Right] = FromFovPort(mFOVPort[VRDisplayInfo::Eye_Right]);
 
   float pixelsPerDisplayPixel = 1.0;
   ovrSizei texSize[2];
 
   // get eye parameters and create the mesh
   for (uint32_t eye = 0; eye < VRDisplayInfo::NumEyes; eye++) {
 
-    ovrEyeRenderDesc renderDesc = ovr_GetRenderDesc(mSession, (ovrEyeType)eye, mFOVPort[eye]);
+    ovrEyeRenderDesc renderDesc = ovr_GetRenderDesc(mSession->Get(), (ovrEyeType)eye, mFOVPort[eye]);
 
     // As of Oculus 0.6.0, the HmdToEyeOffset values are correct and don't need to be negated.
     mDisplayInfo.mEyeTranslation[eye] = Point3D(renderDesc.HmdToEyeOffset.x, renderDesc.HmdToEyeOffset.y, renderDesc.HmdToEyeOffset.z);
 
-    texSize[eye] = ovr_GetFovTextureSize(mSession, (ovrEyeType)eye, mFOVPort[eye], pixelsPerDisplayPixel);
+    texSize[eye] = ovr_GetFovTextureSize(mSession->Get(), (ovrEyeType)eye, mFOVPort[eye], pixelsPerDisplayPixel);
   }
 
   // take the max of both for eye resolution
   mDisplayInfo.mEyeResolution.width = std::max(texSize[VRDisplayInfo::Eye_Left].w, texSize[VRDisplayInfo::Eye_Right].w);
   mDisplayInfo.mEyeResolution.height = std::max(texSize[VRDisplayInfo::Eye_Left].h, texSize[VRDisplayInfo::Eye_Right].h);
 
   UpdateStageParameters();
 }
 
 VRDisplayOculus::~VRDisplayOculus() {
-  StopPresentation();
   Destroy();
   MOZ_COUNT_DTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
 }
 
 void
 VRDisplayOculus::Destroy()
 {
-  if (mSession) {
-    ovr_Destroy(mSession);
-    mSession = nullptr;
-  }
+  StopPresentation();
+  mSession = nullptr;
 }
 
 void
 VRDisplayOculus::UpdateStageParameters()
 {
+  if (!mSession->IsTrackingReady()) {
+    return;
+  }
   ovrVector3f playArea;
-  ovrResult res = ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
+  ovrResult res = ovr_GetBoundaryDimensions(mSession->Get(), ovrBoundary_PlayArea, &playArea);
   if (res == ovrSuccess) {
     mDisplayInfo.mStageSize.width = playArea.x;
     mDisplayInfo.mStageSize.height = playArea.z;
   } else {
     // If we fail, fall back to reasonable defaults.
     // 1m x 1m space
     mDisplayInfo.mStageSize.width = 1.0f;
     mDisplayInfo.mStageSize.height = 1.0f;
   }
 
-  mEyeHeight = ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+  mEyeHeight = ovr_GetFloat(mSession->Get(), OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
 
   mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
   mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
   mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
   mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
 
   mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
   mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
@@ -459,43 +797,48 @@ VRDisplayOculus::UpdateStageParameters()
   mDisplayInfo.mSittingToStandingTransform._42 = mEyeHeight;
   mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
   mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
 }
 
 void
 VRDisplayOculus::ZeroSensor()
 {
-  ovr_RecenterTrackingOrigin(mSession);
+  if (!mSession->IsTrackingReady()) {
+    return;
+  }
+  ovr_RecenterTrackingOrigin(mSession->Get());
   UpdateStageParameters();
 }
 
 VRHMDSensorState
 VRDisplayOculus::GetSensorState()
 {
   VRHMDSensorState result;
-  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);
+  if (mSession->IsTrackingReady()) {
+    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->Get(), 0);
+    }
+    result = GetSensorState(predictedFrameTime);
   }
-  result = GetSensorState(predictedFrameTime);
   result.inputFrameID = mDisplayInfo.mFrameId;
   result.position[1] -= mEyeHeight;
   mDisplayInfo.mLastSensorState[result.inputFrameID % kVRMaxLatencyFrames] = result;
   return result;
 }
 
 VRHMDSensorState
 VRDisplayOculus::GetSensorState(double absTime)
 {
   VRHMDSensorState result;
 
-  ovrTrackingState state = ovr_GetTrackingState(mSession, absTime, true);
+  ovrTrackingState state = ovr_GetTrackingState(mSession->Get(), absTime, true);
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
 
     result.orientation[0] = pose.ThePose.Orientation.x;
@@ -536,171 +879,119 @@ VRDisplayOculus::GetSensorState(double a
   result.flags |= VRDisplayCapabilityFlags::Cap_Present;
 
   return result;
 }
 
 void
 VRDisplayOculus::StartPresentation()
 {
-  if (mIsPresenting) {
+  mSession->StartPresentation(IntSize(mDisplayInfo.mEyeResolution.width * 2, mDisplayInfo.mEyeResolution.height));
+  if (!mSession->IsRenderReady()) {
     return;
   }
-  mIsPresenting = true;
-
-  /**
-   * 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 = mDisplayInfo.mEyeResolution.width * 2;
-  desc.Height = mDisplayInfo.mEyeResolution.height;
-  desc.MipLevels = 1;
-  desc.SampleCount = 1;
-  desc.StaticImage = false;
-  desc.MiscFlags = ovrTextureMisc_DX_Typeless;
-  desc.BindFlags = ovrTextureBind_DX_RenderTarget;
 
   if (!mDevice) {
     mDevice = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
     if (!mDevice) {
       NS_WARNING("Failed to get a D3D11Device for Oculus");
       return;
     }
   }
 
-  mDevice->GetImmediateContext(getter_AddRefs(mContext));
   if (!mContext) {
-    NS_WARNING("Failed to get immediate context for Oculus");
-    return;
+    mDevice->GetImmediateContext(getter_AddRefs(mContext));
+    if (!mContext) {
+      NS_WARNING("Failed to get immediate context for Oculus");
+      return;
+    }
   }
 
-  if (FAILED(mDevice->CreateVertexShader(sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
-    NS_WARNING("Failed to create vertex shader for Oculus");
-    return;
+  if (!mQuadVS) {
+    if (FAILED(mDevice->CreateVertexShader(sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
+      NS_WARNING("Failed to create vertex shader for Oculus");
+      return;
+    }
   }
 
-  if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData, sRGBShader.mLength, nullptr, &mQuadPS))) {
-    NS_WARNING("Failed to create pixel shader for Oculus");
-    return;
+  if (!mQuadPS) {
+    if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData, sRGBShader.mLength, nullptr, &mQuadPS))) {
+      NS_WARNING("Failed to create pixel shader for Oculus");
+      return;
+    }
   }
 
   CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants),
     D3D11_BIND_CONSTANT_BUFFER,
     D3D11_USAGE_DYNAMIC,
     D3D11_CPU_ACCESS_WRITE);
 
-  if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mVSConstantBuffer)))) {
-    NS_WARNING("Failed to vertex shader constant buffer for Oculus");
-    return;
+  if (!mVSConstantBuffer) {
+    if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mVSConstantBuffer)))) {
+      NS_WARNING("Failed to vertex shader constant buffer for Oculus");
+      return;
+    }
   }
 
-  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;
+  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;
+    }
   }
 
-  CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
-  if (FAILED(mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mLinearSamplerState)))) {
-    NS_WARNING("Failed to create sampler state for Oculus");
-    return;
+  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;
+    }
   }
 
-  D3D11_INPUT_ELEMENT_DESC layout[] =
-  {
-    { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-  };
+  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;
+    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;
+    }
   }
 
-  ovrResult orv = ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet);
-  if (orv != ovrSuccess) {
-    NS_WARNING("ovr_CreateTextureSwapChainDX failed");
-    return;
-  }
+  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;
 
-  int textureCount = 0;
-  orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
-  if (orv != ovrSuccess) {
-    NS_WARNING("ovr_GetTextureSwapChainLength failed");
-    return;
+    if (FAILED(mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mVertexBuffer)))) {
+      NS_WARNING("Failed to create vertex buffer for Oculus");
+      return;
+    }
   }
 
-  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;
-  }
-
-  mRenderTargets.SetLength(textureCount);
-
   memset(&mVSConstants, 0, sizeof(mVSConstants));
   memset(&mPSConstants, 0, sizeof(mPSConstants));
-
-  for (int i = 0; i < textureCount; ++i) {
-    RefPtr<CompositingRenderTargetD3D11> rt;
-    ID3D11Texture2D* texture = nullptr;
-    orv = ovr_GetTextureSwapChainBufferDX(mSession, mTextureSet, i, IID_PPV_ARGS(&texture));
-    MOZ_ASSERT(orv == ovrSuccess, "ovr_GetTextureSwapChainBufferDX failed.");
-    rt = new CompositingRenderTargetD3D11(texture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM);
-    rt->SetSize(IntSize(mDisplayInfo.mEyeResolution.width * 2, mDisplayInfo.mEyeResolution.height));
-    mRenderTargets[i] = rt;
-    texture->Release();
-  }
 }
 
 void
 VRDisplayOculus::StopPresentation()
 {
-  if (!mIsPresenting) {
-    return;
-  }
-  mIsPresenting = false;
-
-  if (mTextureSet) {
-    ovr_DestroyTextureSwapChain(mSession, mTextureSet);
-    mTextureSet = nullptr;
+  if (mSession) {
+    mSession->StopPresentation();
   }
 }
 
-already_AddRefed<CompositingRenderTargetD3D11>
-VRDisplayOculus::GetNextRenderTarget()
-{
-  int currentRenderTarget = 0;
-  DebugOnly<ovrResult> orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet, &currentRenderTarget);
-  MOZ_ASSERT(orv == ovrSuccess, "ovr_GetTextureSwapChainCurrentIndex failed.");
-
-  mRenderTargets[currentRenderTarget]->ClearOnBind();
-  RefPtr<CompositingRenderTargetD3D11> rt = mRenderTargets[currentRenderTarget];
-  return rt.forget();
-}
-
 bool
 VRDisplayOculus::UpdateConstantBuffers()
 {
   HRESULT hr;
   D3D11_MAPPED_SUBRESOURCE resource;
   resource.pData = nullptr;
 
   hr = mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
@@ -726,34 +1017,29 @@ VRDisplayOculus::UpdateConstantBuffers()
 }
 
 bool
 VRDisplayOculus::SubmitFrame(TextureSourceD3D11* aSource,
   const IntSize& aSize,
   const gfx::Rect& aLeftEyeRect,
   const gfx::Rect& aRightEyeRect)
 {
-  if (!mIsPresenting) {
+  if (!mSession->IsRenderReady() || !mDevice || !mContext) {
     return false;
   }
-  if (mRenderTargets.IsEmpty()) {
-    /**
-     * 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.
-     **/
-    return false;
-  }
-  MOZ_ASSERT(mDevice);
-  MOZ_ASSERT(mContext);
+  /**
+    * 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.
+    **/
 
-  RefPtr<CompositingRenderTargetD3D11> surface = GetNextRenderTarget();
+  RefPtr<CompositingRenderTargetD3D11> surface = mSession->GetNextRenderTarget();
 
   surface->BindRenderTarget(mContext);
 
   Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
   viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
   viewMatrix.PreScale(1.0f, -1.0f);
   Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
   projection._33 = 0.0f;
@@ -808,27 +1094,27 @@ VRDisplayOculus::SubmitFrame(TextureSour
 
   if (!UpdateConstantBuffers()) {
     NS_WARNING("Failed to update constant buffers for Oculus");
     return false;
   }
 
   mContext->Draw(4, 0);
 
-  ovrResult orv = ovr_CommitTextureSwapChain(mSession, mTextureSet);
+  ovrResult orv = ovr_CommitTextureSwapChain(mSession->Get(), mSession->GetSwapChain());
   if (orv != ovrSuccess) {
     NS_WARNING("ovr_CommitTextureSwapChain failed.\n");
     return false;
   }
 
   ovrLayerEyeFov layer;
   memset(&layer, 0, sizeof(layer));
   layer.Header.Type = ovrLayerType_EyeFov;
   layer.Header.Flags = 0;
-  layer.ColorTexture[0] = mTextureSet;
+  layer.ColorTexture[0] = mSession->GetSwapChain();
   layer.ColorTexture[1] = nullptr;
   layer.Fov[0] = mFOVPort[0];
   layer.Fov[1] = mFOVPort[1];
   layer.Viewport[0].Pos.x = aSize.width * aLeftEyeRect.x;
   layer.Viewport[0].Pos.y = aSize.height * aLeftEyeRect.y;
   layer.Viewport[0].Size.w = aSize.width * aLeftEyeRect.width;
   layer.Viewport[0].Size.h = aSize.height * aLeftEyeRect.height;
   layer.Viewport[1].Pos.x = aSize.width * aRightEyeRect.x;
@@ -855,17 +1141,17 @@ VRDisplayOculus::SubmitFrame(TextureSour
     layer.RenderPose[i].Orientation.z = o.z;
     layer.RenderPose[i].Orientation.w = o.w;
     layer.RenderPose[i].Position.x = p.x + sensorState.position[0];
     layer.RenderPose[i].Position.y = p.y + sensorState.position[1];
     layer.RenderPose[i].Position.z = p.z + sensorState.position[2];
   }
 
   ovrLayerHeader *layers = &layer.Header;
-  orv = ovr_SubmitFrame(mSession, mDisplayInfo.mFrameId, nullptr, &layers, 1);
+  orv = ovr_SubmitFrame(mSession->Get(), mDisplayInfo.mFrameId, 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
@@ -877,19 +1163,24 @@ VRDisplayOculus::SubmitFrame(TextureSour
   }
 
   return true;
 }
 
 void
 VRDisplayOculus::NotifyVSync()
 {
-  ovrSessionStatus sessionStatus;
-  ovrResult ovr = ovr_GetSessionStatus(mSession, &sessionStatus);
-  mDisplayInfo.mIsConnected = (ovr == ovrSuccess && sessionStatus.HmdPresent);
+  mSession->Refresh();
+  if (mSession->IsTrackingReady()) {
+    ovrSessionStatus sessionStatus;
+    ovrResult ovr = ovr_GetSessionStatus(mSession->Get(), &sessionStatus);
+    mDisplayInfo.mIsConnected = (ovr == ovrSuccess && sessionStatus.HmdPresent);
+  } else {
+    mDisplayInfo.mIsConnected = false;
+  }
 
   VRDisplayHost::NotifyVSync();
 }
 
 VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand)
   : VRControllerHost(VRDeviceType::Oculus)
   , mIndexTrigger(0.0f)
   , mHandTrigger(0.0f)
@@ -1116,128 +1407,98 @@ VRSystemManagerOculus::Create()
   {
     return nullptr;
   }
 
   RefPtr<VRSystemManagerOculus> manager = new VRSystemManagerOculus();
   return manager.forget();
 }
 
-bool
-VRSystemManagerOculus::Startup()
+VRSystemManagerOculus::VRSystemManagerOculus()
+  : mSession(nullptr)
 {
-  if (mStarted) {
-    return true;
-  }
-
-  if (!LoadOvrLib()) {
-    return false;
-  }
-
-  nsIThread* thread = nullptr;
-  NS_GetCurrentThread(&thread);
-  mOculusThread = already_AddRefed<nsIThread>(thread);
-
-  ovrInitParams params;
-  memset(&params, 0, sizeof(params));
-  params.Flags = ovrInit_RequestVersion;
-  params.RequestedMinorVersion = OVR_MINOR_VERSION;
-  params.LogCallback = nullptr;
-  params.ConnectionTimeoutMS = 0;
-
-  ovrResult orv = ovr_Initialize(&params);
-
-  if (orv == ovrSuccess) {
-    mStarted = true;
-  }
-
-  return mStarted;
 }
 
 void
 VRSystemManagerOculus::Destroy()
 {
   Shutdown();
+  mSession = nullptr;
 }
 
 void
 VRSystemManagerOculus::Shutdown()
 {
-  if (mStarted) {
-    RemoveControllers();
-    MOZ_ASSERT(NS_GetCurrentThread() == mOculusThread);
-    mOculusThread = nullptr;
-    mSession = nullptr;
-    mHMDInfo = nullptr;
-
-    ovr_Shutdown();
-    UnloadOvrLib();
-    mStarted = false;
+  if (mSession) {
+    mSession->StopTracking();
   }
+  RemoveControllers();
+  if (mDisplay) {
+    mDisplay->Destroy();
+  }
+  mDisplay = nullptr;
 }
 
-void
+bool
 VRSystemManagerOculus::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
 {
-  if (!Startup()) {
-    return;
+  if (!mSession) {
+    mSession = new VROculusSession();
+  }
+  mSession->Refresh();
+  if (mSession->IsQuitTimeoutActive()) {
+    // We have responded to a ShouldQuit flag set by the Oculus runtime
+    // and are waiting for a timeout duration to elapse before allowing
+    // re-initialization of the Oculus OVR lib.  We return true in this case
+    // to prevent further enumeration by other VRSystemManager's such as
+    // VRSystemManagerOpenVR which would also enumerate the connected Oculus
+    // HMD, resulting in interference with the Oculus runtime software updates.
+    mDisplay = nullptr;
+    return true;
   }
 
-  // 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) {
+  if (!mSession->IsTrackingReady()) {
     // No HMD connected.
-    mHMDInfo = nullptr;
-  } else if (mHMDInfo == nullptr) {
+    mDisplay = nullptr;
+  } else if (mDisplay == nullptr) {
     // HMD Detected
-    ovrSession session;
-    ovrGraphicsLuid luid;
-    ovrResult orv = ovr_Create(&session, &luid);
-    if (orv == ovrSuccess) {
-      mSession = session;
-      orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
-      if (orv != ovrSuccess) {
-        NS_WARNING("ovr_SetTrackingOriginType failed.\n");
-      }
-
-      mHMDInfo = new VRDisplayOculus(session);
-    }
+    mDisplay = new VRDisplayOculus(mSession);
   }
 
-  if (mHMDInfo) {
-    aHMDResult.AppendElement(mHMDInfo);
+  if (mDisplay) {
+    aHMDResult.AppendElement(mDisplay);
+    return true;
   }
+  return false;
 }
 
 bool
 VRSystemManagerOculus::GetIsPresenting()
 {
-  if (mHMDInfo) {
-    VRDisplayInfo displayInfo(mHMDInfo->GetDisplayInfo());
+  if (mDisplay) {
+    VRDisplayInfo displayInfo(mDisplay->GetDisplayInfo());
     return displayInfo.GetPresentingGroups() != 0;
   }
 
   return false;
 }
 
 void
 VRSystemManagerOculus::HandleInput()
 {
-  // mSession is available after VRDisplay is created
+  // The session is available after VRDisplay is created
   // at GetHMDs().
-  if (!mSession) {
+  if (!mSession || !mSession->IsTrackingReady()) {
     return;
   }
 
   RefPtr<impl::VRControllerOculus> controller;
   ovrInputState inputState;
   uint32_t axis = 0;
-  const bool hasInputState = ovr_GetInputState(mSession, ovrControllerType_Touch,
+  const bool hasInputState = ovr_GetInputState(mSession->Get(), ovrControllerType_Touch,
                                                &inputState) == ovrSuccess;
 
   if (!hasInputState) {
     return;
   }
 
   for (uint32_t i = 0; i < mOculusController.Length(); ++i) {
     controller = mOculusController[i];
@@ -1289,17 +1550,17 @@ VRSystemManagerOculus::HandleInput()
 
     axis = static_cast<uint32_t>(OculusControllerAxisType::ThumbstickXAxis);
     HandleAxisMove(i, axis, inputState.Thumbstick[i].x);
 
     axis = static_cast<uint32_t>(OculusControllerAxisType::ThumbstickYAxis);
     HandleAxisMove(i, axis, -inputState.Thumbstick[i].y);
 
     // Start to process pose
-    ovrTrackingState state = ovr_GetTrackingState(mSession, 0.0, false);
+    ovrTrackingState state = ovr_GetTrackingState(mSession->Get(), 0.0, false);
 
     // HandPoses is ordered by ovrControllerType_LTouch and ovrControllerType_RTouch,
     // therefore, we can't get its state by the index of mOculusController.
     ovrPoseStatef& pose(state.HandPoses[handIdx]);
     GamepadPoseState poseState;
 
     if (state.HandStatusFlags[handIdx] & ovrStatus_OrientationTracked) {
       poseState.flags |= GamepadCapabilityFlags::Cap_Orientation;
@@ -1326,17 +1587,17 @@ VRSystemManagerOculus::HandleInput()
       poseState.linearVelocity[1] = pose.LinearVelocity.y;
       poseState.linearVelocity[2] = pose.LinearVelocity.z;
 
       poseState.flags |= GamepadCapabilityFlags::Cap_LinearAcceleration;
       poseState.linearAcceleration[0] = pose.LinearAcceleration.x;
       poseState.linearAcceleration[1] = pose.LinearAcceleration.y;
       poseState.linearAcceleration[2] = pose.LinearAcceleration.z;
 
-      float eyeHeight = ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+      float eyeHeight = ovr_GetFloat(mSession->Get(), OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
       poseState.position[1] -= eyeHeight;
       poseState.isPositionValid = true;
     }
     HandlePoseTracking(i, poseState, controller);
   }
 }
 
 void
@@ -1453,34 +1714,34 @@ VRSystemManagerOculus::HandlePoseTrackin
 
 void
 VRSystemManagerOculus::VibrateHaptic(uint32_t aControllerIdx,
                                      uint32_t aHapticIndex,
                                      double aIntensity,
                                      double aDuration,
                                      uint32_t aPromiseID)
 {
-  // mSession is available after VRDisplay is created
+  // The session is available after VRDisplay is created
   // at GetHMDs().
-  if (!mSession) {
+  if (!mSession || !mSession->IsTrackingReady()) {
     return;
   }
 
   RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
   MOZ_ASSERT(controller);
 
-  controller->VibrateHaptic(mSession, aHapticIndex, aIntensity, aDuration, aPromiseID);
+  controller->VibrateHaptic(mSession->Get(), aHapticIndex, aIntensity, aDuration, aPromiseID);
 }
 
 void
 VRSystemManagerOculus::StopVibrateHaptic(uint32_t aControllerIdx)
 {
-  // mSession is available after VRDisplay is created
+  // The session is available after VRDisplay is created
   // at GetHMDs().
-  if (!mSession) {
+  if (!mSession || !mSession->IsTrackingReady()) {
     return;
   }
 
   RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
   MOZ_ASSERT(controller);
 
   controller->StopVibrateHaptic();
 }
@@ -1495,22 +1756,22 @@ VRSystemManagerOculus::GetControllers(ns
   }
 }
 
 void
 VRSystemManagerOculus::ScanForControllers()
 {
   // mSession is available after VRDisplay is created
   // at GetHMDs().
-  if (!mSession) {
+  if (!mSession || !mSession->IsTrackingReady()) {
     return;
   }
 
   ovrInputState inputState;
-  bool hasInputState = ovr_GetInputState(mSession, ovrControllerType_Touch,
+  bool hasInputState = ovr_GetInputState(mSession->Get(), ovrControllerType_Touch,
                                          &inputState) == ovrSuccess;
   ovrControllerType activeControllerArray[2];
   uint32_t newControllerCount = 0;
 
   if (inputState.ControllerType & ovrControllerType_LTouch) {
     activeControllerArray[newControllerCount] = ovrControllerType_LTouch;
     ++newControllerCount;
   }
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -2,16 +2,17 @@
  * 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_OCULUS_H
 #define GFX_VR_OCULUS_H
 
 #include "nsTArray.h"
+#include "nsISupportsImpl.h" // For NS_INLINE_DECL_REFCOUNTING
 #include "mozilla/RefPtr.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/EnumeratedArray.h"
 
 #include "gfxVR.h"
 #include "VRDisplayHost.h"
 #include "ovr_capi_dynamic.h"
@@ -28,16 +29,59 @@ namespace gfx {
 namespace impl {
 
 enum class OculusControllerAxisType : uint16_t {
   ThumbstickXAxis,
   ThumbstickYAxis,
   NumVRControllerAxisType
 };
 
+class VROculusSession
+{
+  NS_INLINE_DECL_REFCOUNTING(VROculusSession);
+public:
+  VROculusSession();
+  void Refresh();
+  bool IsTrackingReady() const;
+  bool IsRenderReady() const;
+  ovrSession Get();
+  void StartPresentation(const IntSize& aSize);
+  void StopPresentation();
+  void StopTracking();
+  bool IsQuitTimeoutActive();
+  already_AddRefed<layers::CompositingRenderTargetD3D11> GetNextRenderTarget();
+  ovrTextureSwapChain GetSwapChain();
+
+private:
+  PRLibrary* mOvrLib;
+  ovrSession mSession;
+  ovrInitFlags mInitFlags;
+  ovrTextureSwapChain mTextureSet;
+  nsTArray<RefPtr<layers::CompositingRenderTargetD3D11>> mRenderTargets;
+  bool mPresenting;
+  IntSize mPresentationSize;
+  RefPtr<ID3D11Device> mDevice;
+  // The timestamp of the last time Oculus set ShouldQuit to true.
+  TimeStamp mLastShouldQuit;
+  // The timestamp of the last ending presentation
+  TimeStamp mLastPresentationEnd;
+
+  ~VROculusSession();
+  void Uninitialize(bool aUnloadLib);
+  bool Initialize(ovrInitFlags aFlags);
+  bool LoadOvrLib();
+  void UnloadOvrLib();
+  bool StartSession();
+  void StopSession();
+  bool StartLib(ovrInitFlags aFlags);
+  void StopLib();
+  bool StartRendering();
+  void StopRendering();
+};
+
 class VRDisplayOculus : public VRDisplayHost
 {
 public:
   virtual void NotifyVSync() override;
   void ZeroSensor() override;
 
 protected:
   virtual VRHMDSensorState GetSensorState() override;
@@ -45,48 +89,40 @@ protected:
   virtual void StopPresentation() override;
   virtual bool SubmitFrame(mozilla::layers::TextureSourceD3D11* aSource,
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) override;
   void UpdateStageParameters();
 
 public:
-  explicit VRDisplayOculus(ovrSession aSession);
+  explicit VRDisplayOculus(VROculusSession* aSession);
+  void Destroy();
 
 protected:
   virtual ~VRDisplayOculus();
-  void Destroy();
-
-  bool RequireSession();
-  const ovrHmdDesc& GetHmdDesc();
-
-  already_AddRefed<layers::CompositingRenderTargetD3D11> GetNextRenderTarget();
 
   VRHMDSensorState GetSensorState(double absTime);
 
   ovrHmdDesc mDesc;
-  ovrSession mSession;
+  RefPtr<VROculusSession> mSession;
   ovrFovPort mFOVPort[2];
-  ovrTextureSwapChain mTextureSet;
-  nsTArray<RefPtr<layers::CompositingRenderTargetD3D11>> mRenderTargets;
 
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11DeviceContext> mContext;
   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;
 
-  bool mIsPresenting;
   float mEyeHeight;
 
   bool UpdateConstantBuffers();
 
   struct Vertex
   {
     float position[2];
   };
@@ -132,55 +168,46 @@ private:
 } // namespace impl
 
 class VRSystemManagerOculus : public VRSystemManager
 {
 public:
   static already_AddRefed<VRSystemManagerOculus> Create();
   virtual void Destroy() override;
   virtual void Shutdown() override;
-  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
+  virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
   virtual bool GetIsPresenting() override;
   virtual void HandleInput() override;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
                               aControllerResult) override;
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                              double aIntensity, double aDuration, uint32_t aPromiseID) override;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
 
 protected:
-  VRSystemManagerOculus()
-    : mOvrLib(nullptr), mSession(nullptr), mStarted(false)
-  {}
-
-  bool Startup();
-  bool LoadOvrLib();
-  void UnloadOvrLib();
+  VRSystemManagerOculus();
 
 private:
   void HandleButtonPress(uint32_t aControllerIdx,
                          uint32_t aButton,
                          uint64_t aButtonMask,
                          uint64_t aButtonPressed,
                          uint64_t aButtonTouched);
   void HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
                       float aValue);
   void HandlePoseTracking(uint32_t aControllerIdx,
                           const dom::GamepadPoseState& aPose,
                           VRControllerHost* aController);
   void HandleIndexTriggerPress(uint32_t aControllerIdx, uint32_t aButton, float aValue);
   void HandleHandTriggerPress(uint32_t aControllerIdx, uint32_t aButton, float aValue);
   void HandleTouchEvent(uint32_t aControllerIdx, uint32_t aButton,
                         uint64_t aTouchMask, uint64_t aTouched);
-  PRLibrary* mOvrLib;
-  RefPtr<impl::VRDisplayOculus> mHMDInfo;
+  RefPtr<impl::VRDisplayOculus> mDisplay;
   nsTArray<RefPtr<impl::VRControllerOculus>> mOculusController;
-  RefPtr<nsIThread> mOculusThread;
-  ovrSession mSession;
-  bool mStarted;
+  RefPtr<impl::VROculusSession> mSession;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_OCULUS_H */
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -544,56 +544,58 @@ VRSystemManagerOpenVR::Shutdown()
 {
   if (mOpenVRHMD) {
     mOpenVRHMD = nullptr;
   }
   RemoveControllers();
   mVRSystem = nullptr;
 }
 
-void
+bool
 VRSystemManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
 {
   if (!::vr::VR_IsHmdPresent() ||
       (mOpenVRHMD && !mOpenVRHMD->GetIsConnected())) {
     // OpenVR runtime could be quit accidentally,
     // and we make it re-initialize.
     mOpenVRHMD = nullptr;
     mVRSystem = nullptr;
   } else if (mOpenVRHMD == nullptr) {
     ::vr::HmdError err;
 
     ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
     if (err) {
-      return;
+      return false;
     }
 
     ::vr::IVRSystem *system = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
     if (err || !system) {
       ::vr::VR_Shutdown();
-      return;
+      return false;
     }
     ::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
     if (err || !chaperone) {
       ::vr::VR_Shutdown();
-      return;
+      return false;
     }
     ::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
     if (err || !compositor) {
       ::vr::VR_Shutdown();
-      return;
+      return false;
     }
 
     mVRSystem = system;
     mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
   }
 
   if (mOpenVRHMD) {
     aHMDResult.AppendElement(mOpenVRHMD);
+    return true;
   }
+  return false;
 }
 
 bool
 VRSystemManagerOpenVR::GetIsPresenting()
 {
   if (mOpenVRHMD) {
     VRDisplayInfo displayInfo(mOpenVRHMD->GetDisplayInfo());
     return displayInfo.GetPresentingGroups() != kVRGroupNone;
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -103,17 +103,17 @@ private:
 
 class VRSystemManagerOpenVR : public VRSystemManager
 {
 public:
   static already_AddRefed<VRSystemManagerOpenVR> Create();
 
   virtual void Destroy() override;
   virtual void Shutdown() override;
-  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
+  virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
   virtual bool GetIsPresenting() override;
   virtual void HandleInput() override;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
                               aControllerResult) override;
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx,
                              uint32_t aHapticIndex,
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -617,23 +617,24 @@ VRSystemManagerPuppet::Destroy()
 }
 
 void
 VRSystemManagerPuppet::Shutdown()
 {
   mPuppetHMD = nullptr;
 }
 
-void
+bool
 VRSystemManagerPuppet::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
 {
   if (mPuppetHMD == nullptr) {
     mPuppetHMD = new VRDisplayPuppet();
   }
   aHMDResult.AppendElement(mPuppetHMD);
+  return true;
 }
 
 bool
 VRSystemManagerPuppet::GetIsPresenting()
 {
   if (mPuppetHMD) {
     VRDisplayInfo displayInfo(mPuppetHMD->GetDisplayInfo());
     return displayInfo.GetPresentingGroups() != kVRGroupNone;
--- a/gfx/vr/gfxVRPuppet.h
+++ b/gfx/vr/gfxVRPuppet.h
@@ -98,17 +98,17 @@ private:
 
 class VRSystemManagerPuppet : public VRSystemManager
 {
 public:
   static already_AddRefed<VRSystemManagerPuppet> Create();
 
   virtual void Destroy() override;
   virtual void Shutdown() override;
-  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
+  virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
   virtual bool GetIsPresenting() override;
   virtual void HandleInput() override;
   virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
                               aControllerResult) override;
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx,
                              uint32_t aHapticIndex,
--- a/gfx/vr/ipc/VRLayerParent.cpp
+++ b/gfx/vr/ipc/VRLayerParent.cpp
@@ -35,25 +35,38 @@ void
 VRLayerParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mIPCOpen = false;
 }
 
 void
 VRLayerParent::Destroy()
 {
+  if (mVRDisplayID) {
+    VRManager* vm = VRManager::Get();
+    RefPtr<gfx::VRDisplayHost> display = vm->GetDisplay(mVRDisplayID);
+    if (display) {
+      display->RemoveLayer(this);
+    }
+    // 0 will never be a valid VRDisplayID; we can use it to indicate that
+    // we are destroyed and no longer associated with a display.
+    mVRDisplayID = 0;
+  }
+
   if (mIPCOpen) {
     Unused << PVRLayerParent::Send__delete__(this);
   }
 }
 
 mozilla::ipc::IPCResult
 VRLayerParent::RecvSubmitFrame(PTextureParent* texture)
 {
-  VRManager* vm = VRManager::Get();
-  vm->SubmitFrame(this, texture, mLeftEyeRect, mRightEyeRect);
+  if (mVRDisplayID) {
+    VRManager* vm = VRManager::Get();
+    vm->SubmitFrame(this, texture, mLeftEyeRect, mRightEyeRect);
+  }
 
   return IPC_OK();
 }
 
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -79,24 +79,16 @@ VRManagerParent::AllocPVRLayerParent(con
     display->AddLayer(layer);
   }
   return layer.forget().take();
 }
 
 bool
 VRManagerParent::DeallocPVRLayerParent(PVRLayerParent* actor)
 {
-  gfx::VRLayerParent* layer = static_cast<gfx::VRLayerParent*>(actor);
-
-  VRManager* vm = VRManager::Get();
-  RefPtr<gfx::VRDisplayHost> display = vm->GetDisplay(layer->GetDisplayID());
-  if (display) {
-    display->RemoveLayer(layer);
-  }
-
   delete actor;
   return true;
 }
 
 bool
 VRManagerParent::AllocShmem(size_t aSize,
   ipc::SharedMemory::SharedMemoryType aType,
   ipc::Shmem* aShmem)
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5158,16 +5158,32 @@ pref("dom.vr.controller_trigger_threshol
 // 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
 pref("dom.vr.oculus.enabled", true);
+// Minimum number of milliseconds after content has stopped VR presentation
+// before the Oculus session is re-initialized to an invisible / tracking
+// only mode.  If this value is too high, users will need to wait longer
+// after stopping WebVR presentation before automatically returning to the
+// Oculus home interface.  (They can immediately return to the Oculus Home
+// interface through the Oculus HUD without waiting this duration)
+// If this value is too low, the Oculus Home interface may be visible
+// momentarily during VR link navigation.
+pref("dom.vr.oculus.present.timeout", 10000);
+// Minimum number of milliseconds that the browser will wait before
+// reloading the Oculus OVR library after seeing a "ShouldQuit" flag set.
+// Oculus requests that we shut down and unload the OVR library, by setting
+// a "ShouldQuit" flag.  To ensure that we don't interfere with
+// Oculus software auto-updates, we will not attempt to re-load the
+// OVR library until this timeout has elapsed.
+pref("dom.vr.oculus.quit.timeout", 30000);
 // OSVR device
 pref("dom.vr.osvr.enabled", false);
 // OpenVR device
 #ifdef XP_WIN
 pref("dom.vr.openvr.enabled", true);
 #else
 // See Bug 1310663 (Linux) and Bug 1310665 (macOS)
 pref("dom.vr.openvr.enabled", false);