Don't reset devices for each tab when the compositor resets. (bug 1316788, r=rhunt)
authorDavid Anderson <danderson@mozilla.com>
Mon, 14 Nov 2016 11:47:01 -0800
changeset 367472 b4ab43a2c1ec4cc95ac7b017b62b0420d7db10b1
parent 367471 4da186740a81e2c4bb7821a957226ffeac631d58
child 367473 878e54671aea2d422d124ba76da873936089fe92
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt
bugs1316788
milestone52.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
Don't reset devices for each tab when the compositor resets. (bug 1316788, r=rhunt)
dom/ipc/TabChild.cpp
gfx/ipc/CompositorSession.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
gfx/ipc/InProcessCompositorSession.cpp
gfx/ipc/InProcessCompositorSession.h
gfx/ipc/RemoteCompositorSession.cpp
gfx/ipc/RemoteCompositorSession.h
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/layers/ipc/CompositorBridgeChild.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
gfx/layers/ipc/PCompositorBridge.ipdl
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/nsWindowGfx.cpp
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2998,18 +2998,16 @@ TabChild::ReinitRendering()
 
   nsCOMPtr<nsIDocument> doc(GetDocument());
   doc->NotifyLayerManagerRecreated();
 }
 
 void
 TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier)
 {
-  gfxPlatform::GetPlatform()->CompositorUpdated();
-
   RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
   ClientLayerManager* clm = lm->AsClientLayerManager();
   MOZ_ASSERT(clm);
 
   mTextureFactoryIdentifier = aNewIdentifier;
   clm->UpdateTextureFactoryIdentifier(aNewIdentifier);
   FrameLayerBuilder::InvalidateAllLayers(clm);
 }
--- a/gfx/ipc/CompositorSession.h
+++ b/gfx/ipc/CompositorSession.h
@@ -40,16 +40,17 @@ protected:
   typedef gfx::GPUProcessHost GPUProcessHost;
   typedef widget::CompositorWidget CompositorWidget;
   typedef widget::CompositorWidgetDelegate CompositorWidgetDelegate;
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession)
 
   virtual bool Reset(const nsTArray<LayersBackend>& aBackendHints,
+                     uint64_t aSeqNo,
                      TextureFactoryIdentifier* aOutIdentifier) = 0;
 
   virtual void Shutdown() = 0;
 
   // This returns a CompositorBridgeParent if the compositor resides in the same process.
   virtual CompositorBridgeParent* GetInProcessBridge() const = 0;
 
   // Set the GeckoContentController for the root of the layer tree.
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -55,16 +55,17 @@ void
 GPUProcessManager::Shutdown()
 {
   sSingleton = nullptr;
 }
 
 GPUProcessManager::GPUProcessManager()
  : mTaskFactory(this),
    mNextLayerTreeId(0),
+   mNextResetSequenceNo(0),
    mNumProcessAttempts(0),
    mDeviceResetCount(0),
    mProcess(nullptr),
    mGPUChild(nullptr)
 {
   MOZ_COUNT_CTOR(GPUProcessManager);
 
   mObserver = new Observer(this);
@@ -301,20 +302,22 @@ GPUProcessManager::OnProcessDeviceReset(
 
   if (ShouldLimitDeviceResets(mDeviceResetCount, delta)) {
     DestroyProcess();
     DisableGPUProcess("GPU processed experienced too many device resets");
 
     HandleProcessLost();
     return;
   }
+  
+  uint64_t seqNo = GetNextDeviceResetSequenceNumber();
 
   // We're good, do a reset like normal
   for (auto& session : mRemoteSessions) {
-    session->NotifyDeviceReset();
+    session->NotifyDeviceReset(seqNo);
   }
 }
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -143,16 +143,24 @@ public:
     return mGPUChild;
   }
 
   // Returns whether or not a GPU process was ever launched.
   bool AttemptedGPUProcess() const {
     return mNumProcessAttempts > 0;
   }
 
+  // Returns the next compositor reset sequence number, a monotonic counter
+  // for when the compositing device resets. Since content processes are
+  // notified of resets through each individual tab, this allows content to
+  // only re-acquire devices once for each reset.
+  uint64_t GetNextDeviceResetSequenceNumber() {
+    return ++mNextResetSequenceNo;
+  }
+
 private:
   // Called from our xpcom-shutdown observer.
   void OnXPCOMShutdown();
 
   bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                      ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
                                 ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
