Bug 1469967 - Implement immersive mode for gfxVRExternal draft
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Tue, 08 May 2018 11:31:28 -0700
changeset 808842 f0c003e325348e9405930882fdad74bff59df45a
parent 808726 681eb7dfa324dd50403c382888929ea8b8b11b00
push id113521
push userkgilbert@mozilla.com
push dateWed, 20 Jun 2018 23:21:27 +0000
bugs1469967
milestone62.0a1
Bug 1469967 - Implement immersive mode for gfxVRExternal - The VR External interface/shmem and gfxVRExternal are updated to enable entering VR, exiting VR, and submitting frames. - You can now construct gfxVRExternal using a pointer to the VR External API shmem, if it is created elsewhere. MozReview-Commit-ID: LZuoLvoEmKO
gfx/vr/VRDisplayHost.cpp
gfx/vr/VRDisplayHost.h
gfx/vr/VRDisplayLocal.cpp
gfx/vr/VRDisplayLocal.h
gfx/vr/VRManager.cpp
gfx/vr/external_api/moz_external_vr.h
gfx/vr/gfxVRExternal.cpp
gfx/vr/gfxVRExternal.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/VRMessageUtils.h
gfx/vr/moz.build
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -268,93 +268,18 @@ VRDisplayHost::SubmitFrameInternal(const
                                    const gfx::Rect& aLeftEyeRect,
                                    const gfx::Rect& aRightEyeRect)
 {
 #if !defined(MOZ_WIDGET_ANDROID)
   MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
 #endif // !defined(MOZ_WIDGET_ANDROID)
   AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplayHost");
 
-  mFrameStarted = false;
-  switch (aTexture.type()) {
-
-#if defined(XP_WIN)
-    case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
-      if (!CreateD3DObjects()) {
-        return;
-      }
-      const SurfaceDescriptorD3D10& surf = aTexture.get_SurfaceDescriptorD3D10();
-      RefPtr<ID3D11Texture2D> dxTexture;
-      HRESULT hr = mDevice->OpenSharedResource((HANDLE)surf.handle(),
-        __uuidof(ID3D11Texture2D),
-        (void**)(ID3D11Texture2D**)getter_AddRefs(dxTexture));
-      if (FAILED(hr) || !dxTexture) {
-        NS_WARNING("Failed to open shared texture");
-        return;
-      }
-
-      // 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;
-        }
-      }
-      bool success = SubmitFrame(dxTexture, surf.size(),
-                                 aLeftEyeRect, aRightEyeRect);
-      if (mutex) {
-        HRESULT hr = mutex->ReleaseSync(0);
-        if (FAILED(hr)) {
-          NS_WARNING("Failed to unlock the texture");
-        }
-      }
-      if (!success) {
-        return;
-      }
-      break;
-    }
-#elif defined(XP_MACOSX)
-    case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
-      const auto& desc = aTexture.get_SurfaceDescriptorMacIOSurface();
-      RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(desc.surfaceId(),
-                                                              desc.scaleFactor(),
-                                                              !desc.isOpaque());
-      if (!surf) {
-        NS_WARNING("VRDisplayHost::SubmitFrame failed to get a MacIOSurface");
-        return;
-      }
-      IntSize texSize = gfx::IntSize(surf->GetDevicePixelWidth(),
-                                     surf->GetDevicePixelHeight());
-      if (!SubmitFrame(surf, texSize, aLeftEyeRect, aRightEyeRect)) {
-        return;
-      }
-      break;
-    }
-#elif defined(MOZ_WIDGET_ANDROID)
-    case SurfaceDescriptor::TSurfaceTextureDescriptor: {
-      const SurfaceTextureDescriptor& desc = aTexture.get_SurfaceTextureDescriptor();
-      if (!SubmitFrame(desc, aLeftEyeRect, aRightEyeRect)) {
-        return;
-      }
-      break;
-    }
-#endif
-    default: {
-      NS_WARNING("Unsupported SurfaceDescriptor type for VR layer texture");
-      return;
-    }
+  if (!SubmitFrame(aTexture, aFrameId, aLeftEyeRect, aRightEyeRect)) {
+    return;
   }
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
 
   /**
    * Trigger the next VSync immediately after we are successfully
    * submitting frames.  As SubmitFrame is responsible for throttling
    * the render loop, if we don't successfully call it, we shouldn't trigger
@@ -376,38 +301,38 @@ VRDisplayHost::SubmitFrameInternal(const
 
 void
 VRDisplayHost::SubmitFrame(VRLayerParent* aLayer,
                            const layers::SurfaceDescriptor &aTexture,
                            uint64_t aFrameId,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect)
 {
-#if !defined(MOZ_WIDGET_ANDROID)
-  if (!mSubmitThread) {
-    mSubmitThread = new VRThread(NS_LITERAL_CSTRING("VR_SubmitFrame"));
-  }
-#endif // !defined(MOZ_WIDGET_ANDROID)
-
   if ((mDisplayInfo.mGroupMask & aLayer->GetGroup()) == 0) {
     // Suppress layers hidden by the group mask
     return;
   }
 
   // Ensure that we only accept the first SubmitFrame call per RAF cycle.
   if (!mFrameStarted || aFrameId != mDisplayInfo.mFrameId) {
     return;
   }
 
+  mFrameStarted = false;
+
   RefPtr<Runnable> submit =
     NewRunnableMethod<StoreCopyPassByConstLRef<layers::SurfaceDescriptor>, uint64_t,
       StoreCopyPassByConstLRef<gfx::Rect>, StoreCopyPassByConstLRef<gfx::Rect>>(
       "gfx::VRDisplayHost::SubmitFrameInternal", this, &VRDisplayHost::SubmitFrameInternal,
       aTexture, aFrameId, aLeftEyeRect, aRightEyeRect);
+
 #if !defined(MOZ_WIDGET_ANDROID)
+  if (!mSubmitThread) {
+    mSubmitThread = new VRThread(NS_LITERAL_CSTRING("VR_SubmitFrame"));
+  }
   mSubmitThread->Start();
   mSubmitThread->PostTask(submit.forget());
 #else
   CompositorThreadHolder::Loop()->PostTask(submit.forget());
 #endif // defined(MOZ_WIDGET_ANDROID)
 }
 
 bool
--- a/gfx/vr/VRDisplayHost.h
+++ b/gfx/vr/VRDisplayHost.h
@@ -67,35 +67,23 @@ public:
 #endif
     bool mSuccess;
   };
 
 protected:
   explicit VRDisplayHost(VRDeviceType aType);
   virtual ~VRDisplayHost();
 
-#if defined(XP_WIN)
-  // Subclasses should override this SubmitFrame function.
-  // Returns true if the SubmitFrame call will block as necessary
-  // to control timing of the next frame and throttle the render loop
-  // for the needed framerate.
-  virtual bool SubmitFrame(ID3D11Texture2D* aSource,
-                           const IntSize& aSize,
+  // This SubmitFrame() must be overridden by children and block until
+  // the next frame is ready to start and the resources in aTexture can
+  // safely be released.
+  virtual bool SubmitFrame(const layers::SurfaceDescriptor& aTexture,
+                           uint64_t aFrameId,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) = 0;
-#elif defined(XP_MACOSX)
-  virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
-                           const IntSize& aSize,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) = 0;
-#elif defined(MOZ_WIDGET_ANDROID)
-  virtual bool SubmitFrame(const mozilla::layers::SurfaceTextureDescriptor& aSurface,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) = 0;
-#endif
 
   VRDisplayInfo mDisplayInfo;
 
   nsTArray<VRLayerParent *> mLayers;
   // Weak reference to mLayers entries are cleared in
   // VRLayerParent destructor
 
 protected:
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRDisplayLocal.cpp
@@ -0,0 +1,132 @@
+/* -*- 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 "VRDisplayLocal.h"
+#include "gfxPrefs.h"
+#include "gfxVR.h"
+#include "ipc/VRLayerParent.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/dom/GamepadBinding.h" // For GamepadMappingType
+#include "VRThread.h"
+
+#if defined(XP_WIN)
+
+#include <d3d11.h>
+#include "gfxWindowsPlatform.h"
+#include "../layers/d3d11/CompositorD3D11.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/TextureD3D11.h"
+
+#elif defined(XP_MACOSX)
+
+#include "mozilla/gfx/MacIOSurface.h"
+
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/layers/CompositorThread.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+VRDisplayLocal::VRDisplayLocal(VRDeviceType aType)
+  : VRDisplayHost(aType)
+{
+  MOZ_COUNT_CTOR_INHERITED(VRDisplayLocal, VRDisplayHost);
+}
+
+VRDisplayLocal::~VRDisplayLocal()
+{
+  MOZ_COUNT_DTOR_INHERITED(VRDisplayLocal, VRDisplayHost);
+}
+
+bool
+VRDisplayLocal::SubmitFrame(const layers::SurfaceDescriptor &aTexture,
+                            uint64_t aFrameId,
+                            const gfx::Rect& aLeftEyeRect,
+                            const gfx::Rect& aRightEyeRect)
+{
+#if !defined(MOZ_WIDGET_ANDROID)
+  MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
+#endif // !defined(MOZ_WIDGET_ANDROID)
+
+  switch (aTexture.type()) {
+
+#if defined(XP_WIN)
+    case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
+      if (!CreateD3DObjects()) {
+        return false;
+      }
+      const SurfaceDescriptorD3D10& surf = aTexture.get_SurfaceDescriptorD3D10();
+      RefPtr<ID3D11Texture2D> dxTexture;
+      HRESULT hr = mDevice->OpenSharedResource((HANDLE)surf.handle(),
+        __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(dxTexture, surf.size(),
+                                 aLeftEyeRect, aRightEyeRect);
+      if (mutex) {
+        HRESULT hr = mutex->ReleaseSync(0);
+        if (FAILED(hr)) {
+          NS_WARNING("Failed to unlock the texture");
+        }
+      }
+      return success;
+    }
+#elif defined(XP_MACOSX)
+    case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
+      const auto& desc = aTexture.get_SurfaceDescriptorMacIOSurface();
+      RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(desc.surfaceId(),
+                                                              desc.scaleFactor(),
+                                                              !desc.isOpaque());
+      if (!surf) {
+        NS_WARNING("VRDisplayHost::SubmitFrame failed to get a MacIOSurface");
+        return false;
+      }
+      IntSize texSize = gfx::IntSize(surf->GetDevicePixelWidth(),
+                                     surf->GetDevicePixelHeight());
+      if (!SubmitFrame(surf, texSize, aLeftEyeRect, aRightEyeRect)) {
+        return false;
+      }
+      return true;
+    }
+#elif defined(MOZ_WIDGET_ANDROID)
+    case SurfaceDescriptor::TSurfaceTextureDescriptor: {
+      const SurfaceTextureDescriptor& desc = aTexture.get_SurfaceTextureDescriptor();
+      if (!SubmitFrame(desc, aLeftEyeRect, aRightEyeRect)) {
+        return false;
+      }
+      return true;
+    }
+#endif
+    default: {
+      NS_WARNING("Unsupported SurfaceDescriptor type for VR layer texture");
+      return false;
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRDisplayLocal.h
@@ -0,0 +1,61 @@
+/* -*- 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_DISPLAY_LOCAL_H
+#define GFX_VR_DISPLAY_LOCAL_H
+
+#include "gfxVR.h"
+#include "VRDisplayHost.h"
+
+#if defined(XP_WIN)
+#include <d3d11_1.h>
+#elif defined(XP_MACOSX)
+class MacIOSurface;
+#endif
+
+namespace mozilla {
+namespace gfx {
+class VRThread;
+
+class VRDisplayLocal : public VRDisplayHost
+{
+public:
+
+#if defined(XP_WIN)
+  // Subclasses should override this SubmitFrame function.
+  // Returns true if the SubmitFrame call will block as necessary
+  // to control timing of the next frame and throttle the render loop
+  // for the needed framerate.
+  virtual bool SubmitFrame(ID3D11Texture2D* aSource,
+                           const IntSize& aSize,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) = 0;
+#elif defined(XP_MACOSX)
+  virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
+                           const IntSize& aSize,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) = 0;
+#elif defined(MOZ_WIDGET_ANDROID)
+  virtual bool SubmitFrame(const mozilla::layers::SurfaceTextureDescriptor& aSurface,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) = 0;
+#endif
+
+protected:
+  explicit VRDisplayLocal(VRDeviceType aType);
+  virtual ~VRDisplayLocal();
+
+private:
+  bool SubmitFrame(const layers::SurfaceDescriptor& aTexture,
+                   uint64_t aFrameId,
+                   const gfx::Rect& aLeftEyeRect,
+                   const gfx::Rect& aRightEyeRect) final;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_DISPLAY_LOCAL_H
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -226,16 +226,22 @@ VRManager::CheckForInactiveTimeout()
   }
   else if (mLastActiveTime.IsNull()) {
     Shutdown();
   }
   else {
     TimeDuration duration = TimeStamp::Now() - mLastActiveTime;
     if (duration.ToMilliseconds() > gfxPrefs::VRInactiveTimeout()) {
       Shutdown();
+      // We must not throttle the next enumeration request
+      // after an idle timeout, as it may result in the
+      // user needing to refresh the browser to detect
+      // VR hardware when leaving and returning to a VR
+      // site.
+      mLastDisplayEnumerationTime = TimeStamp();
     }
   }
 }
 
 void
 VRManager::NotifyVRVsync(const uint32_t& aDisplayID)
 {
   MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
--- a/gfx/vr/external_api/moz_external_vr.h
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -230,16 +230,18 @@ struct VRDisplayState
   VRFieldOfView mEyeFOV[VRDisplayState::NumEyes];
   Point3D_POD mEyeTranslation[VRDisplayState::NumEyes];
   IntSize_POD mEyeResolution;
   bool mIsConnected;
   bool mIsMounted;
   FloatSize_POD mStageSize;
   // We can't use a Matrix4x4 here unless we ensure it's a POD type
   float mSittingToStandingTransform[16];
+  uint64_t mLastSubmittedFrameId;
+  bool mLastSubmittedFrameSuccessful;
   uint32_t mPresentingGeneration;
 };
 
 struct VRControllerState
 {
   char mControllerName[kVRControllerNameMaxLen];
 #ifdef MOZILLA_INTERNAL_API
   dom::GamepadHand mHand;
@@ -269,33 +271,33 @@ struct VRLayerEyeRect
 enum class VRLayerType : uint16_t {
   LayerType_None = 0,
   LayerType_2D_Content = 1,
   LayerType_Stereo_Immersive = 2
 };
 
 enum class VRLayerTextureType : uint16_t {
   LayerTextureType_None = 0,
-  LayerTextureType_DirectX = 1,
-  LayerTextureType_OpenGL = 2,
-  LayerTextureType_Vulkan = 3
+  LayerTextureType_D3D10SurfaceDescriptor = 1,
+  LayerTextureType_MacIOSurface = 2
 };
 
 struct VRLayer_2D_Content
 {
   void* mTextureHandle;
   VRLayerTextureType mTextureType;
   uint64_t mFrameId;
 };
 
 struct VRLayer_Stereo_Immersive
 {
   void* mTextureHandle;
   VRLayerTextureType mTextureType;
   uint64_t mFrameId;
+  uint64_t mInputFrameId;
   VRLayerEyeRect mLeftEyeRect;
   VRLayerEyeRect mRightEyeRect;
 };
 
 struct VRLayerState
 {
   VRLayerType type;
   union {
@@ -310,16 +312,17 @@ struct VRBrowserState
   bool shutdown;
 #endif // defined(__ANDROID__)
   VRLayerState layerState[kVRLayerMaxCount];
 };
 
 struct VRSystemState
 {
   uint32_t presentingGeneration;
+  bool enumerationCompleted;
   VRDisplayState displayState;
   VRHMDSensorState sensorState;
   VRControllerState controllerState[kVRControllerMaxCount];
 };
 
 struct VRExternalShmem
 {
   int32_t version;
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -97,102 +97,156 @@ VRDisplayExternal::Refresh()
 VRHMDSensorState
 VRDisplayExternal::GetSensorState()
 {
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
 
   manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState);
 
-//  result.CalcViewMatrices(headToEyeTransforms);
-  mLastSensorState.inputFrameID = mDisplayInfo.mFrameId;
   return mLastSensorState;
 }
 
 void
 VRDisplayExternal::StartPresentation()
 {
   if (mIsPresenting) {
     return;
   }
   mIsPresenting = true;
   mTelemetry.Clear();
   mTelemetry.mPresentationStart = TimeStamp::Now();
 
-  // TODO - Implement this
+  // Indicate that we are ready to start immersive mode
+  VRBrowserState state;
+  memset(&state, 0, sizeof(VRBrowserState));
+  state.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
+  VRManager *vm = VRManager::Get();
+  VRSystemManagerExternal* manager = vm->GetExternalManager();
+  manager->PushState(&state);
+
+  // TODO - Implement telemetry:
 
   // mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames;
 }
 
 void
 VRDisplayExternal::StopPresentation()
 {
   if (!mIsPresenting) {
     return;
   }
+  mIsPresenting = false;
 
-  // TODO - Implement this
+  // Indicate that we have stopped immersive mode
+  VRBrowserState state;
+  memset(&state, 0, sizeof(VRBrowserState));
+  VRManager *vm = VRManager::Get();
+  VRSystemManagerExternal* manager = vm->GetExternalManager();
+  manager->PushState(&state);
+
+  // TODO - Implement telemetry:
 
 /*
-  mIsPresenting = false;
   const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
   Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
   Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
                         duration.ToMilliseconds());
 
   ::vr::Compositor_CumulativeStats stats;
   mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
   const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
                                         mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
   Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
 */
 }
 
