Bug 1430038 - (wip)Part 4: Connect VR process with Vsync thread; draft
authorDaosheng Mu <daoshengmu@gmail.com>
Fri, 27 Jul 2018 10:44:57 -0700
changeset 823606 ddb303a718a7847e11e2d85b4627a0ffdfc0c43a
parent 823605 20931104cb93fac388b1d5891e4d2cdd2abe5ee6
child 824862 9508e15b61fbf39e89ad9ce087c63a23336b7b1e
push id117744
push userbmo:dmu@mozilla.com
push dateFri, 27 Jul 2018 17:47:03 +0000
bugs1430038
milestone63.0a1
Bug 1430038 - (wip)Part 4: Connect VR process with Vsync thread; MozReview-Commit-ID: M6o4WudrMg
gfx/thebes/VsyncSource.cpp
gfx/thebes/VsyncSource.h
gfx/vr/VRManager.cpp
gfx/vr/ipc/PVR.ipdl
gfx/vr/ipc/VRChild.cpp
gfx/vr/ipc/VRChild.h
gfx/vr/ipc/VRParent.cpp
gfx/vr/ipc/VRParent.h
gfx/vr/ipc/VRProcessChild.cpp
gfx/vr/ipc/VRProcessChild.h
gfx/vr/ipc/VRProcessManager.cpp
gfx/vr/ipc/VRProcessParent.cpp
gfx/vr/ipc/VRProcessParent.h
gfx/vr/service/VRService.h
widget/VsyncDispatcher.cpp
widget/VsyncDispatcher.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
--- a/gfx/thebes/VsyncSource.cpp
+++ b/gfx/thebes/VsyncSource.cpp
@@ -26,16 +26,35 @@ void
 VsyncSource::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
   // See also AddCompositorVsyncDispatcher().
   GetGlobalDisplay().RemoveCompositorVsyncDispatcher(aCompositorVsyncDispatcher);
 }
 
+void
+VsyncSource::AddVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+  // Just use the global display until we have enough information to get the
+  // corresponding display for compositor.
+  GetGlobalDisplay().AddVRVsyncDispatcher(aVRVsyncDispatcher);
+}
+
+void
+VsyncSource::RemoveVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+  // See also AddCompositorVsyncDispatcher().
+  GetGlobalDisplay().RemoveVRVsyncDispatcher(aVRVsyncDispatcher);
+}
+
 RefPtr<RefreshTimerVsyncDispatcher>
 VsyncSource::GetRefreshTimerVsyncDispatcher()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   // See also AddCompositorVsyncDispatcher().
   return GetGlobalDisplay().GetRefreshTimerVsyncDispatcher();
 }
 
@@ -48,28 +67,34 @@ VsyncSource::Display::Display()
 }
 
 VsyncSource::Display::~Display()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MutexAutoLock lock(mDispatcherLock);
   mRefreshTimerVsyncDispatcher = nullptr;
   mCompositorVsyncDispatchers.Clear();
+  mVRVsyncDispatchers.Clear();
 }
 
 void
 VsyncSource::Display::NotifyVsync(TimeStamp aVsyncTimestamp)
 {
   // Called on the vsync thread
   MutexAutoLock lock(mDispatcherLock);
 
   for (size_t i = 0; i < mCompositorVsyncDispatchers.Length(); i++) {
     mCompositorVsyncDispatchers[i]->NotifyVsync(aVsyncTimestamp);
   }
 
+  // TODO: We should make mVRVsyncDispatchers to be an attribute instead of an array.
+  for (size_t i = 0; i < mVRVsyncDispatchers.Length(); i++) {
+    mVRVsyncDispatchers[i]->NotifyVsync(aVsyncTimestamp);
+  }
+
   mRefreshTimerVsyncDispatcher->NotifyVsync(aVsyncTimestamp);
 }
 
 TimeDuration
 VsyncSource::Display::GetVsyncRate()
 {
   // If hardware queries fail / are unsupported, we have to just guess.
   return TimeDuration::FromMilliseconds(1000.0 / 60.0);
@@ -99,16 +124,44 @@ VsyncSource::Display::RemoveCompositorVs
     if (mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
       mCompositorVsyncDispatchers.RemoveElement(aCompositorVsyncDispatcher);
     }
   }
   UpdateVsyncStatus();
 }
 
 void