@@ -208,16 +216,17 @@ private:
   };
   friend class Observer;
 
 private:
   RefPtr<Observer> mObserver;
   ipc::TaskFactory<GPUProcessManager> mTaskFactory;
   RefPtr<VsyncIOThreadHolder> mVsyncIOThread;
   uint64_t mNextLayerTreeId;
+  uint64_t mNextResetSequenceNo;
   uint32_t mNumProcessAttempts;
 
   nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions;
   nsTArray<GPUProcessListener*> mListeners;
 
   uint32_t mDeviceResetCount;
   TimeStamp mDeviceResetLastTime;
 
--- a/gfx/ipc/InProcessCompositorSession.cpp
+++ b/gfx/ipc/InProcessCompositorSession.cpp
@@ -56,19 +56,21 @@ InProcessCompositorSession::SetContentCo
 
 RefPtr<IAPZCTreeManager>
 InProcessCompositorSession::GetAPZCTreeManager() const
 {
   return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId);
 }
 
 bool
-InProcessCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier)
+InProcessCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints,
+                                  uint64_t aSeqNo,
+                                  TextureFactoryIdentifier* aOutIdentifier)
 {
-  return mCompositorBridgeParent->ResetCompositor(aBackendHints, aOutIdentifier);
+  return mCompositorBridgeParent->ResetCompositor(aBackendHints, aSeqNo, aOutIdentifier);
 }
 
 void
 InProcessCompositorSession::Shutdown()
 {
   // Destroy will synchronously wait for the parent to acknowledge shutdown,
   // at which point CBP will defer a Release on the compositor thread. We
   // can safely release our reference now, and let the destructor run on either
--- a/gfx/ipc/InProcessCompositorSession.h
+++ b/gfx/ipc/InProcessCompositorSession.h
@@ -25,17 +25,19 @@ public:
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
   RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
-  bool Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) override;
+  bool Reset(const nsTArray<LayersBackend>& aBackendHints,
+             uint64_t aSeqNo,
+             TextureFactoryIdentifier* aOutIdentifier) override;
   void Shutdown() override;
 
 private:
   InProcessCompositorSession(widget::CompositorWidget* aWidget,
                              CompositorBridgeChild* aChild,
                              CompositorBridgeParent* aParent);
 
 private:
--- a/gfx/ipc/RemoteCompositorSession.cpp
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -34,20 +34,20 @@ RemoteCompositorSession::RemoteComposito
 
 RemoteCompositorSession::~RemoteCompositorSession()
 {
   // This should have been shutdown first.
   MOZ_ASSERT(!mCompositorBridgeChild);
 }
 
 void
-RemoteCompositorSession::NotifyDeviceReset()
+RemoteCompositorSession::NotifyDeviceReset(uint64_t aSeqNo)
 {
   MOZ_ASSERT(mWidget);
-  mWidget->OnRenderingDeviceReset();
+  mWidget->OnRenderingDeviceReset(aSeqNo);
 }
 
 void
 RemoteCompositorSession::NotifySessionLost()
 {
   // Re-entrancy should be impossible: when we are being notified of a lost
   // session, we have by definition not shut down yet. We will shutdown, but
   // then will be removed from the notification list.
@@ -82,20 +82,22 @@ RemoteCompositorSession::GetWidget()
 
 RefPtr<IAPZCTreeManager>
 RemoteCompositorSession::GetAPZCTreeManager() const
 {
   return mAPZ;
 }
 
 bool
-RemoteCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier)
+RemoteCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints,
+                               uint64_t aSeqNo,
+                               TextureFactoryIdentifier* aOutIdentifier)
 {
   bool didReset;
-  Unused << mCompositorBridgeChild->SendReset(aBackendHints, &didReset, aOutIdentifier);
+  Unused << mCompositorBridgeChild->SendReset(aBackendHints, aSeqNo, &didReset, aOutIdentifier);
   return didReset;
 }
 
 void
 RemoteCompositorSession::Shutdown()
 {
   mContentController = nullptr;
   if (mAPZ) {
--- a/gfx/ipc/RemoteCompositorSession.h
+++ b/gfx/ipc/RemoteCompositorSession.h
@@ -23,20 +23,22 @@ public:
                           const uint64_t& aRootLayerTreeId);
   ~RemoteCompositorSession() override;
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
   GeckoContentController* GetContentController();
   nsIWidget* GetWidget();
   RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
-  bool Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) override;
+  bool Reset(const nsTArray<LayersBackend>& aBackendHints,
+             uint64_t aSeqNo,
+             TextureFactoryIdentifier* aOutIdentifier) override;
   void Shutdown() override;
 