+bool
+VRDisplayExternal::PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
+                                        VRLayerTextureType* aTextureType,
+                                        void** aTextureHandle)
+{
+  switch (aTexture.type()) {
 #if defined(XP_WIN)
-
-bool
-VRDisplayExternal::SubmitFrame(ID3D11Texture2D* aSource,
-                             const IntSize& aSize,
-                             const gfx::Rect& aLeftEyeRect,
-                             const gfx::Rect& aRightEyeRect)
-{
-  // FINDME!  Implement this
-  return false;
+    case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
+      const SurfaceDescriptorD3D10& surf = aTexture.get_SurfaceDescriptorD3D10();
+      *aTextureType = VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor;
+      *aTextureHandle = (void *)surf.handle();
+      return true;
+    }
+#elif defined(XP_MACOSX)
+    case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
+      const auto& desc = aTexture.get_SurfaceDescriptorMacIOSurface();
+      RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(desc.surfaceId(),
+                                                              desc.scaleFactor(),
+                                                              !desc.isOpaque());
+      if (!surf) {
+        NS_WARNING("VRDisplayHost::SubmitFrame failed to get a MacIOSurface");
+        return false;
+      }
+      *aTextureType = VRLayerTextureType::LayerTextureType_MacIOSurface;
+      *aTextureHandle = (void *)surf->GetIOSurfacePtr();
+      return true;
+    }
+#endif
+    default: {
+      MOZ_ASSERT(false);
+      return false;
+    }
+  }
 }
 