+VsyncSource::Display::AddVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aVRVsyncDispatcher);
+  { // scope lock
+    MutexAutoLock lock(mDispatcherLock);
+    if (!mVRVsyncDispatchers.Contains(aVRVsyncDispatcher)) {
+      mVRVsyncDispatchers.AppendElement(aVRVsyncDispatcher);
+    }
+  }
+  UpdateVsyncStatus();
+}
+
+void
+VsyncSource::Display::RemoveVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aVRVsyncDispatcher);
+  { // Scope lock
+    MutexAutoLock lock(mDispatcherLock);
+    if (mVRVsyncDispatchers.Contains(aVRVsyncDispatcher)) {
+      mVRVsyncDispatchers.RemoveElement(aVRVsyncDispatcher);
+    }
+  }
+  UpdateVsyncStatus();
+}
+
+void
 VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mRefreshTimerNeedsVsync = aEnable;
   UpdateVsyncStatus();
 }
 
 void
@@ -118,17 +171,18 @@ VsyncSource::Display::UpdateVsyncStatus(
   // WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
   // NotifyVsync grabs a lock to dispatch vsync events
   // When disabling vsync, we wait for the underlying thread to stop on some platforms
   // We can deadlock if we wait for the underlying vsync thread to stop
   // while the vsync thread is in NotifyVsync.
   bool enableVsync = false;
   { // scope lock
     MutexAutoLock lock(mDispatcherLock);
-    enableVsync = !mCompositorVsyncDispatchers.IsEmpty() || mRefreshTimerNeedsVsync;
+    enableVsync = !mCompositorVsyncDispatchers.IsEmpty() || mRefreshTimerNeedsVsync ||
+                  !mVRVsyncDispatchers.IsEmpty();
   }
 
   if (enableVsync) {
     EnableVsync();
   } else {
     DisableVsync();
   }
 
--- a/gfx/thebes/VsyncSource.h
+++ b/gfx/thebes/VsyncSource.h
@@ -10,16 +10,17 @@
 #include "mozilla/RefPtr.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 class RefreshTimerVsyncDispatcher;
 class CompositorVsyncDispatcher;
+class VRVsyncDispatcher;
 
 namespace gfx {
 
 // Controls how and when to enable/disable vsync. Lives as long as the
 // gfxPlatform does on the parent process
 class VsyncSource
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncSource)
@@ -43,37 +44,43 @@ public:
       // All platforms should normalize to the vsync that just occured.
       // Large parts of Gecko assume TimeStamps should not be in the future such as animations
       virtual void NotifyVsync(TimeStamp aVsyncTimestamp);
 
       RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
 
       void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
       void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
+      void AddVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher);
+      void RemoveVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher);
       void NotifyRefreshTimerVsyncStatus(bool aEnable);
       virtual TimeDuration GetVsyncRate();
 
       // These should all only be called on the main thread
       virtual void EnableVsync() = 0;
       virtual void DisableVsync() = 0;
       virtual bool IsVsyncEnabled() = 0;
       virtual void Shutdown() = 0;
 
     private:
       void UpdateVsyncStatus();
 
       Mutex mDispatcherLock;
       bool mRefreshTimerNeedsVsync;
       nsTArray<RefPtr<CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
       RefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
+      nsTArray<RefPtr<VRVsyncDispatcher>> mVRVsyncDispatchers;
   };
 
   void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
   void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
 
+  void AddVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher);
+  void RemoveVRVsyncDispatcher(VRVsyncDispatcher* aVRVsyncDispatcher);
+
   RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
   virtual Display& GetGlobalDisplay() = 0; // Works across all displays
   void Shutdown();
 
 protected:
   virtual ~VsyncSource() {}
 };
 
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -26,16 +26,18 @@
 #include "gfxVROpenVR.h"
 #include "gfxVROSVR.h"
 #endif
 
 #include "gfxVRPuppet.h"
 #include "ipc/VRLayerParent.h"
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
 #include "service/VRService.h"