-  void NotifyDeviceReset();
+  void NotifyDeviceReset(uint64_t aSeqNo);
   void NotifySessionLost();
 
 private:
   nsBaseWidget* mWidget;
   RefPtr<APZCTreeManagerChild> mAPZ;
   RefPtr<GeckoContentController> mContentController;
 };
 
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -333,22 +333,31 @@ CompositorBridgeChild::RecvInvalidateLay
       child->InvalidateLayers();
     }
   }
   return true;
 }
 
 bool
 CompositorBridgeChild::RecvCompositorUpdated(const uint64_t& aLayersId,
-                                             const TextureFactoryIdentifier& aNewIdentifier)
+                                             const TextureFactoryIdentifier& aNewIdentifier,
+                                             const uint64_t& aSeqNo)
 {
   if (mLayerManager) {
     // This case is handled directly by nsBaseWidget.
     MOZ_ASSERT(aLayersId == 0);
   } else if (aLayersId != 0) {
+    // Update gfxPlatform if this is the first time we're seeing this compositor
+    // update (we will get an update for each connected tab).
+    static uint64_t sLastSeqNo = 0;
+    if (sLastSeqNo != aSeqNo) {
+      gfxPlatform::GetPlatform()->CompositorUpdated();
+      sLastSeqNo = aSeqNo;
+    }
+
     if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) {
       child->CompositorUpdated(aNewIdentifier);
     }
     if (!mCanSend) {
       return true;
     }
     SendAcknowledgeCompositorUpdate(aLayersId);
   }
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -98,17 +98,18 @@ public:
                    const TimeStamp& aCompositeStart,
                    const TimeStamp& aCompositeEnd) override;
 
   virtual bool
   RecvInvalidateLayers(const uint64_t& aLayersId) override;
 
   virtual bool
   RecvCompositorUpdated(const uint64_t& aLayersId,
-                        const TextureFactoryIdentifier& aNewIdentifier) override;
+                        const TextureFactoryIdentifier& aNewIdentifier,
+                        const uint64_t& aSeqNo) override;
 
   virtual bool
   RecvOverfill(const uint32_t &aOverfill) override;
 
   virtual bool
   RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset,
                                  const LayoutDeviceIntRegion& aVisibleRegion,
                                  nsTArray<PluginWindowData>&& aPlugins) override;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -679,20 +679,23 @@ CompositorBridgeParent::Initialize()
   }
 
   LayerScope::SetPixelScale(mScale.scale);
 
   mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
 }
 
 bool
-CompositorBridgeParent::RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier)
+CompositorBridgeParent::RecvReset(nsTArray<LayersBackend>&& aBackendHints,
+                                  const uint64_t& aSeqNo,
+                                  bool* aResult,
+                                  TextureFactoryIdentifier* aOutIdentifier)
 {
   Maybe<TextureFactoryIdentifier> newIdentifier;
-  ResetCompositorTask(aBackendHints, &newIdentifier);
+  ResetCompositorTask(aBackendHints, aSeqNo, &newIdentifier);
   
   if (newIdentifier) {
     *aResult = true;
     *aOutIdentifier = newIdentifier.value();
   } else {
     *aResult = false;
   }
 
@@ -2049,44 +2052,48 @@ CompositorBridgeParent::InvalidateRemote
       CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
       Unused << cpcp->SendInvalidateLayers(aLayersId);
     }
   });
 }
 
 bool
 CompositorBridgeParent::ResetCompositor(const nsTArray<LayersBackend>& aBackendHints,
+                                        uint64_t aSeqNo,
                                         TextureFactoryIdentifier* aOutIdentifier)
 {
   Maybe<TextureFactoryIdentifier> newIdentifier;
   {
     MonitorAutoLock lock(mResetCompositorMonitor);
 
     CompositorLoop()->PostTask(NewRunnableMethod
                                <StoreCopyPassByConstLRef<nsTArray<LayersBackend>>,
-                                Maybe<TextureFactoryIdentifier>*>(this,
-                                                                  &CompositorBridgeParent::ResetCompositorTask,
-                                                                  aBackendHints,
-                                                                  &newIdentifier));
+                               uint64_t,
+                               Maybe<TextureFactoryIdentifier>*>(this,
+                                                                 &CompositorBridgeParent::ResetCompositorTask,
+                                                                 aBackendHints,
+                                                                 aSeqNo,
+                                                                 &newIdentifier));
 
     mResetCompositorMonitor.Wait();
   }
 
   if (!newIdentifier) {
     return false;
   }
 
   *aOutIdentifier = newIdentifier.value();
   return true;
 }
 
 // Invoked on the compositor thread. The main thread is waiting on the given
 // monitor.
 void
 CompositorBridgeParent::ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints,
