Bug 1510853 - Introduce VsyncId and VsyncEvent for identifying vsyncs without timestamp comparisons. r=jrmuizel
☠☠ backed out by 9b7e80071dec ☠ ☠
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 07 Dec 2018 17:06:11 +0000
changeset 508833 ae190948ad730114399ac7074dbbf1cda2ebe359
parent 508832 0ade0aa77b2f2e04e15b4618c9ef1a74b8739777
child 508834 d1ef6db7fc285d2d914ef98452033fc8517e1914
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1510853
milestone65.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 1510853 - Introduce VsyncId and VsyncEvent for identifying vsyncs without timestamp comparisons. r=jrmuizel MozReview-Commit-ID: 6TO6hYOdJYo Differential Revision: https://phabricator.services.mozilla.com/D13757
gfx/ipc/CompositorWidgetVsyncObserver.cpp
gfx/ipc/CompositorWidgetVsyncObserver.h
gfx/ipc/PVsyncBridge.ipdl
gfx/ipc/VsyncBridgeChild.cpp
gfx/ipc/VsyncBridgeChild.h
gfx/ipc/VsyncBridgeParent.cpp
gfx/ipc/VsyncBridgeParent.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/CompositorVsyncScheduler.cpp
gfx/layers/ipc/CompositorVsyncScheduler.h
gfx/layers/ipc/LayersMessageUtils.h
gfx/tests/gtest/TestVsync.cpp
gfx/thebes/VsyncSource.cpp
gfx/thebes/VsyncSource.h
layout/base/nsRefreshDriver.cpp
layout/ipc/PVsync.ipdl
layout/ipc/VsyncChild.cpp
layout/ipc/VsyncChild.h
layout/ipc/VsyncParent.cpp
layout/ipc/VsyncParent.h
toolkit/recordreplay/ipc/ChildIPC.cpp
widget/VsyncDispatcher.cpp
widget/VsyncDispatcher.h
--- a/gfx/ipc/CompositorWidgetVsyncObserver.cpp
+++ b/gfx/ipc/CompositorWidgetVsyncObserver.cpp
@@ -13,19 +13,19 @@ namespace widget {
 CompositorWidgetVsyncObserver::CompositorWidgetVsyncObserver(
     RefPtr<VsyncBridgeChild> aVsyncBridge,
     const layers::LayersId& aRootLayerTreeId)
     : mVsyncBridge(aVsyncBridge), mRootLayerTreeId(aRootLayerTreeId) {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
 }
 
-bool CompositorWidgetVsyncObserver::NotifyVsync(TimeStamp aTimeStamp) {
+bool CompositorWidgetVsyncObserver::NotifyVsync(const VsyncEvent& aVsync) {
   // Vsync notifications should only arrive on the vsync thread.
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(!NS_IsMainThread());
 
-  mVsyncBridge->NotifyVsync(aTimeStamp, mRootLayerTreeId);
+  mVsyncBridge->NotifyVsync(aVsync, mRootLayerTreeId);
   return true;
 }
 
 }  // namespace widget
 }  // namespace mozilla
--- a/gfx/ipc/CompositorWidgetVsyncObserver.h
+++ b/gfx/ipc/CompositorWidgetVsyncObserver.h
@@ -19,17 +19,17 @@ namespace widget {
 
 class CompositorWidgetVsyncObserver : public VsyncObserver {
   typedef gfx::VsyncBridgeChild VsyncBridgeChild;
 
  public:
   CompositorWidgetVsyncObserver(RefPtr<VsyncBridgeChild> aVsyncBridge,
                                 const layers::LayersId& aRootLayerTreeId);
 
-  bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
+  bool NotifyVsync(const VsyncEvent& aVsync) override;
 
  private:
   RefPtr<VsyncBridgeChild> mVsyncBridge;
   layers::LayersId mRootLayerTreeId;
 };
 
 }  // namespace widget
 }  // namespace mozilla
