Add a top-level protocol for sending vsync messages to the GPU process. (bug 1285625 part 1, r=billm)
authorDavid Anderson <danderson@mozilla.com>
Tue, 19 Jul 2016 11:56:06 -0700
changeset 345770 de2f89a5b4c3f821c37b230ef99dffa0b7325474
parent 345769 19947fb6ef54f487e50883b5885b2a1017ce0780
child 345771 522249786c4c5cd027df51e2369f74366ea3374f
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1285625
milestone50.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
Add a top-level protocol for sending vsync messages to the GPU process. (bug 1285625 part 1, r=billm)
gfx/ipc/GPUChild.h
gfx/ipc/GPUParent.cpp
gfx/ipc/GPUParent.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
gfx/ipc/PGPU.ipdl
gfx/ipc/PVsyncBridge.ipdl
gfx/ipc/VsyncBridgeChild.cpp
gfx/ipc/VsyncBridgeChild.h
gfx/ipc/VsyncBridgeParent.cpp
gfx/ipc/VsyncBridgeParent.h
gfx/ipc/VsyncIOThreadHolder.cpp
gfx/ipc/VsyncIOThreadHolder.h
gfx/ipc/moz.build
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -1,18 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* 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 _include_mozilla_gfx_ipc_GPUChild_h_
 #define _include_mozilla_gfx_ipc_GPUChild_h_
 
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/PGPUChild.h"
-#include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 namespace gfx {
 
 class GPUProcessHost;
 
 class GPUChild final : public PGPUChild
 {
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* 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 "GPUParent.h"
 #include "gfxConfig.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
+#include "VsyncBridgeParent.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 
 namespace mozilla {
 namespace gfx {
 
@@ -43,17 +44,23 @@ GPUParent::Init(base::ProcessId aParentP
 
 bool
 GPUParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs)
 {
   for (auto setting : prefs) {
     gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
     pref->SetCachedValue(setting.value());
   }
+  return true;
+}
 
+bool
+GPUParent::RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint)
+{
+  VsyncBridgeParent::Start(Move(aVsyncEndpoint));
   return true;
 }
 
 bool
 GPUParent::RecvUpdatePref(const GfxPrefSetting& setting)
 {
   gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
   pref->SetCachedValue(setting.value());
@@ -99,14 +106,17 @@ GPUParent::ActorDestroy(ActorDestroyReas
 
 #ifndef NS_FREE_PERMANENT_DATA
   // No point in going through XPCOM shutdown because we don't keep persistent
   // state. Currently we quick-exit in RecvBeginShutdown so this should be
   // unreachable.
   ProcessChild::QuickExit();
 #endif
 
+  if (mVsyncBridge) {
+    mVsyncBridge->Shutdown();
+  }
   CompositorThreadHolder::Shutdown();
   XRE_ShutdownChildProcess();
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -1,39 +1,46 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* 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 _include_gfx_ipc_GPUParent_h__
 #define _include_gfx_ipc_GPUParent_h__
 
+#include "mozilla/RefPtr.h"
 #include "mozilla/gfx/PGPUParent.h"
 
 namespace mozilla {
 namespace gfx {
 
+class VsyncBridgeParent;
+
 class GPUParent final : public PGPUParent
 {
 public:
   GPUParent();
   ~GPUParent();
 
   bool Init(base::ProcessId aParentPid,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
 
   bool RecvInit(nsTArray<GfxPrefSetting>&& prefs) override;
+  bool RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
   bool RecvUpdatePref(const GfxPrefSetting& pref) override;
   bool RecvNewWidgetCompositor(
     Endpoint<PCompositorBridgeParent>&& aEndpoint,
     const CSSToLayoutDeviceScale& aScale,
     const bool& aUseExternalSurface,
     const IntSize& aSurfaceSize) override;
   bool RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+  RefPtr<VsyncBridgeParent> mVsyncBridge;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_gfx_ipc_GPUParent_h__
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -9,16 +9,18 @@
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/InProcessCompositorSession.h"
 #include "mozilla/layers/RemoteCompositorSession.h"
 #include "mozilla/widget/PlatformWidgetTypes.h"
 #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
 # include "mozilla/widget/CompositorWidgetChild.h"
 #endif
 #include "nsContentUtils.h"
+#include "VsyncBridgeChild.h"
+#include "VsyncIOThreadHolder.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace mozilla::layers;
 
 static StaticAutoPtr<GPUProcessManager> sSingleton;
 
@@ -79,41 +81,45 @@ GPUProcessManager::Observer::Observe(nsI
 void
 GPUProcessManager::OnXPCOMShutdown()
 {
   if (mObserver) {
     nsContentUtils::UnregisterShutdownObserver(mObserver);
     mObserver = nullptr;
   }
 
-  DestroyProcess();
+  CleanShutdown();
 }
 
 void
 GPUProcessManager::EnableGPUProcess()
 {
   if (mProcess) {
     return;
   }
 
+  // Start the Vsync I/O thread so can use it as soon as the process launches.
+  EnsureVsyncIOThread();
+
   // The subprocess is launched asynchronously, so we wait for a callback to
   // acquire the IPDL actor.
   mProcess = new GPUProcessHost(this);
   if (!mProcess->Launch()) {
     DisableGPUProcess("Failed to launch GPU process");
   }
 }
 
 void
 GPUProcessManager::DisableGPUProcess(const char* aMessage)
 {
   gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage);
   gfxCriticalNote << aMessage;
 
   DestroyProcess();
+  ShutdownVsyncIOThread();
 }
 
 void
 GPUProcessManager::EnsureGPUReady()
 {
   if (mProcess && mProcess->IsConnected()) {
     if (!mProcess->WaitForLaunch()) {
       // If this fails, we should have fired OnProcessLaunchComplete and
@@ -131,16 +137,31 @@ GPUProcessManager::OnProcessLaunchComple
 
   if (!mProcess->IsConnected()) {
     DisableGPUProcess("Failed to launch GPU process");
     return;
   }
 
   mGPUChild = mProcess->GetActor();
   mProcessToken = mProcess->GetProcessToken();
+
+  Endpoint<PVsyncBridgeParent> vsyncParent;
+  Endpoint<PVsyncBridgeChild> vsyncChild;
+  nsresult rv = PVsyncBridge::CreateEndpoints(
+    mGPUChild->OtherPid(),
+    base::GetCurrentProcId(),
+    &vsyncParent,
+    &vsyncChild);
+  if (NS_FAILED(rv)) {
+    DisableGPUProcess("Failed to create PVsyncBridge endpoints");
+    return;
+  }
+
+  mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken, Move(vsyncChild));
+  mGPUChild->SendInitVsyncBridge(Move(vsyncParent));
 }
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
@@ -164,26 +185,40 @@ GPUProcessManager::NotifyRemoteActorDest
   // One of the bridged top-level actors for the GPU process has been
   // prematurely terminated, and we're receiving a notification. This
   // can happen if the ActorDestroy for a bridged protocol fires
   // before the ActorDestroy for PGPUChild.
   DestroyProcess();
 }
 
 void
+GPUProcessManager::CleanShutdown()
+{
+  if (!mProcess) {
+    return;
+  }
+
+#ifdef NS_FREE_PERMANENT_DATA
+  mVsyncBridge->Close();
+#endif
+  DestroyProcess();
+}
+
+void
 GPUProcessManager::DestroyProcess()
 {
   if (!mProcess) {
     return;
   }
 
   mProcess->Shutdown();
   mProcessToken = 0;
   mProcess = nullptr;
   mGPUChild = nullptr;
+  mVsyncBridge = nullptr;
 }
 
 RefPtr<CompositorSession>
 GPUProcessManager::CreateTopLevelCompositor(nsIWidget* aWidget,
                                             ClientLayerManager* aLayerManager,
                                             CSSToLayoutDeviceScale aScale,
                                             bool aUseAPZ,
                                             bool aUseExternalSurfaceSize,
@@ -353,10 +388,27 @@ GPUProcessManager::UpdateRemoteContentCo
 {
   return CompositorBridgeParent::UpdateRemoteContentController(
     aLayersId,
     aContentParent,
     aTabId,
     aBrowserParent);
 }
 
+void
+GPUProcessManager::EnsureVsyncIOThread()
+{
+  if (mVsyncIOThread) {
+    return;
+  }
+
+  mVsyncIOThread = new VsyncIOThreadHolder();
+  MOZ_RELEASE_ASSERT(mVsyncIOThread->Start());
+}
+
+void
+GPUProcessManager::ShutdownVsyncIOThread()
+{
+  mVsyncIOThread = nullptr;
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -4,16 +4,17 @@
  * 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 _include_mozilla_gfx_ipc_GPUProcessManager_h_
 #define _include_mozilla_gfx_ipc_GPUProcessManager_h_
 
 #include "base/basictypes.h"
 #include "base/process.h"
 #include "Units.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/gfx/GPUProcessHost.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/TaskFactory.h"
 #include "mozilla/ipc/Transport.h"
 #include "nsIObserverService.h"
 
@@ -35,16 +36,18 @@ class ContentParent;
 class TabParent;
 } // namespace dom
 namespace ipc {
 class GeckoChildProcessHost;
 } // namespace ipc
 namespace gfx {
 
 class GPUChild;
+class VsyncBridgeChild;
+class VsyncIOThreadHolder;
 
 // The GPUProcessManager is a singleton responsible for creating GPU-bound
 // objects that may live in another process. Currently, it provides access
 // to the compositor via CompositorBridgeParent.
 class GPUProcessManager final : public GPUProcessHost::Listener
 {
   typedef layers::APZCTreeManager APZCTreeManager;
   typedef layers::ClientLayerManager ClientLayerManager;
@@ -127,18 +130,22 @@ private:
 
 private:
   GPUProcessManager();
 
   // Permanently disable the GPU process and record a message why.
   void DisableGPUProcess(const char* aMessage);
 
   // Shutdown the GPU process.
+  void CleanShutdown();
   void DestroyProcess();
 
+  void EnsureVsyncIOThread();
+  void ShutdownVsyncIOThread();
+
   RefPtr<CompositorSession> CreateRemoteSession(
     nsIWidget* aWidget,
     ClientLayerManager* aLayerManager,
     const uint64_t& aRootLayerTreeId,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
@@ -156,19 +163,22 @@ private:
 
     GPUProcessManager* mManager;
   };
   friend class Observer;
 
 private:
   RefPtr<Observer> mObserver;
   ipc::TaskFactory<GPUProcessManager> mTaskFactory;
+  RefPtr<VsyncIOThreadHolder> mVsyncIOThread;
   uint64_t mNextLayerTreeId;
 
+  // Fields that are associated with the current GPU process.
   GPUProcessHost* mProcess;
   uint64_t mProcessToken;
   GPUChild* mGPUChild;
+  RefPtr<VsyncBridgeChild> mVsyncBridge;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_GPUProcessManager_h_
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -1,14 +1,15 @@
 /* -*- 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/. */
 
 include protocol PCompositorBridge;
+include protocol PVsyncBridge;
 
 using mozilla::CSSToLayoutDeviceScale from "Units.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
 
 namespace mozilla {
 namespace gfx {
 
 union GfxPrefValue {
@@ -24,16 +25,18 @@ struct GfxPrefSetting {
 };
 
 sync protocol PGPU
 {
 parent:
   // Sent by the UI process to initiate core settings.
   async Init(GfxPrefSetting[] prefs);
 
+  async InitVsyncBridge(Endpoint<PVsyncBridgeParent> vsyncEndpoint);
+
   // Called to update a gfx preference.
   async UpdatePref(GfxPrefSetting pref);
 
   // Create a new top-level compositor.
   async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint,
                             CSSToLayoutDeviceScale scale,
                             bool useExternalSurface,
                             IntSize surfaceSize);
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/PVsyncBridge.ipdl
@@ -0,0 +1,22 @@
+/* -*- 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";
+
+namespace mozilla {
+namespace gfx {
+
+// This protocol only serves one purpose: deliver vsync notifications from a
+// dedicated thread in the UI process to the compositor thread in the
+// compositor process. The child side exists in the UI process, and the
+// parent side in the GPU process.
+sync protocol PVsyncBridge
+{
+parent:
+  async NotifyVsync(TimeStamp vsyncTimeStamp);
+};
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/VsyncBridgeChild.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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 "VsyncBridgeChild.h"
+#include "VsyncIOThreadHolder.h"
+
+namespace mozilla {
+namespace gfx {
+
+VsyncBridgeChild::VsyncBridgeChild(RefPtr<VsyncIOThreadHolder> aThread, const uint64_t& aProcessToken)
+ : mThread(aThread),
+   mLoop(nullptr),
+   mProcessToken(aProcessToken)
+{
+}
+
+VsyncBridgeChild::~VsyncBridgeChild()
+{
+}
+
+/* static */ RefPtr<VsyncBridgeChild>
+VsyncBridgeChild::Create(RefPtr<VsyncIOThreadHolder> aThread,
+                         const uint64_t& aProcessToken,
+                         Endpoint<PVsyncBridgeChild>&& aEndpoint)
+{
+  RefPtr<VsyncBridgeChild> child = new VsyncBridgeChild(aThread, aProcessToken);
+
+  RefPtr<nsIRunnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeChild>&&>(
+    child, &VsyncBridgeChild::Open, Move(aEndpoint));
+  aThread->GetThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL);
+
+  return child;
+}
+
+void
+VsyncBridgeChild::Open(Endpoint<PVsyncBridgeChild>&& aEndpoint)
+{
+  if (!aEndpoint.Bind(this, nullptr)) {
+    // The GPU Process Manager might be gone if we receive ActorDestroy very
+    // late in shutdown.
+    if (GPUProcessManager* gpm = GPUProcessManager::Get())
+      gpm->NotifyRemoteActorDestroyed(mProcessToken);
+    return;
+  }
+
+  mLoop = MessageLoop::current();
+
+  // Last reference is freed in DeallocPVsyncBridgeChild.
+  AddRef();
+}
+
+void
+VsyncBridgeChild::Close()
+{
+  if (MessageLoop::current() != mLoop) {
+    mLoop->PostTask(NewRunnableMethod(this, &VsyncBridgeChild::Close));
+    return;
+  }
+
+  // We clear mProcessToken when the channel is closed.
+  if (!mProcessToken) {
+    return;
+  }
+  PVsyncBridgeChild::Close();
+  mProcessToken = 0;
+}
+
+void
+VsyncBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mProcessToken) {
+    GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+    mProcessToken = 0;
+  }
+}
+
+void
+VsyncBridgeChild::DeallocPVsyncBridgeChild()
+{
+  Release();
+}
+
+void
+VsyncBridgeChild::ProcessingError(Result aCode, const char* aReason)
+{
+  MOZ_RELEASE_ASSERT(aCode != MsgDropped, "Processing error in VsyncBridgeChild");
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/VsyncBridgeChild.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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 include_gfx_ipc_VsyncBridgeChild_h
+#define include_gfx_ipc_VsyncBridgeChild_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/PVsyncBridgeChild.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VsyncIOThreadHolder;
+
+class VsyncBridgeChild final : public PVsyncBridgeChild
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeChild)
+
+  static RefPtr<VsyncBridgeChild> Create(RefPtr<VsyncIOThreadHolder> aThread,
+                                         const uint64_t& aProcessToken,
+                                         Endpoint<PVsyncBridgeChild>&& aEndpoint);
+
+  void Close();
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeallocPVsyncBridgeChild() override;
+  void ProcessingError(Result aCode, const char* aReason) override;
+
+private:
+  VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken);
+  ~VsyncBridgeChild();
+
+  void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint);
+
+private:
+  RefPtr<VsyncIOThreadHolder> mThread;
+  MessageLoop* mLoop;
+  uint64_t mProcessToken;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // include_gfx_ipc_VsyncBridgeChild_h
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/VsyncBridgeParent.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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 "VsyncBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+
+namespace mozilla {
+namespace gfx {
+
+RefPtr<VsyncBridgeParent>
+VsyncBridgeParent::Start(Endpoint<PVsyncBridgeParent>&& aEndpoint)
+{
+  RefPtr<VsyncBridgeParent> parent = new VsyncBridgeParent();
+
+  RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeParent>&&>(
+    parent, &VsyncBridgeParent::Open, Move(aEndpoint));
+  CompositorThreadHolder::Loop()->PostTask(task.forget());
+
+  return parent;
+}
+
+VsyncBridgeParent::VsyncBridgeParent()
+ : mOpen(false)
+{
+  MOZ_COUNT_CTOR(VsyncBridgeParent);
+}
+
+VsyncBridgeParent::~VsyncBridgeParent()
+{
+  MOZ_COUNT_DTOR(VsyncBridgeParent);
+}
+
+void
+VsyncBridgeParent::Open(Endpoint<PVsyncBridgeParent>&& aEndpoint)
+{
+  if (!aEndpoint.Bind(this, nullptr)) {
+    // We can't recover from this.
+    MOZ_CRASH("Failed to bind VsyncBridgeParent to endpoint");
+  }
+  AddRef();
+  mOpen = true;
+}
+
+bool
+VsyncBridgeParent::RecvNotifyVsync(const TimeStamp& vsyncTimeStamp)
+{
+  return true;
+}
+
+void
+VsyncBridgeParent::Shutdown()
+{
+  MessageLoop* ccloop = CompositorThreadHolder::Loop();
+  if (MessageLoop::current() != ccloop) {
+    ccloop->PostTask(NewRunnableMethod(this, &VsyncBridgeParent::Shutdown));
+    return;
+  }
+
+  if (mOpen) {
+    Close();
+    mOpen = false;
+  }
+}
+
+void
+VsyncBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mOpen = false;
+}
+
+void
+VsyncBridgeParent::DeallocPVsyncBridgeParent()
+{
+  Release();
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/VsyncBridgeParent.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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 include_gfx_ipc_VsyncBridgeParent_h
+#define include_gfx_ipc_VsyncBridgeParent_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/PVsyncBridgeParent.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VsyncBridgeParent final : public PVsyncBridgeParent
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeParent)
+
+  static RefPtr<VsyncBridgeParent> Start(Endpoint<PVsyncBridgeParent>&& aEndpoint);
+
+  bool RecvNotifyVsync(const TimeStamp& vsyncTimeStamp) override;
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeallocPVsyncBridgeParent() override;
+
+  void Shutdown();
+
+private:
+  VsyncBridgeParent();
+  ~VsyncBridgeParent();
+
+  void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint);
+
+private:
+  bool mOpen;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // include_gfx_ipc_VsyncBridgeParent_h
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/VsyncIOThreadHolder.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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 "VsyncIOThreadHolder.h"
+
+namespace mozilla {
+namespace gfx {
+
+VsyncIOThreadHolder::VsyncIOThreadHolder()
+{
+}
+
+VsyncIOThreadHolder::~VsyncIOThreadHolder()
+{
+  if (!mThread) {
+    return;
+  }
+
+  NS_DispatchToMainThread(NewRunnableMethod(mThread, &nsIThread::AsyncShutdown));
+}
+
+bool
+VsyncIOThreadHolder::Start()
+{
+  nsresult rv = NS_NewNamedThread("VsyncIOThread", getter_AddRefs(mThread));
+  return NS_SUCCEEDED(rv);
+}
+
+RefPtr<nsIThread>
+VsyncIOThreadHolder::GetThread() const
+{
+  return mThread;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/VsyncIOThreadHolder.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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 mozilla_gfx_ipc_VsyncIOThreadHolder_h
+#define mozilla_gfx_ipc_VsyncIOThreadHolder_h
+
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VsyncIOThreadHolder final
+{
+public:
+  VsyncIOThreadHolder();
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncIOThreadHolder)
+
+  bool Start();
+
+  RefPtr<nsIThread> GetThread() const;
+
+private:
+  ~VsyncIOThreadHolder();
+
+private:
+  RefPtr<nsIThread> mThread;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_ipc_VsyncIOThreadHolder_h
--- a/gfx/ipc/moz.build
+++ b/gfx/ipc/moz.build
@@ -11,16 +11,19 @@ EXPORTS.mozilla += [
 
 EXPORTS.mozilla.gfx += [
     'GPUChild.h',
     'GPUParent.h',
     'GPUProcessHost.h',
     'GPUProcessImpl.h',
     'GPUProcessManager.h',
     'SharedDIB.h',
+    'VsyncBridgeChild.h',
+    'VsyncBridgeParent.h',
+    'VsyncIOThreadHolder.h',
 ]
 
 EXPORTS.mozilla.layers += [
     'CompositorSession.h',
     'InProcessCompositorSession.h',
     'RemoteCompositorSession.h',
 ]
 
@@ -40,21 +43,25 @@ UNIFIED_SOURCES += [
     'GPUChild.cpp',
     'GPUParent.cpp',
     'GPUProcessHost.cpp',
     'GPUProcessImpl.cpp',
     'GPUProcessManager.cpp',
     'InProcessCompositorSession.cpp',
     'RemoteCompositorSession.cpp',
     'SharedDIB.cpp',
+    'VsyncBridgeChild.cpp',
+    'VsyncBridgeParent.cpp',
+    'VsyncIOThreadHolder.cpp',
 ]
 
 IPDL_SOURCES = [
     'GraphicsMessages.ipdlh',
     'PGPU.ipdl',
+    'PVsyncBridge.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
 CXXFLAGS += CONFIG['TK_CFLAGS']