+                                            uint64_t aSeqNo,
                                             Maybe<TextureFactoryIdentifier>* aOutNewIdentifier)
 {
   // Perform the reset inside a lock, so the main thread can wake up as soon as
   // possible. We notify child processes (if necessary) outside the lock.
   Maybe<TextureFactoryIdentifier> newIdentifier;
   {
     MonitorAutoLock lock(mResetCompositorMonitor);
 
@@ -2103,17 +2110,17 @@ CompositorBridgeParent::ResetCompositorT
   if (!newIdentifier) {
     // No compositor change; nothing to do.
     return;
   }
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   ForEachIndirectLayerTree([&] (LayerTreeState* lts, uint64_t layersId) -> void {
     if (CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent) {
-      Unused << cpcp->SendCompositorUpdated(layersId, newIdentifier.value());
+      Unused << cpcp->SendCompositorUpdated(layersId, newIdentifier.value(), aSeqNo);
 
       if (LayerTransactionParent* ltp = lts->mLayerTree) {
         ltp->AddPendingCompositorUpdate();
       }
       lts->mPendingCompositorUpdates++;
     }
   });
 }
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -257,17 +257,20 @@ public:
                        bool aUseAPZ);
 
   // Must only be called by GPUParent. After invoking this, the IPC channel
   // is active and RecvWillStop/ActorDestroy must be called to free the
   // compositor.
   bool Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint);
 
   virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override;
-  virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override;
+  virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints,
+                         const uint64_t& aSeqNo,
+                         bool* aResult,
+                         TextureFactoryIdentifier* aOutIdentifier) override;
   virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override;
   virtual bool RecvRequestOverfill() override;
   virtual bool RecvWillClose() override;
   virtual bool RecvPause() override;
   virtual bool RecvResume() override;
   virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
   virtual bool RecvNotifyChildRecreated(const uint64_t& child) override;
   virtual bool RecvAdoptChild(const uint64_t& child) override;