--- a/gfx/ipc/PVsyncBridge.ipdl
+++ b/gfx/ipc/PVsyncBridge.ipdl
@@ -1,23 +1,24 @@
 /* -*- 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";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using mozilla::VsyncEvent from "mozilla/VsyncDispatcher.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, LayersId layersId);
+  async NotifyVsync(VsyncEvent vsync, LayersId layersId);
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/VsyncBridgeChild.cpp
+++ b/gfx/ipc/VsyncBridgeChild.cpp
@@ -41,57 +41,56 @@ void VsyncBridgeChild::Open(Endpoint<PVs
   mLoop = MessageLoop::current();
 
   // Last reference is freed in DeallocPVsyncBridgeChild.
   AddRef();
 }
 
 class NotifyVsyncTask : public Runnable {
  public:
-  NotifyVsyncTask(RefPtr<VsyncBridgeChild> aVsyncBridge, TimeStamp aTimeStamp,
-                  const layers::LayersId& aLayersId)
+  NotifyVsyncTask(RefPtr<VsyncBridgeChild> aVsyncBridge,
+                  const VsyncEvent& aVsync, const layers::LayersId& aLayersId)
       : Runnable("gfx::NotifyVsyncTask"),
         mVsyncBridge(aVsyncBridge),
-        mTimeStamp(aTimeStamp),
+        mVsync(aVsync),
         mLayersId(aLayersId) {}
 
   NS_IMETHOD Run() override {
-    mVsyncBridge->NotifyVsyncImpl(mTimeStamp, mLayersId);
+    mVsyncBridge->NotifyVsyncImpl(mVsync, mLayersId);
     return NS_OK;
   }
 
  private:
   RefPtr<VsyncBridgeChild> mVsyncBridge;
-  TimeStamp mTimeStamp;
+  VsyncEvent mVsync;
   layers::LayersId mLayersId;
 };
 
 bool VsyncBridgeChild::IsOnVsyncIOThread() const {
   return MessageLoop::current() == mLoop;
 }
 
-void VsyncBridgeChild::NotifyVsync(TimeStamp aTimeStamp,
+void VsyncBridgeChild::NotifyVsync(const VsyncEvent& aVsync,
                                    const layers::LayersId& aLayersId) {
   // This should be on the Vsync thread (not the Vsync I/O thread).
   MOZ_ASSERT(!IsOnVsyncIOThread());
 
-  RefPtr<NotifyVsyncTask> task =
-      new NotifyVsyncTask(this, aTimeStamp, aLayersId);
+  RefPtr<NotifyVsyncTask> task = new NotifyVsyncTask(this, aVsync, aLayersId);
   mLoop->PostTask(task.forget());
 }
 
-void VsyncBridgeChild::NotifyVsyncImpl(TimeStamp aTimeStamp,
+void VsyncBridgeChild::NotifyVsyncImpl(const VsyncEvent& aVsync,
                                        const layers::LayersId& aLayersId) {
   // This should be on the Vsync I/O thread.
   MOZ_ASSERT(IsOnVsyncIOThread());
 
   if (!mProcessToken) {
     return;
   }
-  SendNotifyVsync(aTimeStamp, aLayersId);
+  SendNotifyVsync(aVsync, aLayersId);
 }
 
 void VsyncBridgeChild::Close() {
   if (!IsOnVsyncIOThread()) {
     mLoop->PostTask(NewRunnableMethod("gfx::VsyncBridgeChild::Close", this,
                                       &VsyncBridgeChild::Close));
     return;
   }
--- a/gfx/ipc/VsyncBridgeChild.h
+++ b/gfx/ipc/VsyncBridgeChild.h
@@ -25,27 +25,28 @@ class VsyncBridgeChild final : public PV
       Endpoint<PVsyncBridgeChild>&& aEndpoint);
 
   void Close();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPVsyncBridgeChild() override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
-  void NotifyVsync(TimeStamp aTimeStamp, const layers::LayersId& aLayersId);
+  void NotifyVsync(const VsyncEvent& aVsync, const layers::LayersId& aLayersId);
 
   virtual void HandleFatalError(const char* aMsg) const override;
 
  private:
   VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken);
   ~VsyncBridgeChild();
 
   void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint);
 
-  void NotifyVsyncImpl(TimeStamp aTimeStamp, const layers::LayersId& aLayersId);
+  void NotifyVsyncImpl(const VsyncEvent& aVsync,
+                       const layers::LayersId& aLayersId);
 
   bool IsOnVsyncIOThread() const;
 
  private:
   RefPtr<VsyncIOThreadHolder> mThread;
   MessageLoop* mLoop;
   uint64_t mProcessToken;
 };
--- a/gfx/ipc/VsyncBridgeParent.cpp
+++ b/gfx/ipc/VsyncBridgeParent.cpp
@@ -37,18 +37,18 @@ void VsyncBridgeParent::Open(Endpoint<PV
     // We can't recover from this.
     MOZ_CRASH("Failed to bind VsyncBridgeParent to endpoint");
   }
   AddRef();
   mOpen = true;
 }
 
 mozilla::ipc::IPCResult VsyncBridgeParent::RecvNotifyVsync(
-    const TimeStamp& aTimeStamp, const LayersId& aLayersId) {
-  CompositorBridgeParent::NotifyVsync(aTimeStamp, aLayersId);
+    const VsyncEvent& aVsync, const LayersId& aLayersId) {
+  CompositorBridgeParent::NotifyVsync(aVsync, aLayersId);
   return IPC_OK();
 }
 
 void VsyncBridgeParent::Shutdown() {
   MessageLoop* ccloop = CompositorThreadHolder::Loop();
   if (MessageLoop::current() != ccloop) {
     ccloop->PostTask(NewRunnableMethod("gfx::VsyncBridgeParent::ShutdownImpl",
                                        this, &VsyncBridgeParent::ShutdownImpl));
--- a/gfx/ipc/VsyncBridgeParent.h
+++ b/gfx/ipc/VsyncBridgeParent.h
@@ -18,17 +18,17 @@ namespace gfx {
 
 class VsyncBridgeParent final : public PVsyncBridgeParent {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeParent)
 
   static RefPtr<VsyncBridgeParent> Start(
       Endpoint<PVsyncBridgeParent>&& aEndpoint);
 
-  mozilla::ipc::IPCResult RecvNotifyVsync(const TimeStamp& vsyncTimeStamp,
+  mozilla::ipc::IPCResult RecvNotifyVsync(const VsyncEvent& aVsync,
                                           const LayersId& aLayersId) override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPVsyncBridgeParent() override;
 
   void Shutdown();
 
  private:
   VsyncBridgeParent();
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1567,32 +1567,32 @@ CompositorBridgeParent* CompositorBridge
   if (it == sCompositorMap->end()) {
     return nullptr;
   }
   CompositorBridgeParent* retval = it->second;
   sCompositorMap->erase(it);
   return retval;
 }
 
-void CompositorBridgeParent::NotifyVsync(const TimeStamp& aTimeStamp,
+void CompositorBridgeParent::NotifyVsync(const VsyncEvent& aVsync,
                                          const LayersId& aLayersId) {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   auto it = sIndirectLayerTrees.find(aLayersId);
   if (it == sIndirectLayerTrees.end()) return;
 
   CompositorBridgeParent* cbp = it->second.mParent;
   if (!cbp || !cbp->mWidget) return;
 
   RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver();
   if (!obs) return;
 
-  obs->NotifyVsync(aTimeStamp);
+  obs->NotifyVsync(aVsync);
 }
 
 mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyChildCreated(
     const LayersId& child, CompositorOptions* aOptions) {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   NotifyChildCreated(child);
   *aOptions = mOptions;
   return IPC_OK();
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -36,16 +36,17 @@
 #include "mozilla/layers/MetricsSharingController.h"
 #include "mozilla/layers/PCompositorBridgeParent.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsISupportsImpl.h"
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
 #include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/VsyncDispatcher.h"
 
 class MessageLoop;
 class nsIWidget;
 
 namespace mozilla {
 
 class CancelableRunnable;
 
@@ -388,18 +389,17 @@ class CompositorBridgeParent final : pub
    * Returns a pointer to the CompositorBridgeParent corresponding to the given
    * ID.
    */
   static CompositorBridgeParent* GetCompositorBridgeParent(uint64_t id);
 
   /**
    * Notify the compositor for the given layer tree that vsync has occurred.
    */
