Bug 1469967 - Implement immersive mode for gfxVRExternal r=rbarker
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Tue, 08 May 2018 11:31:28 -0700
changeset 477454 f918ad3d1296b31808da0b810757df4f0889414c
parent 477427 a0c124cdf70fae2889fa93ac3c0d08f4a0b4fe68
child 477455 f966dedaa07cc97f0d2f28bd6414577e1767300d
push id9385
push userdluca@mozilla.com
push dateFri, 22 Jun 2018 15:47:18 +0000
treeherdermozilla-beta@82a9a1027e2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarker
bugs1469967
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1469967 - Implement immersive mode for gfxVRExternal r=rbarker - 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 += [