-#elif defined(XP_MACOSX)
-
 bool
-VRDisplayExternal::SubmitFrame(MacIOSurface* aMacIOSurface,
-                             const IntSize& aSize,
-                             const gfx::Rect& aLeftEyeRect,
-                             const gfx::Rect& aRightEyeRect)
+VRDisplayExternal::SubmitFrame(const layers::SurfaceDescriptor& aTexture,
+                               uint64_t aFrameId,
+                               const gfx::Rect& aLeftEyeRect,
+                               const gfx::Rect& aRightEyeRect)
 {
-  const void* ioSurface = aMacIOSurface->GetIOSurfacePtr();
-  bool result = false;
-  if (ioSurface == nullptr) {
-    NS_WARNING("VRDisplayExternal::SubmitFrame() could not get an IOSurface");
-  } else {
-    // FINDME!  Implement this
+  VRBrowserState state;
+  memset(&state, 0, sizeof(VRBrowserState));
+  state.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
+  VRLayer_Stereo_Immersive& layer = state.layerState[0].layer_stereo_immersive;
+  if (!PopulateLayerTexture(aTexture, &layer.mTextureType, &layer.mTextureHandle)) {
+    return false;
   }
-  return result;
-}
+  layer.mFrameId = aFrameId;
+  layer.mInputFrameId = mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames].inputFrameID;
 