+#include "VRProcessManager.h"
+#include "VRContentChild.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 
 namespace mozilla {
@@ -76,22 +78,32 @@ VRManager::VRManager()
    *
    * OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS,
    * to support everyone else.
    */
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   // The VR Service accesses all hardware from a separate process
   // and replaces the other VRSystemManager when enabled.
-  mVRService = VRService::Create();
-  if (mVRService) {
-    mExternalManager = VRSystemManagerExternal::Create(mVRService->GetAPIShmem());
-  }
-  if (mExternalManager) {
-    mManagers.AppendElement(mExternalManager);
+  // if (gfxPrefs::VRProcessEnabled()) {
+  //   ipc::Endpoint<PVRContentChild> vrContentBridge;
+  //   VRProcessManager* vpm = VRProcessManager::Get();
+  //   bool opened = vpm->CreateContentBridges(OtherId(), &vrContentBridge);
+  //   MOZ_ASSERT(opened);
+
+  //   Unused << SendInitVR(std::move(vrContentBridge));
+  // }
+  if (!gfxPrefs::VRProcessEnabled()) {
+    mVRService = VRService::Create();
+    if (mVRService) {
+      mExternalManager = VRSystemManagerExternal::Create(mVRService->GetAPIShmem());
+    }
+    if (mExternalManager) {
+      mManagers.AppendElement(mExternalManager);
+    }
   }
 #endif
 
   if (!mExternalManager) {
     mExternalManager = VRSystemManagerExternal::Create();
     if (mExternalManager) {
       mManagers.AppendElement(mExternalManager);
     }
--- a/gfx/vr/ipc/PVR.ipdl
+++ b/gfx/vr/ipc/PVR.ipdl
@@ -1,21 +1,26 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+
 include protocol PVRContent;
 
 namespace mozilla {
 namespace gfx {
 
 async protocol PVR
 {
 
 parent:
   async NewContentVRManager(Endpoint<PVRContentParent> endpoint);
   async InitVRManager(Endpoint<PVRContentParent> endpoint);
+  async NotifyVsync(TimeStamp vsyncTimeStamp);
 
+child:
+  async ObserveVsync();
 };
 
 } // namespace gfx
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/vr/ipc/VRChild.cpp
+++ b/gfx/vr/ipc/VRChild.cpp
@@ -2,31 +2,98 @@
 /* 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 "VRChild.h"
 #include "VRProcessParent.h"
 
+#include "mozilla/SystemGroup.h"
+#include "mozilla/VsyncDispatcher.h"
+
 namespace mozilla {
 namespace gfx {
 
-VRChild::VRChild(VRProcessParent* aHost)
- : mHost(aHost)
+VRChild::VRChild(VRProcessParent* aHost,
+                 RefPtr<VRVsyncDispatcher> aVsyncDispatcher,
+                 RefPtr<VRVsyncObserver> aVsyncObserver)
+ : mHost(aHost),
+   mVsyncDispatcher(aVsyncDispatcher),
+   mVsyncObserver(aVsyncObserver)
 {
+  MOZ_ASSERT(XRE_IsParentProcess());
+  mVsyncObserver->SetBridge(this);
 }
 
 void
 VRChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mHost->OnChannelClosed();
   XRE_ShutdownChildProcess();
 }
 
+mozilla::ipc::IPCResult
+VRChild::RecvObserveVsync()
+{
+  // Setup vsync observe.
+  mVsyncDispatcher->SetVRVsyncObserver(mVsyncObserver);
+  return IPC_OK();
+}
+
+class VRNotifyVsyncTask : public Runnable
+{
+public:
+	VRNotifyVsyncTask(VRChild* aVsyncBridge,
+                      TimeStamp aTimeStamp)
+    : Runnable("VRNotifyVsyncTask")
+    , mVsyncBridge(aVsyncBridge)
+    , mTimeStamp(aTimeStamp)
+  {}
+
+  NS_IMETHOD Run() override {
+    mVsyncBridge->SendNotifyVsync(mTimeStamp);
+    return NS_OK;
+  }
+
+private:
+  VRChild* mVsyncBridge;
+  TimeStamp mTimeStamp;
+};
+
+VRVsyncObserver::VRVsyncObserver()
+{
+  // MOZ_ALWAYS_SUCCEEDS(NS_NewNamedThread("VRVsyncThread",
+  //                     getter_AddRefs(mVsyncThread)));
+}
+
+VRVsyncObserver::~VRVsyncObserver()
+{
+  // if (NS_IsMainThread()) {
+  //   mVsyncThread->AsyncShutdown();
+  // } else {
+  //   SystemGroup::Dispatch(TaskCategory::Other, NewRunnableMethod(
+  //     "nsIThread::AsyncShutdown", mVsyncThread, &nsIThread::AsyncShutdown));
+  // }
+}
+
+bool VRVsyncObserver::NotifyVsync(TimeStamp aVsyncTimestamp)
+{
+  //Vsync notifications should only arrive on the vsync thread.
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  // Notify Vsync event to VR process.
+  // TODO: Create a Vsync thread
+  RefPtr<VRNotifyVsyncTask> task = new VRNotifyVsyncTask(mVRChild, aVsyncTimestamp);
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(task.forget()));
+  //MOZ_ALWAYS_SUCCEEDS(mVsyncThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL));
+  return true;
+}
+
 class DeferredDeleteVRChild : public Runnable
 {
 public:
   explicit DeferredDeleteVRChild(UniquePtr<VRChild>&& aChild)
     : Runnable("gfx::DeferredDeleteVRChild")
     , mChild(std::move(aChild))
   {
   }
--- a/gfx/vr/ipc/VRChild.h
+++ b/gfx/vr/ipc/VRChild.h
@@ -3,34 +3,60 @@
 /* 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_CHILD_H
 #define GFX_VR_CHILD_H
 
 #include "mozilla/gfx/PVRChild.h"
+#include "mozilla/VsyncDispatcher.h"
 
 namespace mozilla {
 namespace gfx {
 
 class VRProcessParent;
+class VRChild;
+
+class VRVsyncObserver : public VsyncObserver
+{
+  // typedef gfx::VsyncBridgeChild VsyncBridgeChild;
+
+  public:
+    VRVsyncObserver();
+    ~VRVsyncObserver();
+
+    void SetBridge(VRChild* aVRChild) {
+      mVRChild = aVRChild;
+    }
+
+    virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
+
+ private:
+    VRChild* mVRChild;
+   // RefPtr<nsIThread> mVsyncThread;
+    // layers::LayersId mRootLayerTreeId;
+};
 
 class VRChild final : public PVRChild {
 
 public:
-  explicit VRChild(VRProcessParent* aHost);
-  ~VRChild() {}
+  explicit VRChild(VRProcessParent* aHost,
+                   RefPtr<VRVsyncDispatcher> aVsyncDispatcher,
+                   RefPtr<VRVsyncObserver> aVsyncObserver);
+  ~VRChild() = default;
 
   static void Destroy(UniquePtr<VRChild>&& aChild);
 
 protected:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+  virtual mozilla::ipc::IPCResult RecvObserveVsync() override;
 
 private:
   VRProcessParent* mHost;
-
+  RefPtr<VRVsyncDispatcher> mVsyncDispatcher;
+  RefPtr<VRVsyncObserver> mVsyncObserver;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif  // GFX_VR_CHILD_H
\ No newline at end of file
--- a/gfx/vr/ipc/VRParent.cpp
+++ b/gfx/vr/ipc/VRParent.cpp
@@ -31,16 +31,24 @@ VRParent::RecvNewContentVRManager(Endpoi
 
 mozilla::ipc::IPCResult
 VRParent::RecvInitVRManager(Endpoint<PVRContentParent>&& aEndpoint)
 {
   VRContentParent::CreateForVRProcess(std::move(aEndpoint));
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+VRParent::RecvNotifyVsync(const TimeStamp& aVsyncTimeStamp)
+{
+  TimeStamp test = aVsyncTimeStamp;
+ // MOZ_ASSERT(false);
+  return IPC_OK();
+}
+
 void
 VRParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Shutting down VR process early due to a crash!");
     ProcessChild::QuickExit();
   }
 
@@ -65,13 +73,15 @@ VRParent::Init(base::ProcessId aParentPi
   if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
     // We need to quit this process if the buildID doesn't match the parent's.
     // This can occur when an update occurred in the background.
     ProcessChild::QuickExit();
   }
 
   gfxPrefs::GetSingleton();
 
+  // Sending reply msg to the parent process.
+  SendObserveVsync();
   return true;
 }
 
 } // namespace gfx
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/vr/ipc/VRParent.h
+++ b/gfx/vr/ipc/VRParent.h
@@ -22,16 +22,17 @@ public:
             const char* aParentBuildID,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
 protected:
   virtual mozilla::ipc::IPCResult RecvNewContentVRManager(Endpoint<PVRContentParent>&& aEndpoint) override;
   virtual mozilla::ipc::IPCResult RecvInitVRManager(Endpoint<PVRContentParent>&& aEndpoint) override;
+  virtual mozilla::ipc::IPCResult RecvNotifyVsync(const TimeStamp& aVsyncTimeStamp) override;
 
 private:
   VRContentParent* mVRContent;
 
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/ipc/VRProcessChild.cpp
+++ b/gfx/vr/ipc/VRProcessChild.cpp
@@ -11,16 +11,19 @@
 #include "mozilla/sandboxTarget.h"
 #endif
 
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using mozilla::ipc::IOThreadChild;
 
+// We need to a scheduler to notify its self.
+
+
 VRProcessChild::VRProcessChild(ProcessId aParentPid)
   : ProcessChild(aParentPid)
 #if defined(aParentPid)
   , mVR(nullptr)
 #endif
 {
 }
 
@@ -42,16 +45,27 @@ VRProcessChild::Init(int aArgc, char* aA
       parentBuildID = aArgv[i + 1];
     }
   }
 
   mVR.Init(ParentPid(), parentBuildID,
            IOThreadChild::message_loop(),
            IOThreadChild::channel());
 
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  // Create VR service.
+  mVRService = VRService::Create();
+  if (mVRService) {
+    mExternalManager = VRSystemManagerExternal::Create(mVRService->GetAPIShmem());
+  }
+  // if (mExternalManager) {
+  //   mManagers.AppendElement(mExternalManager);
+  // }
+#endif // defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+
   return true;
 }
 
 void
 VRProcessChild::CleanUp()
 {
   NS_ShutdownXPCOM(nullptr);
 }
\ No newline at end of file
--- a/gfx/vr/ipc/VRProcessChild.h
+++ b/gfx/vr/ipc/VRProcessChild.h
@@ -7,16 +7,20 @@
 #define GFX_VR_PROCESS_CHILD_H
 
 #include "mozilla/ipc/ProcessChild.h"
 #include "VRParent.h"
 
 
 namespace mozilla {
 namespace gfx {
+class VRSystemManagerExternal;
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+class VRService;
+#endif
 
 /**
  * Contains the VRChild object that facilitates IPC communication to/from
  * the instance of the VR library that is run in this process.
  */
 class VRProcessChild final : public mozilla::ipc::ProcessChild
 {
 protected:
@@ -29,14 +33,18 @@ public:
   // ProcessChild functions.
   bool Init(int aArgc, char* aArgv[]) override;
   void CleanUp() override;
 
 private:
   DISALLOW_COPY_AND_ASSIGN(VRProcessChild);
 
   VRParent mVR;
+  RefPtr<VRSystemManagerExternal> mExternalManager;
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  RefPtr<VRService> mVRService;
+#endif
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_PROCESS_CHILD_H */
\ No newline at end of file
--- a/gfx/vr/ipc/VRProcessManager.cpp
+++ b/gfx/vr/ipc/VRProcessManager.cpp
@@ -91,18 +91,17 @@ VRProcessManager::DestroyProcess()
   mProcess->Shutdown();
   mProcess = nullptr;
 }
 
 bool
 VRProcessManager::CreateContentBridges(base::ProcessId aOtherProcess,
                                        mozilla::ipc::Endpoint<PVRContentChild>* aOutVRBridge)
 {
-  if (!CreateContentVRManager(aOtherProcess, aOutVRBridge))
-  {
+  if (!CreateContentVRManager(aOtherProcess, aOutVRBridge)) {
     return false;
   }
   return true;
 }
 
 bool
 VRProcessManager::CreateContentVRManager(base::ProcessId aOtherProcess,
                                          mozilla::ipc::Endpoint<PVRContentChild>* aOutEndpoint)
--- a/gfx/vr/ipc/VRProcessParent.cpp
+++ b/gfx/vr/ipc/VRProcessParent.cpp
@@ -25,16 +25,38 @@ using std::vector;
 using std::string;
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
 // using namespace layers;
 namespace gfx {
 
+//class VRVsyncObserver : public VsyncObserver
+//{
+//  // typedef gfx::VsyncBridgeChild VsyncBridgeChild;
+//
+//  public:
+//    VRVsyncObserver() {}
+//
+//    virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override {
+//      // Vsync notifications should only arrive on the vsync thread.
+//      MOZ_ASSERT(XRE_IsParentProcess());
+//      MOZ_ASSERT(!NS_IsMainThread());
+//
+//      // TODO: Notify to VR process.
+//      // mVsyncBridge->NotifyVsync(aTimeStamp, mRootLayerTreeId);
+//      return true;
+//    }
+//
+////  private:
+////     RefPtr<VsyncBridgeChild> mVsyncBridge;
+////     layers::LayersId mRootLayerTreeId;
+//};
+
 VRProcessParent::VRProcessParent()
   : GeckoChildProcessHost(GeckoProcessType_VR),
     mTaskFactory(this),
     mChannelClosed(false)
 {
   MOZ_COUNT_CTOR(VRProcessParent);
 }
 
@@ -109,25 +131,39 @@ VRProcessParent::DestroyProcess()
 {
   mLaunchThread->Dispatch(NewRunnableFunction("DestroyProcessRunnable", DelayedDeleteSubprocess, this));
 }
 
 void
 VRProcessParent::InitAfterConnect(bool aSucceeded)
 {
   if (aSucceeded) {
-    mVRChild = MakeUnique<VRChild>(this);
+    RefPtr<VRVsyncDispatcher> dispatcher = new VRVsyncDispatcher(); //aWidget->GetVRVsyncDispatcher();
+    RefPtr<VRVsyncObserver> observer = new VRVsyncObserver();
+    
+    mVRChild = MakeUnique<VRChild>(this, dispatcher, observer);
 
     DebugOnly<bool> rv =
       mVRChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
     MOZ_ASSERT(rv);
   }
 }
 
 void
+VRProcessParent::KillHard(const char* aReason)
+{
+  ProcessHandle handle = GetChildProcessHandle();
+  if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
+    NS_WARNING("failed to kill subprocess!");
+  }
+
+  SetAlreadyDead();
+}
+
+void
 VRProcessParent::OnChannelError()
 {
   MOZ_ASSERT(false, "VR process channel error.");
 }
 
 void
 VRProcessParent::OnChannelConnected(int32_t peer_pid)
 {
--- a/gfx/vr/ipc/VRProcessParent.h
+++ b/gfx/vr/ipc/VRProcessParent.h
@@ -38,16 +38,17 @@ public:
   VRChild* GetActor() const {
     return mVRChild.get();
   }
 
 private:
   DISALLOW_COPY_AND_ASSIGN(VRProcessParent);
 
   void InitAfterConnect(bool aSucceeded);
+  void KillHard(const char* aReason);
 
   UniquePtr<VRChild> mVRChild;
   mozilla::ipc::TaskFactory<VRProcessParent> mTaskFactory;
   nsCOMPtr<nsIThread> mLaunchThread;
   bool mChannelClosed;
 };
 
 } // namespace gfx
--- a/gfx/vr/service/VRService.h
+++ b/gfx/vr/service/VRService.h
@@ -6,17 +6,16 @@
 
 #ifndef GFX_VR_SERVICE_VRSERVICE_H
 #define GFX_VR_SERVICE_VRSERVICE_H
 
 #include "mozilla/Atomics.h"
 
 #include "moz_external_vr.h"
 
-#include <thread>
 namespace base {
 class Thread;
 } // namespace base
 namespace mozilla {
 namespace gfx {
 
 class VRSession;
 
--- a/widget/VsyncDispatcher.cpp
+++ b/widget/VsyncDispatcher.cpp
@@ -90,16 +90,95 @@ CompositorVsyncDispatcher::Shutdown()
   ObserveVsync(false);
   mDidShutdown = true;
   { // scope lock
     MutexAutoLock lock(mCompositorObserverLock);
     mCompositorVsyncObserver = nullptr;
   }
 }
 
+VRVsyncDispatcher::VRVsyncDispatcher()
+  : mVRObserverLock("VRObserverLock")
+  , mDidShutdown(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+VRVsyncDispatcher::~VRVsyncDispatcher()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  // We auto remove this vsync dispatcher from the vsync source in the nsBaseWidget
+}
+
+void
+VRVsyncDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp)
+{
+  // In vsync thread
+
+  MutexAutoLock lock(mVRObserverLock);
+  if (mVRVsyncObserver) {
+    mVRVsyncObserver->NotifyVsync(aVsyncTimestamp);
+  }
+}
+
+void
+VRVsyncDispatcher::ObserveVsync(bool aEnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_IsParentProcess());
+  if (mDidShutdown) {
+    return;
+  }
+
+  if (aEnable) {
+    gfxPlatform::GetPlatform()->GetHardwareVsync()->AddVRVsyncDispatcher(this);
+  } else {
+    gfxPlatform::GetPlatform()->GetHardwareVsync()->RemoveVRVsyncDispatcher(this);
+  }
+}
+
+void
+VRVsyncDispatcher::SetVRVsyncObserver(VsyncObserver* aVsyncObserver)
+{
+  // When remote vr or running gtests, vsync observation is
+  // initiated on the main thread. Otherwise, it is initiated from the compositor
+  // thread.
+  MOZ_ASSERT(NS_IsMainThread());
+
+  { // scope lock
+    MutexAutoLock lock(mVRObserverLock);
+    mVRVsyncObserver = aVsyncObserver;
+  }
+
+  bool observeVsync = aVsyncObserver != nullptr;
+  nsCOMPtr<nsIRunnable> vsyncControl =
+    NewRunnableMethod<bool>("VRVsyncDispatcher::ObserveVsync",
+                            this,
+                            &VRVsyncDispatcher::ObserveVsync,
+                            observeVsync);
+  NS_DispatchToMainThread(vsyncControl);
+}
+
+void
+VRVsyncDispatcher::Shutdown()
+{
+  // Need to explicitly remove VRVsyncDispatcher when the nsBaseWidget shuts down.
+  // Otherwise, we would get dead vsync notifications between when the nsBaseWidget
+  // shuts down and the CompositorBridgeParent shuts down.
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+  ObserveVsync(false);
+  mDidShutdown = true;
+  { // scope lock
+    MutexAutoLock lock(mVRObserverLock);
+    mVRVsyncObserver = nullptr;
+  }
+}
+
 RefreshTimerVsyncDispatcher::RefreshTimerVsyncDispatcher()
   : mRefreshTimersLock("RefreshTimers lock")
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 RefreshTimerVsyncDispatcher::~RefreshTimerVsyncDispatcher()
--- a/widget/VsyncDispatcher.h
+++ b/widget/VsyncDispatcher.h
@@ -61,16 +61,47 @@ private:
   virtual ~CompositorVsyncDispatcher();
   void ObserveVsync(bool aEnable);
 
   Mutex mCompositorObserverLock;
   RefPtr<VsyncObserver> mCompositorVsyncObserver;
   bool mDidShutdown;
 };
 
+// Used to dispatch vsync events in the parent process to VR process.
+//
+// When the VR is out-of-process, the VRWidgetDelegate owns
+// the vsync dispatcher instead. The widget receives vsync observer/unobserve
+// commands via IPDL, and uses this to attach a VRWidgetVsyncObserver.
+// This observer forwards vsync notifications (on the vsync thread) to a
+// dedicated vsync I/O thread, which then forwards the notification to the
+// compositor thread in the VR process.
+class VRVsyncDispatcher final
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRVsyncDispatcher)
+
+public:
+  VRVsyncDispatcher();
+
+  // Called on the vsync thread when a hardware vsync occurs
+  void NotifyVsync(TimeStamp aVsyncTimestamp);
+
+  // VR vsync observers must be added/removed on the VR thread
+  void SetVRVsyncObserver(VsyncObserver* aVsyncObserver);
+  void Shutdown();
+
+private:
+  virtual ~VRVsyncDispatcher();
+  void ObserveVsync(bool aEnable);
+
+  Mutex mVRObserverLock;
+  RefPtr<VsyncObserver> mVRVsyncObserver;
+  bool mDidShutdown;
+};
+
 // Dispatch vsync event to ipc actor parent and chrome RefreshTimer.
 class RefreshTimerVsyncDispatcher final
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefreshTimerVsyncDispatcher)
 
 public:
   RefreshTimerVsyncDispatcher();
 
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -292,16 +292,28 @@ void nsBaseWidget::DestroyCompositor()
 
     // XXX CompositorBridgeChild and CompositorBridgeParent might be re-created in
     // ClientLayerManager destructor. See bug 1133426.
     RefPtr<CompositorSession> session = mCompositorSession.forget();
     session->Shutdown();
   }
 }
 