-  static void NotifyVsync(const TimeStamp& aTimeStamp,
-                          const LayersId& aLayersId);
+  static void NotifyVsync(const VsyncEvent& aVsync, const LayersId& aLayersId);
 
   /**
    * Set aController as the pan/zoom callback for the subtree referred
    * to by aLayersId.
    *
    * Must run on content main thread.
    */
   static void SetControllerForLayerTree(LayersId aLayersId,
--- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
@@ -43,23 +43,22 @@ namespace layers {
 using namespace mozilla::gfx;
 using namespace std;
 
 CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
     : mMutex("CompositorVsyncScheduler.Observer.Mutex"), mOwner(aOwner) {}
 
 CompositorVsyncScheduler::Observer::~Observer() { MOZ_ASSERT(!mOwner); }
 
-bool CompositorVsyncScheduler::Observer::NotifyVsync(
-    TimeStamp aVsyncTimestamp) {
+bool CompositorVsyncScheduler::Observer::NotifyVsync(const VsyncEvent& aVsync) {
   MutexAutoLock lock(mMutex);
   if (!mOwner) {
     return false;
   }
-  return mOwner->NotifyVsync(aVsyncTimestamp);
+  return mOwner->NotifyVsync(aVsync);
 }
 
 void CompositorVsyncScheduler::Observer::Destroy() {
   MutexAutoLock lock(mMutex);
   mOwner = nullptr;
 }
 
 CompositorVsyncScheduler::CompositorVsyncScheduler(
@@ -162,26 +161,26 @@ void CompositorVsyncScheduler::ScheduleC
       // through the main thread of the UI process. It's possible that
       // we're blocking there waiting on a composite, so schedule an initial
       // one now to get things started.
       PostCompositeTask(TimeStamp::Now());
     }
   }
 }
 
-bool CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp) {
+bool CompositorVsyncScheduler::NotifyVsync(const VsyncEvent& aVsync) {
   // Called from the vsync dispatch thread. When in the GPU Process, that's
   // the same as the compositor thread.
   MOZ_ASSERT_IF(XRE_IsParentProcess(),
                 !CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU,
                 CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(!NS_IsMainThread());
-  PostCompositeTask(aVsyncTimestamp);
-  PostVRTask(aVsyncTimestamp);
+  PostCompositeTask(aVsync.mTime);
+  PostVRTask(aVsync.mTime);
   return true;
 }
 
 void CompositorVsyncScheduler::CancelCurrentCompositeTask() {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
              NS_IsMainThread());
   MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
   if (mCurrentCompositeTask) {
--- a/gfx/layers/ipc/CompositorVsyncScheduler.h
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.h
@@ -43,17 +43,17 @@ class CompositorVsyncScheduler {
   explicit CompositorVsyncScheduler(
       CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
       widget::CompositorWidget* aWidget);
 
   /**
    * Notify this class of a vsync. This will trigger a composite if one is
    * needed. This must be called from the vsync dispatch thread.
    */
-  bool NotifyVsync(TimeStamp aVsyncTimestamp);
+  bool NotifyVsync(const VsyncEvent& aVsync);
 
   /**
    * Do cleanup. This must be called on the compositor thread.
    */
   void Destroy();
 
   /**
    * Notify this class that a composition is needed. This will trigger a
@@ -121,17 +121,17 @@ class CompositorVsyncScheduler {
   void ObserveVsync();
   void UnobserveVsync();
 
   void DispatchVREvents(TimeStamp aVsyncTimestamp);
 
   class Observer final : public VsyncObserver {
    public:
     explicit Observer(CompositorVsyncScheduler* aOwner);
-    virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
+    virtual bool NotifyVsync(const VsyncEvent& aVsync) override;
     void Destroy();
 
    private:
     virtual ~Observer();
 
     Mutex mMutex;
     // Hold raw pointer to avoid mutual reference.
     CompositorVsyncScheduler* mOwner;
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -19,16 +19,17 @@
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FocusTarget.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/KeyboardMap.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/RefCountedShmem.h"
 #include "mozilla/layers/RepaintRequest.h"
+#include "VsyncSource.h"
 #include "mozilla/Move.h"
 
 #include <stdint.h>
 
 #ifdef _MSC_VER
 #pragma warning(disable : 4800)
 #endif
 
@@ -38,16 +39,35 @@ template <>
 struct ParamTraits<mozilla::layers::LayersId>
     : public PlainOldDataSerializer<mozilla::layers::LayersId> {};
 
 template <typename T>
 struct ParamTraits<mozilla::layers::BaseTransactionId<T>>
     : public PlainOldDataSerializer<mozilla::layers::BaseTransactionId<T>> {};
 
 template <>
+struct ParamTraits<mozilla::VsyncId>
+    : public PlainOldDataSerializer<mozilla::VsyncId> {};
+
+template <>
+struct ParamTraits<mozilla::VsyncEvent> {
+  typedef mozilla::VsyncEvent paramType;
+
+  static void Write(Message* msg, const paramType& param) {
+    WriteParam(msg, param.mId);
+    WriteParam(msg, param.mTime);
+  }
+  static bool Read(const Message* msg, PickleIterator* iter,
+                   paramType* result) {
+    return ReadParam(msg, iter, &result->mId) &&
+           ReadParam(msg, iter, &result->mTime);
+  }
+};
+
+template <>
 struct ParamTraits<mozilla::layers::LayersObserverEpoch>
     : public PlainOldDataSerializer<mozilla::layers::LayersObserverEpoch> {};
 
 template <>
 struct ParamTraits<mozilla::layers::LayersBackend>
     : public ContiguousEnumSerializer<
           mozilla::layers::LayersBackend,
           mozilla::layers::LayersBackend::LAYERS_NONE,
--- a/gfx/tests/gtest/TestVsync.cpp
+++ b/gfx/tests/gtest/TestVsync.cpp
@@ -25,17 +25,17 @@ using ::testing::_;
 // Windows 8.1 has intermittents at 50 ms. Raise limit to 5 vsync intervals.
 const int kVsyncTimeoutMS = 80;
 
 class TestVsyncObserver : public VsyncObserver {
  public:
   TestVsyncObserver()
       : mDidGetVsyncNotification(false), mVsyncMonitor("VsyncMonitor") {}
 
-  virtual bool NotifyVsync(TimeStamp aVsyncTimeStamp) override {
+  virtual bool NotifyVsync(const VsyncEvent& aVsync) override {
     MonitorAutoLock lock(mVsyncMonitor);
     mDidGetVsyncNotification = true;
     mVsyncMonitor.Notify();
     return true;
   }
 
   void WaitForVsyncNotification() {
     MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/thebes/VsyncSource.cpp
+++ b/gfx/thebes/VsyncSource.cpp
@@ -50,21 +50,24 @@ VsyncSource::Display::~Display() {
   mRefreshTimerVsyncDispatcher = nullptr;
   mCompositorVsyncDispatchers.Clear();
 }
 
 void VsyncSource::Display::NotifyVsync(TimeStamp aVsyncTimestamp) {
   // Called on the vsync thread
   MutexAutoLock lock(mDispatcherLock);
 
+  mVsyncId = mVsyncId.Next();
+  VsyncEvent event(mVsyncId, aVsyncTimestamp);
+
   for (size_t i = 0; i < mCompositorVsyncDispatchers.Length(); i++) {
-    mCompositorVsyncDispatchers[i]->NotifyVsync(aVsyncTimestamp);
+    mCompositorVsyncDispatchers[i]->NotifyVsync(event);
   }
 
-  mRefreshTimerVsyncDispatcher->NotifyVsync(aVsyncTimestamp);
+  mRefreshTimerVsyncDispatcher->NotifyVsync(event);
 }
 
 TimeDuration VsyncSource::Display::GetVsyncRate() {
   // If hardware queries fail / are unsupported, we have to just guess.
   return TimeDuration::FromMilliseconds(1000.0 / 60.0);
 }
 
 void VsyncSource::Display::AddCompositorVsyncDispatcher(
--- a/gfx/thebes/VsyncSource.h
+++ b/gfx/thebes/VsyncSource.h
@@ -6,22 +6,31 @@
 #ifndef GFX_VSYNCSOURCE_H
 #define GFX_VSYNCSOURCE_H
 
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
 #include "nsISupportsImpl.h"
+#include "mozilla/layers/LayersTypes.h"
 
 namespace mozilla {
 class RefreshTimerVsyncDispatcher;
 class CompositorVsyncDispatcher;
 
+class VsyncIdType {};
+typedef layers::BaseTransactionId<VsyncIdType> VsyncId;
+
+namespace layout {
+class PVsyncChild;
+}
+
 namespace gfx {
+class PVsyncBridgeParent;
 
 // 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)
 
   typedef mozilla::RefreshTimerVsyncDispatcher RefreshTimerVsyncDispatcher;
   typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher;
@@ -61,27 +70,49 @@ class VsyncSource {
 
    private:
     void UpdateVsyncStatus();
 
     Mutex mDispatcherLock;
     bool mRefreshTimerNeedsVsync;
     nsTArray<RefPtr<CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
     RefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
+    VsyncId mVsyncId;
   };
 
   void AddCompositorVsyncDispatcher(
       CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
   void RemoveCompositorVsyncDispatcher(
       CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
 
   RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
   virtual Display& GetGlobalDisplay() = 0;  // Works across all displays
   void Shutdown();
 
  protected:
   virtual ~VsyncSource() {}
 };
 
 }  // namespace gfx
+
+namespace recordreplay {
+namespace child {
+void NotifyVsyncObserver();
+}
+}  // namespace recordreplay
+
+struct VsyncEvent {
+  VsyncId mId;
+  TimeStamp mTime;
+
+ private:
+  VsyncEvent(const VsyncId& aId, const TimeStamp& aTime)
+      : mId(aId), mTime(aTime) {}
+  VsyncEvent() {}
+  friend class gfx::VsyncSource::Display;
+  friend class gfx::PVsyncBridgeParent;
+  friend class layout::PVsyncChild;
+  friend void recordreplay::child::NotifyVsyncObserver();
+};
+
 }  // namespace mozilla
 
 #endif /* GFX_VSYNCSOURCE_H */
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -496,55 +496,55 @@ class VsyncRefreshDriverTimer : public R
 
      private:
       ~ParentProcessVsyncNotifier() {}
       RefPtr<RefreshDriverVsyncObserver> mObserver;
       TimeStamp mVsyncTimestamp;
       static mozilla::Atomic<bool> sHighPriorityEnabled;
     };
 
-    bool NotifyVsync(TimeStamp aVsyncTimestamp) override {
+    bool NotifyVsync(const VsyncEvent& aVsync) override {
       // IMPORTANT: All paths through this method MUST hold a strong ref on
       // |this| for the duration of the TickRefreshDriver callback.
 
       if (!NS_IsMainThread()) {
         MOZ_ASSERT(XRE_IsParentProcess());
         // Compress vsync notifications such that only 1 may run at a time
         // This is so that we don't flood the refresh driver with vsync messages
         // if the main thread is blocked for long periods of time
         {  // scope lock
           MonitorAutoLock lock(mRefreshTickLock);
-          mRecentVsync = aVsyncTimestamp;
+          mRecentVsync = aVsync.mTime;
           if (!mProcessedVsync) {
             return true;
           }
           mProcessedVsync = false;
         }
 
         nsCOMPtr<nsIRunnable> vsyncEvent =
-            new ParentProcessVsyncNotifier(this, aVsyncTimestamp);
+            new ParentProcessVsyncNotifier(this, aVsync.mTime);
         NS_DispatchToMainThread(vsyncEvent);
       } else {
-        mRecentVsync = aVsyncTimestamp;
-        if (!mBlockUntil.IsNull() && mBlockUntil > aVsyncTimestamp) {
+        mRecentVsync = aVsync.mTime;
+        if (!mBlockUntil.IsNull() && mBlockUntil > aVsync.mTime) {
           if (mProcessedVsync) {
             // Re-post vsync update as a normal priority runnable. This way
             // runnables already in normal priority queue get processed.
             mProcessedVsync = false;
             nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<>(
                 "RefreshDriverVsyncObserver::NormalPriorityNotify", this,
                 &RefreshDriverVsyncObserver::NormalPriorityNotify);
             NS_DispatchToMainThread(vsyncEvent);
           }
 
           return true;
         }
 
         RefPtr<RefreshDriverVsyncObserver> kungFuDeathGrip(this);
-        TickRefreshDriver(aVsyncTimestamp);
+        TickRefreshDriver(aVsync.mTime);
       }
 
       return true;
     }
 
     void Shutdown() {
       MOZ_ASSERT(NS_IsMainThread());
       mVsyncRefreshDriverTimer = nullptr;
--- a/layout/ipc/PVsync.ipdl
+++ b/layout/ipc/PVsync.ipdl
@@ -2,32 +2,33 @@
 /* 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 PBackground;
 include "mozilla/layers/LayersMessageUtils.h";
 
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::VsyncEvent from "mozilla/VsyncDispatcher.h";
 
 namespace mozilla {
 namespace layout {
 
 /*
  * The PVsync is a sub-protocol in PBackground and it is used to notify
  * the vsync event from chrome to content process. It also provides the
  * interfaces for content to observe/unobserve vsync event notifications.
  */
 async protocol PVsync
 {
   manager PBackground;
 
 child:
   // Send vsync event from chrome to content process.
-  prio(high) async Notify(TimeStamp aVsyncTimestamp) compress;
+  prio(high) async Notify(VsyncEvent aVsync) compress;
 
   // Send the vsync rate to the content process.
   async VsyncRate(float aVsyncRate);
 
 parent:
   // Content process use these messages to acquire the vsync event.
   async Observe();
   async Unobserve();
--- a/layout/ipc/VsyncChild.cpp
+++ b/layout/ipc/VsyncChild.cpp
@@ -46,29 +46,28 @@ void VsyncChild::ActorDestroy(ActorDestr
   mIsShutdown = true;
   mObserver = nullptr;
 
   if (recordreplay::IsRecordingOrReplaying()) {
     recordreplay::child::SetVsyncObserver(nullptr);
   }
 }
 
-mozilla::ipc::IPCResult VsyncChild::RecvNotify(
-    const TimeStamp& aVsyncTimestamp) {
+mozilla::ipc::IPCResult VsyncChild::RecvNotify(const VsyncEvent& aVsync) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mIsShutdown);
 
   SchedulerGroup::MarkVsyncRan();
   if (mObservingVsync && mObserver) {
     if (recordreplay::IsRecordingOrReplaying() &&
         !recordreplay::child::OnVsync()) {
       return IPC_OK();
     }
 
-    mObserver->NotifyVsync(aVsyncTimestamp);
+    mObserver->NotifyVsync(aVsync);
   }
   return IPC_OK();
 }
 
 void VsyncChild::SetVsyncObserver(VsyncObserver* aVsyncObserver) {
   MOZ_ASSERT(NS_IsMainThread());
   mObserver = aVsyncObserver;
 
--- a/layout/ipc/VsyncChild.h
+++ b/layout/ipc/VsyncChild.h
@@ -47,18 +47,17 @@ class VsyncChild final : public PVsyncCh
   // TimeDuration::Forever() if mVsyncRate hasn't been set by calling
   // GetVsyncRate.
   TimeDuration VsyncRate();
 
  private:
   VsyncChild();
   virtual ~VsyncChild();
 
-  virtual mozilla::ipc::IPCResult RecvNotify(
-      const TimeStamp& aVsyncTimestamp) override;
+  virtual mozilla::ipc::IPCResult RecvNotify(const VsyncEvent& aVsync) override;
   virtual mozilla::ipc::IPCResult RecvVsyncRate(
       const float& aVsyncRate) override;
   virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
 
   bool mObservingVsync;
   bool mIsShutdown;
 
   // The content side vsync observer.
--- a/layout/ipc/VsyncParent.cpp
+++ b/layout/ipc/VsyncParent.cpp
@@ -37,37 +37,37 @@ VsyncParent::VsyncParent()
   AssertIsOnBackgroundThread();
 }
 
 VsyncParent::~VsyncParent() {
   // Since we use NS_INLINE_DECL_THREADSAFE_REFCOUNTING, we can't make sure
   // VsyncParent is always released on the background thread.
 }
 
-bool VsyncParent::NotifyVsync(TimeStamp aTimeStamp) {
+bool VsyncParent::NotifyVsync(const VsyncEvent& aVsync) {
   // Called on hardware vsync thread. We should post to current ipc thread.
   MOZ_ASSERT(!IsOnBackgroundThread());
-  nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<TimeStamp>(
+  nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<VsyncEvent>(
       "layout::VsyncParent::DispatchVsyncEvent", this,
-      &VsyncParent::DispatchVsyncEvent, aTimeStamp);
+      &VsyncParent::DispatchVsyncEvent, aVsync);
   MOZ_ALWAYS_SUCCEEDS(
       mBackgroundThread->Dispatch(vsyncEvent, NS_DISPATCH_NORMAL));
   return true;
 }
 
-void VsyncParent::DispatchVsyncEvent(TimeStamp aTimeStamp) {
+void VsyncParent::DispatchVsyncEvent(const VsyncEvent& aVsync) {
   AssertIsOnBackgroundThread();
 
   // If we call NotifyVsync() when we handle ActorDestroy() message, we might
   // still call DispatchVsyncEvent().
   // Similarly, we might also receive RecvUnobserveVsync() when call
   // NotifyVsync(). We use mObservingVsync and mDestroyed flags to skip this
   // notification.
   if (mObservingVsync && !mDestroyed) {
-    Unused << SendNotify(aTimeStamp);
+    Unused << SendNotify(aVsync);
   }
 }
 
 mozilla::ipc::IPCResult VsyncParent::RecvRequestVsyncRate() {
   AssertIsOnBackgroundThread();
   TimeDuration vsyncRate = gfxPlatform::GetPlatform()
                                ->GetHardwareVsync()
                                ->GetGlobalDisplay()
--- a/layout/ipc/VsyncParent.h
+++ b/layout/ipc/VsyncParent.h
@@ -29,24 +29,24 @@ class VsyncParent final : public PVsyncP
   friend class mozilla::ipc::BackgroundParentImpl;
 
  private:
   static already_AddRefed<VsyncParent> Create();
 
   VsyncParent();
   virtual ~VsyncParent();
 
-  virtual bool NotifyVsync(TimeStamp aTimeStamp) override;
+  virtual bool NotifyVsync(const VsyncEvent& aVsync) override;
   virtual mozilla::ipc::IPCResult RecvRequestVsyncRate() override;
 
   virtual mozilla::ipc::IPCResult RecvObserve() override;
   virtual mozilla::ipc::IPCResult RecvUnobserve() override;
   virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
 
-  void DispatchVsyncEvent(TimeStamp aTimeStamp);
+  void DispatchVsyncEvent(const VsyncEvent& aVsync);
 
   bool mObservingVsync;
   bool mDestroyed;
   nsCOMPtr<nsIThread> mBackgroundThread;
   RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
 };
 
 }  // namespace layout
--- a/toolkit/recordreplay/ipc/ChildIPC.cpp
+++ b/toolkit/recordreplay/ipc/ChildIPC.cpp
@@ -409,19 +409,22 @@ void NotifyAlwaysMarkMajorCheckpoints() 
 
 static VsyncObserver* gVsyncObserver;
 
 void SetVsyncObserver(VsyncObserver* aObserver) {
   MOZ_RELEASE_ASSERT(!gVsyncObserver || !aObserver);
   gVsyncObserver = aObserver;
 }
 
-static void NotifyVsyncObserver() {
+void NotifyVsyncObserver() {
   if (gVsyncObserver) {
-    gVsyncObserver->NotifyVsync(TimeStamp::Now());
+    static VsyncId vsyncId;
+    vsyncId = vsyncId.Next();
+    VsyncEvent event(vsyncId, TimeStamp::Now());
+    gVsyncObserver->NotifyVsync(event);
   }
 }
 
 // How many paints have been started and haven't reached PaintFromMainThread
 // yet. Only accessed on the main thread.
 static int32_t gNumPendingMainThreadPaints;
 
 bool OnVsync() {
--- a/widget/VsyncDispatcher.cpp
+++ b/widget/VsyncDispatcher.cpp
@@ -22,24 +22,23 @@ CompositorVsyncDispatcher::CompositorVsy
 }
 
 CompositorVsyncDispatcher::~CompositorVsyncDispatcher() {
   MOZ_ASSERT(XRE_IsParentProcess());
   // We auto remove this vsync dispatcher from the vsync source in the
   // nsBaseWidget
 }
 
-void CompositorVsyncDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp) {
+void CompositorVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
   // In vsync thread
-  layers::CompositorBridgeParent::PostInsertVsyncProfilerMarker(
-      aVsyncTimestamp);
+  layers::CompositorBridgeParent::PostInsertVsyncProfilerMarker(aVsync.mTime);
 
   MutexAutoLock lock(mCompositorObserverLock);
   if (mCompositorVsyncObserver) {
-    mCompositorVsyncObserver->NotifyVsync(aVsyncTimestamp);
+    mCompositorVsyncObserver->NotifyVsync(aVsync);
   }
 }
 
 void CompositorVsyncDispatcher::ObserveVsync(bool aEnable) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(XRE_IsParentProcess());
   if (mDidShutdown) {
     return;
@@ -96,25 +95,25 @@ RefreshTimerVsyncDispatcher::RefreshTime
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 RefreshTimerVsyncDispatcher::~RefreshTimerVsyncDispatcher() {
   MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
   MOZ_ASSERT(NS_IsMainThread());
 }
 
-void RefreshTimerVsyncDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp) {
+void RefreshTimerVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
   MutexAutoLock lock(mRefreshTimersLock);
 
   for (size_t i = 0; i < mChildRefreshTimers.Length(); i++) {
-    mChildRefreshTimers[i]->NotifyVsync(aVsyncTimestamp);
+    mChildRefreshTimers[i]->NotifyVsync(aVsync);
   }
 
   if (mParentRefreshTimer) {
-    mParentRefreshTimer->NotifyVsync(aVsyncTimestamp);
+    mParentRefreshTimer->NotifyVsync(aVsync);
   }
 }
 
 void RefreshTimerVsyncDispatcher::SetParentRefreshTimer(
     VsyncObserver* aVsyncObserver) {
   MOZ_ASSERT(NS_IsMainThread());
   {  // lock scope because UpdateVsyncStatus runs on main thread and will
      // deadlock
--- a/widget/VsyncDispatcher.h
+++ b/widget/VsyncDispatcher.h
@@ -6,29 +6,30 @@
 #ifndef mozilla_widget_VsyncDispatcher_h
 #define mozilla_widget_VsyncDispatcher_h
 
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
+#include "VsyncSource.h"
 
 namespace mozilla {
 
 class VsyncObserver {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncObserver)
 
  public:
   // The method called when a vsync occurs. Return true if some work was done.
   // In general, this vsync notification will occur on the hardware vsync
   // thread from VsyncSource. But it might also be called on PVsync ipc thread
   // if this notification is cross process. Thus all observer should check the
   // thread model before handling the real task.
-  virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) = 0;
+  virtual bool NotifyVsync(const VsyncEvent& aVsync) = 0;
 
  protected:
   VsyncObserver() {}
   virtual ~VsyncObserver() {}
 };  // VsyncObserver
 
 // Used to dispatch vsync events in the parent process to compositors.
 //
@@ -44,17 +45,17 @@ class VsyncObserver {
 // compositor thread in the compositor process.
 class CompositorVsyncDispatcher final {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncDispatcher)
 
  public:
   CompositorVsyncDispatcher();
 
   // Called on the vsync thread when a hardware vsync occurs
-  void NotifyVsync(TimeStamp aVsyncTimestamp);
+  void NotifyVsync(const VsyncEvent& aVsync);
 
   // Compositor vsync observers must be added/removed on the compositor thread
   void SetCompositorVsyncObserver(VsyncObserver* aVsyncObserver);
   void Shutdown();
 
  private:
   virtual ~CompositorVsyncDispatcher();
   void ObserveVsync(bool aEnable);
@@ -67,17 +68,17 @@ class CompositorVsyncDispatcher final {
 // Dispatch vsync event to ipc actor parent and chrome RefreshTimer.
 class RefreshTimerVsyncDispatcher final {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefreshTimerVsyncDispatcher)
 
  public:
   RefreshTimerVsyncDispatcher();
 
   // Please check CompositorVsyncDispatcher::NotifyVsync().
-  void NotifyVsync(TimeStamp aVsyncTimestamp);
+  void NotifyVsync(const VsyncEvent& aVsync);
 
   // Set chrome process's RefreshTimer to this dispatcher.
   // This function can be called from any thread.
   void SetParentRefreshTimer(VsyncObserver* aVsyncObserver);
 
   // Add or remove the content process' RefreshTimer to this dispatcher. This
   // will be a no-op for AddChildRefreshTimer() if the observer is already
   // registered.