-#elif defined(MOZ_WIDGET_ANDROID)
+  layer.mLeftEyeRect.x = aLeftEyeRect.x;
+  layer.mLeftEyeRect.y = aLeftEyeRect.y;
+  layer.mLeftEyeRect.width = aLeftEyeRect.width;
+  layer.mLeftEyeRect.height = aLeftEyeRect.height;
+  layer.mRightEyeRect.x = aRightEyeRect.x;
+  layer.mRightEyeRect.y = aRightEyeRect.y;
+  layer.mRightEyeRect.width = aRightEyeRect.width;
+  layer.mRightEyeRect.height = aRightEyeRect.height;
+
+  VRManager *vm = VRManager::Get();
+  VRSystemManagerExternal* manager = vm->GetExternalManager();
+  manager->PushState(&state);
 
-bool
-VRDisplayExternal::SubmitFrame(const layers::SurfaceTextureDescriptor& aSurface,
-                               const gfx::Rect& aLeftEyeRect,
-                               const gfx::Rect& aRightEyeRect) {
+  VRDisplayState displayState;
+  memset(&displayState, 0, sizeof(VRDisplayState));
+  while (displayState.mLastSubmittedFrameId < aFrameId) {
+    if (manager->PullState(&displayState)) {
+      if (!displayState.mIsConnected) {
+        // Service has shut down or hardware has been disconnected
+        return false;
+      }
+    }
+#ifdef XP_WIN
+    Sleep(0);
+#else
+    sleep(0);
+#endif
+  }
 
-  return false;
+  return displayState.mLastSubmittedFrameSuccessful;
 }
 
-#endif
-
 VRControllerExternal::VRControllerExternal(dom::GamepadHand aHand, uint32_t aDisplayID,
                                        uint32_t aNumButtons, uint32_t aNumTriggers,
                                        uint32_t aNumAxes, const nsCString& aId)
   : VRControllerHost(VRDeviceType::External, aHand, aDisplayID)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerExternal, VRControllerHost);
 
   VRControllerState& state = mControllerInfo.mControllerState;
@@ -203,18 +257,19 @@ VRControllerExternal::VRControllerExtern
   state.mNumHaptics = kNumExternalHaptcs;
 }
 
 VRControllerExternal::~VRControllerExternal()
 {
   MOZ_COUNT_DTOR_INHERITED(VRControllerExternal, VRControllerHost);
 }
 
-VRSystemManagerExternal::VRSystemManagerExternal()
- : mExternalShmem(nullptr)
+VRSystemManagerExternal::VRSystemManagerExternal(VRExternalShmem* aAPIShmem /* = nullptr*/)
+ : mExternalShmem(aAPIShmem)
+ , mSameProcess(aAPIShmem != nullptr)
 {
 #if defined(XP_MACOSX)
   mShmemFD = 0;
 #elif defined(XP_WIN)
   mShmemFile = NULL;
 #elif defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
   mExternalStructFailed = false;
@@ -318,16 +373,19 @@ VRSystemManagerExternal::CheckForShutdow
     }
   }
 #endif // defined(MOZ_WIDGET_ANDROID)
 }
 
 void
 VRSystemManagerExternal::CloseShmem()
 {
+  if (mSameProcess) {
+    return;
+  }
 #if defined(XP_MACOSX)
   if (mExternalShmem) {
     munmap((void *)mExternalShmem, sizeof(VRExternalShmem));
     mExternalShmem = NULL;
   }
   if (mShmemFD) {
     close(mShmemFD);
   }
@@ -342,25 +400,29 @@ VRSystemManagerExternal::CloseShmem()
     mShmemFile = NULL;
   }
 #elif defined(MOZ_WIDGET_ANDROID)
   mExternalShmem = NULL;
 #endif
 }
 
 /*static*/ already_AddRefed<VRSystemManagerExternal>