+void
+nsBaseWidget::DestroyVR()
+{
+  if (mVRVsyncDispatcher) {
+    MOZ_ASSERT(mVRVsyncDispatcherLock.get());
+
+    MutexAutoLock lock(*mVRVsyncDispatcherLock.get());
+    mVRVsyncDispatcher->Shutdown();
+    mVRVsyncDispatcher = nullptr;
+  }
+}
+
 // This prevents the layer manager from starting a new transaction during
 // shutdown.
 void
 nsBaseWidget::RevokeTransactionIdAllocator()
 {
   if (!mLayerManager) {
     return;
   }
@@ -1275,16 +1287,40 @@ nsBaseWidget::GetDocument() const
   if (mWidgetListener) {
     if (nsIPresShell* presShell = mWidgetListener->GetPresShell()) {
       return presShell->GetDocument();
     }
   }
   return nullptr;
 }
 
+void nsBaseWidget::CreateVRVsyncDispatcher()
+{
+  // Parent directly listens to the vsync source whereas
+  // child process communicate via IPC
+  // Should be called AFTER VRProcess is initialized
+  if (XRE_IsParentProcess()) {
+    if (!mVRVsyncDispatcherLock) {
+      mVRVsyncDispatcherLock = MakeUnique<Mutex>("mVRVsyncDispatcherLock");
+    }
+    MutexAutoLock lock(*mVRVsyncDispatcherLock.get());
+    mVRVsyncDispatcher = new VRVsyncDispatcher();
+  }
+}
+
+already_AddRefed<VRVsyncDispatcher>
+nsBaseWidget::GetVRVsyncDispatcher()
+{
+  MOZ_ASSERT(mVRVsyncDispatcherLock.get());
+
+  MutexAutoLock lock(*mVRVsyncDispatcherLock.get());
+  RefPtr<VRVsyncDispatcher> dispatcher = mVRVsyncDispatcher;
+  return dispatcher.forget();
+}
+
 void nsBaseWidget::CreateCompositorVsyncDispatcher()
 {
   // Parent directly listens to the vsync source whereas
   // child process communicate via IPC
   // Should be called AFTER gfxPlatform is initialized
   if (XRE_IsParentProcess()) {
     if (!mCompositorVsyncDispatcherLock) {
       mCompositorVsyncDispatcherLock = MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -33,16 +33,17 @@ const mozilla::gfx::SurfaceFormat kScrol
   mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32;
 #endif
 
 class nsIContent;
 class gfxContext;
 
 namespace mozilla {
 class CompositorVsyncDispatcher;
+class VRVsyncDispatcher;
 class LiveResizeListener;
 
 #ifdef ACCESSIBILITY
 namespace a11y {
 class Accessible;
 }
 #endif
 
@@ -211,16 +212,18 @@ public:
   // returned.
   void NotifyCompositorSessionLost(mozilla::layers::CompositorSession* aSession);
 
   already_AddRefed<mozilla::CompositorVsyncDispatcher> GetCompositorVsyncDispatcher();
   void            CreateCompositorVsyncDispatcher();
   virtual void            CreateCompositor();
   virtual void            CreateCompositor(int aWidth, int aHeight);
   virtual void            SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {}
+  void            CreateVRVsyncDispatcher();
+  already_AddRefed<mozilla::VRVsyncDispatcher> GetVRVsyncDispatcher();
   virtual void            PrepareWindowEffects() override {}
   virtual void            UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override {}
   virtual void            SetModal(bool aModal) override {}
   virtual uint32_t        GetMaxTouchPoints() const override;
   virtual void            SetWindowClass(const nsAString& xulWinType)
                             override {}
   virtual nsresult        SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect>& aRects, bool aIntersectWithExisting) override;
   // Return whether this widget interprets parameters to Move and Resize APIs
@@ -663,32 +666,36 @@ protected:
    *
    * When this function returns, the compositor should not be
    * able to access the opengl context anymore.
    * It is safe to call it several times if platform implementations
    * require the compositor to be destroyed before ~nsBaseWidget is
    * reached (This is the case with gtk2 for instance).
    */
   virtual void DestroyCompositor();
+  void DestroyVR();
   void DestroyLayerManager();
   void ReleaseContentController();
   void RevokeTransactionIdAllocator();
 
   void FreeShutdownObserver();
 
   nsIWidgetListener* mWidgetListener;
   nsIWidgetListener* mAttachedWidgetListener;
   nsIWidgetListener* mPreviouslyAttachedWidgetListener;
   RefPtr<LayerManager> mLayerManager;
   RefPtr<CompositorSession> mCompositorSession;
   RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
 
   mozilla::UniquePtr<mozilla::Mutex> mCompositorVsyncDispatcherLock;
   RefPtr<mozilla::CompositorVsyncDispatcher> mCompositorVsyncDispatcher;
 
+  mozilla::UniquePtr<mozilla::Mutex> mVRVsyncDispatcherLock;
+  RefPtr<mozilla::VRVsyncDispatcher> mVRVsyncDispatcher;
+
   RefPtr<IAPZCTreeManager> mAPZC;
   RefPtr<GeckoContentController> mRootContentController;
   RefPtr<APZEventState> mAPZEventState;
   SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;
   RefPtr<WidgetShutdownObserver> mShutdownObserver;
   RefPtr<TextEventDispatcher> mTextEventDispatcher;
   nsCursor          mCursor;
   nsBorderStyle     mBorderStyle;