@@ -344,16 +347,17 @@ public:
    *
    * Note that this posts a task directly, rather than using synchronous
    * IPDL, and waits on a monitor notification from the compositor thread.
    * We do this as a best-effort attempt to jump any IPDL messages that
    * have not yet been posted (and are sitting around in the IO pipe), to
    * minimize the amount of time the main thread is blocked.
    */
   bool ResetCompositor(const nsTArray<LayersBackend>& aBackendHints,
+                       uint64_t aSeqNo,
                        TextureFactoryIdentifier* aOutIdentifier);
 
   /**
    * This forces the is-first-paint flag to true. This is intended to
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
@@ -579,16 +583,17 @@ protected:
   void ForceComposition();
   void CancelCurrentCompositeTask();
   void Invalidate();
   bool IsPendingComposite();
   void FinishPendingComposite();
 
   RefPtr<Compositor> NewCompositor(const nsTArray<LayersBackend>& aBackendHints);
   void ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints,
+                           uint64_t aSeqNo,
                            Maybe<TextureFactoryIdentifier>* aOutNewIdentifier);
   Maybe<TextureFactoryIdentifier> ResetCompositorImpl(const nsTArray<LayersBackend>& aBackendHints);
 
   /**
    * Add a compositor to the global compositor map.
    */
   static void AddCompositor(CompositorBridgeParent* compositor, uint64_t* id);
   /**
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -37,17 +37,21 @@ public:
     }
     mSelfRef = this;
   }
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   // FIXME/bug 774388: work out what shutdown protocol we need.
   virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override { return false; }
-  virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override { return false; }
+  virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints,
+                         const uint64_t& aSeqNo,
+                         bool* aResult,
+                         TextureFactoryIdentifier* aOutIdentifier) override
+  { return false; }
   virtual bool RecvRequestOverfill() override { return true; }
   virtual bool RecvWillClose() override { return true; }
   virtual bool RecvPause() override { return true; }
   virtual bool RecvResume() override { return true; }
   virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
   virtual bool RecvNotifyChildRecreated(const uint64_t& child) override { return false; }
   virtual bool RecvAdoptChild(const uint64_t& child) override { return false; }
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -57,18 +57,22 @@ sync protocol PCompositorBridge
 child:
   // The child should invalidate retained layers. This is used for local
   // compositor device resets, such as in CompositorD3D9, and ensures that
   // TextureSources are recreated.
   async InvalidateLayers(uint64_t layersId);
 
   // The compositor type or device has changed, and a new texture factory
   // identifier is available. Layers must be invalidated and the new identifier
-  // must be propagated.
-  async CompositorUpdated(uint64_t layersId, TextureFactoryIdentifier newIdentifier);
+  // must be propagated. The sequence number is a generation count for the
+  // compositor.
+  async CompositorUpdated(
+    uint64_t layersId,
+    TextureFactoryIdentifier newIdentifier,
+    uint64_t seqNo);
 
   // The compositor completed a layers transaction. id is the layers id
   // of the child layer tree that was composited (or 0 when notifying
   // the root layer tree).
   // transactionId is the id of the transaction before this composite, or 0
   // if there was no transaction since the last composite.
   async DidComposite(uint64_t id, uint64_t transactionId,
                      TimeStamp compositeStart, TimeStamp compositeEnd);
@@ -118,17 +122,18 @@ child:
   async ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive);
 
 parent:
   // Must be called before Initialize().
   async PCompositorWidget(CompositorWidgetInitData aInitData);
 
   // When out-of-process, this must be called to finish initialization.
   sync Initialize(uint64_t rootLayerTreeId);
-  sync Reset(LayersBackend[] aBackendHints) returns (bool aResult, TextureFactoryIdentifier aOutIdentifier);
+  sync Reset(LayersBackend[] aBackendHints, uint64_t aSeqNo)
+    returns (bool aResult, TextureFactoryIdentifier aOutIdentifier);
 
   // Returns whether this Compositor has APZ enabled or not.
   sync AsyncPanZoomEnabled(uint64_t layersId) returns (bool aHasAPZ);
 
   // Must be called after Initialize(), and only succeeds if AsyncPanZoomEnabled() is true.
   async PAPZ(uint64_t layersId);
   async PAPZCTreeManager(uint64_t layersId);
 
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -308,17 +308,17 @@ void nsBaseWidget::DestroyLayerManager()
   if (mLayerManager) {
     mLayerManager->Destroy();
     mLayerManager = nullptr;
   }
   DestroyCompositor();
 }
 
 void
-nsBaseWidget::OnRenderingDeviceReset()
+nsBaseWidget::OnRenderingDeviceReset(uint64_t aSeqNo)
 {
   if (!mLayerManager || !mCompositorSession) {
     return;
   }
 
   nsTArray<LayersBackend> backendHints;
   gfxPlatform::GetPlatform()->GetCompositorBackends(ComputeShouldAccelerate(), backendHints);
 
@@ -332,17 +332,17 @@ nsBaseWidget::OnRenderingDeviceReset()
   if (!ComputeShouldAccelerate() &&
       clm->GetTextureFactoryIdentifier().mParentBackend != LayersBackend::LAYERS_BASIC)
   {
     return;
   }
 
   // Recreate the compositor.
   TextureFactoryIdentifier identifier;
-  if (!mCompositorSession->Reset(backendHints, &identifier)) {
+  if (!mCompositorSession->Reset(backendHints, aSeqNo, &identifier)) {
     // No action was taken, so we don't have to do anything.
     return;
   }
 
   // Invalidate all layers.
   FrameLayerBuilder::InvalidateAllLayers(mLayerManager);
 
   // Update the texture factory identifier.
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -569,17 +569,17 @@ protected:
   virtual void WindowUsesOMTC() {}
   virtual void RegisterTouchWindow() {}
 
   nsIDocument* GetDocument() const;
 
   void EnsureTextEventDispatcher();
 
   // Notify the compositor that a device reset has occurred.
-  void OnRenderingDeviceReset();
+  void OnRenderingDeviceReset(uint64_t aSeqNo);
 
   bool UseAPZ();
 
   /**
    * For widgets that support synthesizing native touch events, this function
    * can be used to manage the current state of synthetic pointers. Each widget
    * must maintain its own MultiTouchInput instance and pass it in as the state,
    * along with the desired parameters for the changes. This function returns
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1155,38 +1155,40 @@ nsWindow::GetParentWindowBase(bool aIncl
   return static_cast<nsWindowBase*>(widget);
 }
  
 BOOL CALLBACK
 nsWindow::EnumAllChildWindProc(HWND aWnd, LPARAM aParam)
 {
   nsWindow *wnd = WinUtils::GetNSWindowPtr(aWnd);
   if (wnd) {
-    ((nsWindow::WindowEnumCallback*)aParam)(wnd);
+    reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
   }
   return TRUE;
 }
 
 BOOL CALLBACK
 nsWindow::EnumAllThreadWindowProc(HWND aWnd, LPARAM aParam)
 {
   nsWindow *wnd = WinUtils::GetNSWindowPtr(aWnd);
   if (wnd) {
-    ((nsWindow::WindowEnumCallback*)aParam)(wnd);
+    reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
   }
   EnumChildWindows(aWnd, EnumAllChildWindProc, aParam);
   return TRUE;
 }
 
-void
-nsWindow::EnumAllWindows(WindowEnumCallback aCallback)
-{
+/* static*/ nsTArray<nsWindow*>
+nsWindow::EnumAllWindows()
+{
+  nsTArray<nsWindow*> windows;
   EnumThreadWindows(GetCurrentThreadId(),
                     EnumAllThreadWindowProc,
-                    (LPARAM)aCallback);
+                    reinterpret_cast<LPARAM>(&windows));
+  return windows;
 }
 
 static already_AddRefed<SourceSurface>
 CreateSourceSurfaceForGfxSurface(gfxASurface* aSurface)
 {
   MOZ_ASSERT(aSurface);
   return Factory::CreateSourceSurfaceForCairoSurface(
            aSurface->CairoSurface(), aSurface->GetSize(),
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -249,18 +249,17 @@ public:
   /**
    * Window utilities
    */
   nsWindow*               GetTopLevelWindow(bool aStopOnDialogOrPopup);
   WNDPROC                 GetPrevWindowProc() { return mPrevWndProc; }
   WindowHook&             GetWindowHook() { return mWindowHook; }
   nsWindow*               GetParentWindow(bool aIncludeOwner);
   // Get an array of all nsWindow*s on the main thread.
-  typedef void            (WindowEnumCallback)(nsWindow*);
-  static void             EnumAllWindows(WindowEnumCallback aCallback);
+  static nsTArray<nsWindow*> EnumAllWindows();
 
   /**
    * Misc.
    */
   virtual bool            AutoErase(HDC dc);
   bool WidgetTypeSupportsAcceleration() override;
 
   void                    ForcePresent();
--- a/widget/windows/nsWindowGfx.cpp
+++ b/widget/windows/nsWindowGfx.cpp
@@ -38,16 +38,17 @@ using mozilla::plugins::PluginInstancePa
 #include "gfxContext.h"
 #include "prmem.h"
 #include "WinUtils.h"
 #include "nsIWidgetListener.h"
 #include "mozilla/Unused.h"
 #include "nsDebug.h"
 #include "nsIXULRuntime.h"
 
+#include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "ClientLayerManager.h"
 
 #include "nsUXThemeData.h"
 #include "nsUXThemeConstants.h"
 
 using namespace mozilla;
@@ -167,26 +168,27 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t
   // windows event spin loop. If we don't trap for this, we'll try to paint,
   // but view manager will refuse to paint the surface, resulting is black
   // flashes on the plugin rendering surface.
   if (mozilla::ipc::MessageChannel::IsSpinLoopActive() && mPainting)
     return false;
 
   DeviceResetReason resetReason = DeviceResetReason::OK;
   if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset(&resetReason)) {
-
     gfxCriticalNote << "(nsWindow) Detected device reset: " << (int)resetReason;
 
     gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
-    EnumAllWindows([] (nsWindow* aWindow) -> void {
-      aWindow->OnRenderingDeviceReset();
-    });
+
+    uint64_t resetSeqNo = GPUProcessManager::Get()->GetNextDeviceResetSequenceNumber();
+    nsTArray<nsWindow*> windows = EnumAllWindows();
+    for (nsWindow* window : windows) {
+      window->OnRenderingDeviceReset(resetSeqNo);
+    }
 
     gfxCriticalNote << "(nsWindow) Finished device reset.";
-
     return false;
   }
 
   // After we CallUpdateWindow to the child, occasionally a WM_PAINT message
   // is posted to the parent event loop with an empty update rect. Do a
   // dummy paint so that Windows stops dispatching WM_PAINT in an inifinite
   // loop. See bug 543788.
   if (IsPlugin()) {