-VRSystemManagerExternal::Create()
+VRSystemManagerExternal::Create(VRExternalShmem* aAPIShmem /* = nullptr*/)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!gfxPrefs::VREnabled() || !gfxPrefs::VRExternalEnabled()) {
+  if (!gfxPrefs::VREnabled()) {
     return nullptr;
   }
 
-  RefPtr<VRSystemManagerExternal> manager = new VRSystemManagerExternal();
+  if (!gfxPrefs::VRExternalEnabled() && aAPIShmem == nullptr) {
+    return nullptr;
+  }
+
+  RefPtr<VRSystemManagerExternal> manager = new VRSystemManagerExternal(aAPIShmem);
   return manager.forget();
 }
 
 void
 VRSystemManagerExternal::Destroy()
 {
   Shutdown();
 }
@@ -392,17 +454,27 @@ VRSystemManagerExternal::NotifyVSync()
 
 void
 VRSystemManagerExternal::Enumerate()
 {
   if (mDisplay == nullptr) {
     OpenShmem();
     if (mExternalShmem) {
       VRDisplayState displayState;
-      PullState(&displayState);
+      memset(&displayState, 0, sizeof(VRDisplayState));
+      // We must block until enumeration has completed in order
+      // to signal that the WebVR promise should be resolved at the
+      // right time.
+      while (!PullState(&displayState)) {
+#ifdef XP_WIN
+        Sleep(0);
+#else
+        sleep(0);
+#endif
+      }
       if (displayState.mIsConnected) {
         mDisplay = new VRDisplayExternal(displayState);
       }
     }
   }
 }
 
 bool
@@ -483,34 +555,58 @@ VRSystemManagerExternal::RemoveControlle
   // The controller count is changed, removing the existing gamepads first.
   for (uint32_t i = 0; i < mExternalController.Length(); ++i) {
     RemoveGamepad(i);
   }
   mExternalController.Clear();
   mControllerCount = 0;
 }
 
-void
+bool
 VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState /* = nullptr */)
 {
+  bool success = false;
   MOZ_ASSERT(mExternalShmem);
   if (mExternalShmem) {
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
         memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
         if (aSensorState) {
           memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
         }
         pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
         mDoShutdown = aDisplayState->shutdown;
+        success = mExternalShmem->state.enumerationCompleted;
     }
 #else
     VRExternalShmem tmp;
     memcpy(&tmp, (void *)mExternalShmem, sizeof(VRExternalShmem));
-    if (tmp.generationA == tmp.generationB && tmp.generationA != 0 && tmp.generationA != -1) {
+    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));
       }
+      success = true;
     }
 #endif // defined(MOZ_WIDGET_ANDROID)
   }
+
+  return success;
 }
+
+void
+VRSystemManagerExternal::PushState(VRBrowserState* aBrowserState)
+{
+  MOZ_ASSERT(aBrowserState);
+  MOZ_ASSERT(mExternalShmem);
+  if (mExternalShmem) {
+#if defined(MOZ_WIDGET_ANDROID)
+    if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
+      memcpy((void *)&(mExternalShmem->browserState), aBrowserState, sizeof(VRBrowserState));
+      pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
+    }
+#else
+    mExternalShmem->browserGenerationA++;
+    memcpy((void *)&(mExternalShmem->browserState), (void *)aBrowserState, sizeof(VRBrowserState));
+    mExternalShmem->browserGenerationB++;
+#endif // defined(MOZ_WIDGET_ANDROID)
+  }
+}
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -28,42 +28,37 @@ class VRThread;
 namespace impl {
 
 class VRDisplayExternal : public VRDisplayHost
 {
 public:
   void ZeroSensor() override;
 
 protected:
-  virtual VRHMDSensorState GetSensorState() override;
-  virtual void StartPresentation() override;
-  virtual void StopPresentation() override;
-#if defined(XP_WIN)
-  virtual bool SubmitFrame(ID3D11Texture2D* aSource,
-                           const IntSize& aSize,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) override;
-#elif defined(XP_MACOSX)
-  virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
-                           const IntSize& aSize,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) override;
-#elif defined(MOZ_WIDGET_ANDROID)
-  bool SubmitFrame(const layers::SurfaceTextureDescriptor& aSurface,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) override;
-#endif
+  VRHMDSensorState GetSensorState() override;
+  void StartPresentation() override;
+  void StopPresentation() override;
+
+  bool SubmitFrame(const layers::SurfaceDescriptor& aTexture,
+                   uint64_t aFrameId,
+                   const gfx::Rect& aLeftEyeRect,
+                   const gfx::Rect& aRightEyeRect) override;
 
 public:
   explicit VRDisplayExternal(const VRDisplayState& aDisplayState);
   void Refresh();
 protected:
   virtual ~VRDisplayExternal();
   void Destroy();
 
+private:
+  bool PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
+                            VRLayerTextureType* aTextureType,
+                            void** aTextureHandle);
+
   VRTelemetry mTelemetry;
   bool mIsPresenting;
   VRHMDSensorState mLastSensorState;
 };
 
 class VRControllerExternal : public VRControllerHost
 {
 public:
@@ -75,17 +70,17 @@ protected:
   virtual ~VRControllerExternal();
 };
 
 } // namespace impl
 
 class VRSystemManagerExternal : public VRSystemManager
 {
 public:
-  static already_AddRefed<VRSystemManagerExternal> Create();
+  static already_AddRefed<VRSystemManagerExternal> Create(VRExternalShmem* aAPIShmem = nullptr);
 
   virtual void Destroy() override;
   virtual void Shutdown() override;
   virtual void NotifyVSync() override;
   virtual void Enumerate() override;
   virtual bool ShouldInhibitEnumeration() override;
   virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
   virtual bool GetIsPresenting() override;
@@ -95,36 +90,38 @@ public:
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx,
                              uint32_t aHapticIndex,
                              double aIntensity,
                              double aDuration,
                              const VRManagerPromise& aPromise) override;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
-  void PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState = nullptr);
+  bool PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState = nullptr);
+  void PushState(VRBrowserState* aBrowserState);
 
 protected:
-  VRSystemManagerExternal();
+  explicit VRSystemManagerExternal(VRExternalShmem* aAPIShmem = nullptr);
   virtual ~VRSystemManagerExternal();
 
 private:
   // there can only be one
   RefPtr<impl::VRDisplayExternal> mDisplay;
   nsTArray<RefPtr<impl::VRControllerExternal>> mExternalController;
 #if defined(XP_MACOSX)
   int mShmemFD;
 #elif defined(XP_WIN)
   HANDLE mShmemFile;
 #elif defined(MOZ_WIDGET_ANDROID)
   bool mDoShutdown;
   bool mExternalStructFailed;
 #endif
 
   volatile VRExternalShmem* mExternalShmem;
+  bool mSameProcess;
 
   void OpenShmem();
   void CloseShmem();
   void CheckForShutdown();
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/gfxVROSVR.cpp
+++ b/gfx/vr/gfxVROSVR.cpp
@@ -206,23 +206,23 @@ SetFromTanRadians(double left, double ri
   fovInfo.upDegrees = atan(top) * 180.0 / M_PI;
   fovInfo.downDegrees = atan(bottom) * 180.0 / M_PI;
   return fovInfo;
 }
 
 VRDisplayOSVR::VRDisplayOSVR(OSVR_ClientContext* context,
                          OSVR_ClientInterface* iface,
                          OSVR_DisplayConfig* display)
-  : VRDisplayHost(VRDeviceType::OSVR)
+  : VRDisplayLocal(VRDeviceType::OSVR)
   , m_ctx(context)
   , m_iface(iface)
   , m_display(display)
 {
 
-  MOZ_COUNT_CTOR_INHERITED(VRDisplayOSVR, VRDisplayHost);
+  MOZ_COUNT_CTOR_INHERITED(VRDisplayOSVR, VRDisplayLocal);
 
   VRDisplayState& state = mDisplayInfo.mDisplayState;
   state.mIsConnected = true;
   strncpy(state.mDisplayName, "OSVR HMD", kVRDisplayNameMaxLen);
   state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
   state.mCapabilityFlags =
     VRDisplayCapabilityFlags::Cap_Orientation | VRDisplayCapabilityFlags::Cap_Position;
 
--- a/gfx/vr/gfxVROSVR.h
+++ b/gfx/vr/gfxVROSVR.h
@@ -9,29 +9,29 @@
 
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/EnumeratedArray.h"
 
-#include "VRDisplayHost.h"
+#include "VRDisplayLocal.h"
 
 #include <osvr/ClientKit/ClientKitC.h>
 #include <osvr/ClientKit/DisplayC.h>
 
 #if defined(XP_MACOSX)
 class MacIOSurface;
 #endif
 namespace mozilla {
 namespace gfx {
 namespace impl {
 
-class VRDisplayOSVR : public VRDisplayHost
+class VRDisplayOSVR : public VRDisplayLocal
 {
 public:
   void ZeroSensor() override;
 
 protected:
   VRHMDSensorState GetSensorState() override;
   virtual void StartPresentation() override;
   virtual void StopPresentation() override;
@@ -52,17 +52,17 @@ public:
   explicit VRDisplayOSVR(OSVR_ClientContext* context,
                          OSVR_ClientInterface* iface,
                          OSVR_DisplayConfig* display);
 
 protected:
   virtual ~VRDisplayOSVR()
   {
     Destroy();
-    MOZ_COUNT_DTOR_INHERITED(VRDisplayOSVR, VRDisplayHost);
+    MOZ_COUNT_DTOR_INHERITED(VRDisplayOSVR, VRDisplayLocal);
   }
   void Destroy();
 
   OSVR_ClientContext* m_ctx;
   OSVR_ClientInterface* m_iface;
   OSVR_DisplayConfig* m_display;
 
   gfx::Matrix4x4 mHeadToEye[2];
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -790,28 +790,28 @@ VROculusSession::UnloadOvrLib()
 {
   if (mOvrLib) {
     PR_UnloadLibrary(mOvrLib);
     mOvrLib = nullptr;
   }
 }
 
 VRDisplayOculus::VRDisplayOculus(VROculusSession* aSession)
-  : VRDisplayHost(VRDeviceType::Oculus)
+  : VRDisplayLocal(VRDeviceType::Oculus)
   , mSession(aSession)
   , mQuadVS(nullptr)
   , mQuadPS(nullptr)
   , mLinearSamplerState(nullptr)
   , mVSConstantBuffer(nullptr)
   , mPSConstantBuffer(nullptr)
   , mVertexBuffer(nullptr)
   , mInputLayout(nullptr)
   , mEyeHeight(OVR_DEFAULT_EYE_HEIGHT)
 {
-  MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
+  MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayLocal);
   VRDisplayState& state = mDisplayInfo.mDisplayState;
   strncpy(state.mDisplayName, "Oculus VR HMD", kVRDisplayNameMaxLen);
   state.mIsConnected = true;
   state.mIsMounted = false;
 
   mDesc = ovr_GetHmdDesc(aSession->Get());
 
   state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
@@ -847,17 +847,17 @@ VRDisplayOculus::VRDisplayOculus(VROculu
   state.mEyeResolution.height = std::max(texSize[VRDisplayState::Eye_Left].h, texSize[VRDisplayState::Eye_Right].h);
 
   UpdateEyeParameters();
   UpdateStageParameters();
 }
 
 VRDisplayOculus::~VRDisplayOculus() {
   Destroy();
-  MOZ_COUNT_DTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
+  MOZ_COUNT_DTOR_INHERITED(VRDisplayOculus, VRDisplayLocal);
 }
 
 void
 VRDisplayOculus::Destroy()
 {
   StopPresentation();
   mSession = nullptr;
 }
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -10,17 +10,17 @@
 #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 "VRDisplayLocal.h"
 #include "ovr_capi_dynamic.h"
 
 struct ID3D11Device;
 
 namespace mozilla {
 namespace layers {
 class CompositingRenderTargetD3D11;
 struct VertexShaderConstants;
@@ -85,17 +85,17 @@ private:
   bool StartSession();
   void StopSession();
   bool StartLib(ovrInitFlags aFlags);
   void StopLib();
   bool StartRendering();
   void StopRendering();
 };
 
-class VRDisplayOculus : public VRDisplayHost
+class VRDisplayOculus : public VRDisplayLocal
 {
 public:
   void ZeroSensor() override;
 
 protected:
   virtual VRHMDSensorState GetSensorState() override;
   virtual void StartPresentation() override;
   virtual void StopPresentation() override;
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -45,23 +45,23 @@ using namespace mozilla::dom;
 #define BTN_MASK_FROM_ID(_id) \
   ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
 
 static const uint32_t kNumOpenVRHaptcs = 1;
 
 VRDisplayOpenVR::VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
                                  ::vr::IVRChaperone *aVRChaperone,
                                  ::vr::IVRCompositor *aVRCompositor)
-  : VRDisplayHost(VRDeviceType::OpenVR)
+  : VRDisplayLocal(VRDeviceType::OpenVR)
   , mVRSystem(aVRSystem)
   , mVRChaperone(aVRChaperone)
   , mVRCompositor(aVRCompositor)
   , mIsPresenting(false)
 {
-  MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
+  MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
 
   VRDisplayState& state = mDisplayInfo.mDisplayState;
 
   strncpy(state.mDisplayName, "OpenVR HMD", kVRDisplayNameMaxLen);
   state.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
   state.mIsMounted = false;
   state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
                            VRDisplayCapabilityFlags::Cap_Orientation |
@@ -93,17 +93,17 @@ VRDisplayOpenVR::VRDisplayOpenVR(::vr::I
   }
   UpdateEyeParameters();
   UpdateStageParameters();
 }
 
 VRDisplayOpenVR::~VRDisplayOpenVR()
 {
   Destroy();
-  MOZ_COUNT_DTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
+  MOZ_COUNT_DTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
 }
 
 void
 VRDisplayOpenVR::Destroy()
 {
   StopPresentation();
   ::vr::VR_Shutdown();
 }
@@ -345,21 +345,21 @@ VRDisplayOpenVR::StopPresentation()
   ::vr::Compositor_CumulativeStats stats;
   mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
   const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
                                         mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
   Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
 }
 
 bool
-VRDisplayOpenVR::SubmitFrame(void* aTextureHandle,
-                             ::vr::ETextureType aTextureType,
-                             const IntSize& aSize,
-                             const gfx::Rect& aLeftEyeRect,
-                             const gfx::Rect& aRightEyeRect)
+VRDisplayOpenVR::SubmitFrameOpenVRHandle(void* aTextureHandle,
+                                         ::vr::ETextureType aTextureType,
+                                         const IntSize& aSize,
+                                         const gfx::Rect& aLeftEyeRect,
+                                         const gfx::Rect& aRightEyeRect)
 {
   MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
   if (!mIsPresenting) {
     return false;
   }
 
   ::vr::Texture_t tex;
   tex.handle = aTextureHandle;
@@ -395,37 +395,37 @@ VRDisplayOpenVR::SubmitFrame(void* aText
 #if defined(XP_WIN)
 
 bool
 VRDisplayOpenVR::SubmitFrame(ID3D11Texture2D* aSource,
                              const IntSize& aSize,
                              const gfx::Rect& aLeftEyeRect,
                              const gfx::Rect& aRightEyeRect)
 {
-  return SubmitFrame((void *)aSource,
-                     ::vr::ETextureType::TextureType_DirectX,
-                     aSize, aLeftEyeRect, aRightEyeRect);
+  return SubmitFrameOpenVRHandle((void *)aSource,
+                                 ::vr::ETextureType::TextureType_DirectX,
+                                 aSize, aLeftEyeRect, aRightEyeRect);
 }
 
 #elif defined(XP_MACOSX)
 
 bool
 VRDisplayOpenVR::SubmitFrame(MacIOSurface* aMacIOSurface,
                              const IntSize& aSize,
                              const gfx::Rect& aLeftEyeRect,
                              const gfx::Rect& aRightEyeRect)
 {
   const void* ioSurface = aMacIOSurface->GetIOSurfacePtr();
   bool result = false;
   if (ioSurface == nullptr) {
     NS_WARNING("VRDisplayOpenVR::SubmitFrame() could not get an IOSurface");
   } else {
-    result = SubmitFrame((void *)ioSurface,
-                         ::vr::ETextureType::TextureType_IOSurface,
-                         aSize, aLeftEyeRect, aRightEyeRect);
+    result = SubmitFrameOpenVRHandle((void *)ioSurface,
+                                     ::vr::ETextureType::TextureType_IOSurface,
+                                     aSize, aLeftEyeRect, aRightEyeRect);
   }
   return result;
 }
 
 #endif
 
 VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID,
                                        uint32_t aNumButtons, uint32_t aNumTriggers,
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -12,28 +12,28 @@
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/EnumeratedArray.h"
 
 #include "openvr.h"
 #include "gfxVR.h"
-#include "VRDisplayHost.h"
+#include "VRDisplayLocal.h"
 
 #if defined(XP_MACOSX)
 class MacIOSurface;
 #endif
 namespace mozilla {
 namespace gfx {
 class VRThread;
 
 namespace impl {
 
-class VRDisplayOpenVR : public VRDisplayHost
+class VRDisplayOpenVR : public VRDisplayLocal
 {
 public:
   void ZeroSensor() override;
   bool GetIsHmdPresent();
 
 protected:
   virtual VRHMDSensorState GetSensorState() override;
   virtual void StartPresentation() override;
@@ -65,21 +65,21 @@ protected:
   ::vr::IVRCompositor *mVRCompositor;
 
   VRTelemetry mTelemetry;
   bool mIsPresenting;
   bool mIsHmdPresent;
 
   void UpdateStageParameters();
   void UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms = nullptr);
-  bool SubmitFrame(void* aTextureHandle,
-                   ::vr::ETextureType aTextureType,
-                   const IntSize& aSize,
-                   const gfx::Rect& aLeftEyeRect,
-                   const gfx::Rect& aRightEyeRect);
+  bool SubmitFrameOpenVRHandle(void* aTextureHandle,
+                               ::vr::ETextureType aTextureType,
+                               const IntSize& aSize,
+                               const gfx::Rect& aLeftEyeRect,
+                               const gfx::Rect& aRightEyeRect);
 };
 
 class VRControllerOpenVR : public VRControllerHost
 {
 public:
   explicit VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID, uint32_t aNumButtons,
                               uint32_t aNumTriggers, uint32_t aNumAxes,
                               const nsCString& aId);
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -45,21 +45,21 @@ static const uint64_t kPuppetButtonMask[
   8
 };
 static const uint32_t kNumPuppetButtonMask = sizeof(kPuppetButtonMask) /
                                              sizeof(uint64_t);
 static const uint32_t kNumPuppetAxis = 3;
 static const uint32_t kNumPuppetHaptcs = 1;
 
 VRDisplayPuppet::VRDisplayPuppet()
- : VRDisplayHost(VRDeviceType::Puppet)
+ : VRDisplayLocal(VRDeviceType::Puppet)
  , mIsPresenting(false)
  , mSensorState{}
 {
-  MOZ_COUNT_CTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
+  MOZ_COUNT_CTOR_INHERITED(VRDisplayPuppet, VRDisplayLocal);
 
   VRDisplayState& state = mDisplayInfo.mDisplayState;
   strncpy(state.mDisplayName, "Puppet HMD", kVRDisplayNameMaxLen);
   state.mIsConnected = true;
   state.mIsMounted = false;
   state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
                            VRDisplayCapabilityFlags::Cap_Orientation |
                            VRDisplayCapabilityFlags::Cap_Position |
@@ -118,17 +118,17 @@ VRDisplayPuppet::VRDisplayPuppet()
   mSensorState.position[2] = 0.0f;
   mSensorState.linearVelocity[0] = 0.0f;
   mSensorState.linearVelocity[1] = 0.0f;
   mSensorState.linearVelocity[2] = 0.0f;
 }
 
 VRDisplayPuppet::~VRDisplayPuppet()
 {
-  MOZ_COUNT_DTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
+  MOZ_COUNT_DTOR_INHERITED(VRDisplayPuppet, VRDisplayLocal);
 }
 
 void
 VRDisplayPuppet::SetDisplayInfo(const VRDisplayInfo& aDisplayInfo)
 {
   // We are only interested in the eye and mount info of the display info.
   VRDisplayState& state = mDisplayInfo.mDisplayState;
   state.mEyeResolution = aDisplayInfo.mDisplayState.mEyeResolution;
--- a/gfx/vr/gfxVRPuppet.h
+++ b/gfx/vr/gfxVRPuppet.h
@@ -7,26 +7,26 @@
 #ifndef GFX_VR_PUPPET_H
 #define GFX_VR_PUPPET_H
 
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include "nsRefPtrHashtable.h"
 
 #include "gfxVR.h"
-#include "VRDisplayHost.h"
+#include "VRDisplayLocal.h"
 
 #if defined(XP_MACOSX)
 class MacIOSurface;
 #endif
 namespace mozilla {
 namespace gfx {
 namespace impl {
 
-class VRDisplayPuppet : public VRDisplayHost
+class VRDisplayPuppet : public VRDisplayLocal
 {
 public:
   void SetDisplayInfo(const VRDisplayInfo& aDisplayInfo);
   void SetSensorState(const VRHMDSensorState& aSensorState);
   void ZeroSensor() override;
 
 protected:
   virtual VRHMDSensorState GetSensorState() override;
--- a/gfx/vr/ipc/VRMessageUtils.h
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -42,16 +42,17 @@ struct ParamTraits<mozilla::gfx::VRDispl
     WriteParam(aMsg, displayName);
     WriteParam(aMsg, aParam.mCapabilityFlags);
     WriteParam(aMsg, aParam.mEyeResolution.width);
     WriteParam(aMsg, aParam.mEyeResolution.height);
     WriteParam(aMsg, aParam.mIsConnected);
     WriteParam(aMsg, aParam.mIsMounted);
     WriteParam(aMsg, aParam.mStageSize.width);
     WriteParam(aMsg, aParam.mStageSize.height);
+    WriteParam(aMsg, aParam.mLastSubmittedFrameId);
     WriteParam(aMsg, aParam.mPresentingGeneration);
     for (int i = 0; i < 16; i++) {
       // TODO - Should probably memcpy the whole array or
       // convert Maxtrix4x4 to a POD type and use it
       // instead
       WriteParam(aMsg, aParam.mSittingToStandingTransform[i]);
     }
     for (int i = 0; i < mozilla::gfx::VRDisplayState::NumEyes; i++) {
@@ -68,16 +69,17 @@ struct ParamTraits<mozilla::gfx::VRDispl
     if (!ReadParam(aMsg, aIter, &(displayName)) ||
         !ReadParam(aMsg, aIter, &(aResult->mCapabilityFlags)) ||
         !ReadParam(aMsg, aIter, &(aResult->mEyeResolution.width)) ||
         !ReadParam(aMsg, aIter, &(aResult->mEyeResolution.height)) ||
         !ReadParam(aMsg, aIter, &(aResult->mIsConnected)) ||
         !ReadParam(aMsg, aIter, &(aResult->mIsMounted)) ||
         !ReadParam(aMsg, aIter, &(aResult->mStageSize.width)) ||
         !ReadParam(aMsg, aIter, &(aResult->mStageSize.height)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mLastSubmittedFrameId)) ||
         !ReadParam(aMsg, aIter, &(aResult->mPresentingGeneration))) {
       return false;
     }
     for (int i = 0; i < 16; i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mSittingToStandingTransform[i]))) {
         return false;
       }
     }
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -42,16 +42,17 @@ if CONFIG['OS_TARGET'] != 'Android':
 
 # VRDisplayHost includes MacIOSurface.h which includes Mac headers
 # which define Size and Points types in the root namespace that
 # often conflict with our own types.
 SOURCES += [
     'gfxVRExternal.cpp',
     'gfxVRPuppet.cpp',
     'VRDisplayHost.cpp',
+    'VRDisplayLocal.cpp',
 ]
 
 # Build OpenVR on Windows, Linux, and macOS desktop targets
 if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
     DIRS += [
         'openvr',
     ]
     SOURCES += [