Handle remote device resets by recreating the entire rendering stack. (bug 1363126 part 1, r=rhunt, ipc_r=billm)
authorDavid Anderson <dvander@alliedmods.net>
Thu, 11 May 2017 22:44:27 -0700
changeset 406153 063adb4deaf5efe3ef4f80635dd68e15a7a0325e
parent 406152 a21423aa28940c7320478713f1bde4f0f92f6698
child 406154 ff60b177a442a1b0c73286c51f60ac014685f787
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt, ipc_r
bugs1363126
milestone55.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
Handle remote device resets by recreating the entire rendering stack. (bug 1363126 part 1, r=rhunt, ipc_r=billm)
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
gfx/ipc/GPUProcessListener.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
gfx/layers/ipc/LayerTransactionChild.h
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/ipc/LayerTransactionParent.h
gfx/layers/ipc/PLayerTransaction.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
ipc/ipdl/sync-messages.ini
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1187,16 +1187,30 @@ ContentChild::RecvReinitRendering(Endpoi
       tabChild->ReinitRendering();
     }
   }
 
   VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentChild::RecvReinitRenderingForDeviceReset()
+{
+  gfxPlatform::GetPlatform()->CompositorUpdated();
+
+  nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
+  for (const auto& tabChild : tabs) {
+    if (tabChild->LayersId()) {
+      tabChild->ReinitRenderingForDeviceReset();
+    }
+  }
+  return IPC_OK();
+}
+
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
 
 #include <stdlib.h>
 
 static bool
 GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath, nsCString &aAppDir)
 {
   nsAutoCString appPath;
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -164,16 +164,18 @@ public:
   mozilla::ipc::IPCResult
   RecvReinitRendering(
     Endpoint<PCompositorBridgeChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
     Endpoint<PVRManagerChild>&& aVRBridge,
     Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
     nsTArray<uint32_t>&& namespaces) override;
 
+  mozilla::ipc::IPCResult RecvReinitRenderingForDeviceReset() override;
+
   virtual mozilla::ipc::IPCResult RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override;
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const TabId& aSameTabGroupAs,
                                             const IPCTabContext& aContext,
                                             const uint32_t& aChromeFlags,
                                             const ContentParentId& aCpID,
                                             const bool& aIsForBrowser) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2475,16 +2475,22 @@ ContentParent::OnCompositorUnexpectedShu
     Move(compositor),
     Move(imageBridge),
     Move(vrBridge),
     Move(videoManager),
     Move(namespaces));
 }
 
 void
+ContentParent::OnCompositorDeviceReset()
+{
+  Unused << SendReinitRenderingForDeviceReset();
+}
+
+void
 ContentParent::OnVarChanged(const GfxVarUpdate& aVar)
 {
   if (!mIPCOpen) {
     return;
   }
   Unused << SendVarUpdate(aVar);
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -664,16 +664,17 @@ protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
   void OnVarChanged(const GfxVarUpdate& aVar) override;
   void OnCompositorUnexpectedShutdown() override;
+  void OnCompositorDeviceReset() override;
 
 private:
   /**
    * A map of the remote content process type to a list of content parents
    * currently available to host *new* tabs/frames of that type.
    *
    * If a content process is identified as troubled or dead, it will be
    * removed from this list, but will still be in the sContentParents list for
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -368,16 +368,19 @@ child:
     // newly launched GPU process, or the compositor thread of the UI process.
     async ReinitRendering(
       Endpoint<PCompositorBridgeChild> compositor,
       Endpoint<PImageBridgeChild> bridge,
       Endpoint<PVRManagerChild> vr,
       Endpoint<PVideoDecoderManagerChild> video,
       uint32_t[] namespaces);
 
+    // Re-create the rendering stack for a device reset.
+    async ReinitRenderingForDeviceReset();
+
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
     async RequestMemoryReport(uint32_t generation,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3120,16 +3120,39 @@ TabChild::ReinitRendering()
 
   InitAPZState();
 
   nsCOMPtr<nsIDocument> doc(GetDocument());
   doc->NotifyLayerManagerRecreated();
 }
 
 void
+TabChild::ReinitRenderingForDeviceReset()
+{
+  InvalidateLayers();
+
+  RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+  ClientLayerManager* clm = lm->AsClientLayerManager();
+  if (!clm) {
+    return;
+  }
+
+  if (ShadowLayerForwarder* fwd = clm->AsShadowForwarder()) {
+    // Force the LayerTransactionChild to synchronously shutdown. It is
+    // okay to do this early, we'll simply stop sending messages. This
+    // step is necessary since otherwise the compositor will think we
+    // are trying to attach two layer trees to the same ID.
+    fwd->SynchronouslyShutdown();
+  }
+
+  // Proceed with destroying and recreating the layer manager.
+  ReinitRendering();
+}
+
+void
 TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier,
                             uint64_t aDeviceResetSeqNo)
 {
   MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT
              || mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR);
 
   RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -563,16 +563,17 @@ public:
                     const TimeStamp& aCompositeEnd);
 
   void DidRequestComposite(const TimeStamp& aCompositeReqStart,
                            const TimeStamp& aCompositeReqEnd);
 
   void ClearCachedResources();
   void InvalidateLayers();
   void ReinitRendering();
+  void ReinitRenderingForDeviceReset();
   void CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier,
                          uint64_t aDeviceResetSeqNo);
 
   static inline TabChild* GetFrom(nsIDOMWindow* aWindow)
   {
     nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
     nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
     return GetFrom(docShell);
--- a/gfx/ipc/GPUProcessListener.h
+++ b/gfx/ipc/GPUProcessListener.h
@@ -14,14 +14,19 @@ class GPUProcessListener
  public:
   virtual ~GPUProcessListener()
   {}
 
   // Called when the compositor has died and the rendering stack must be
   // recreated.
   virtual void OnCompositorUnexpectedShutdown()
   {}
+
+  // Called when devices have been reset and tabs must throw away their
+  // layer managers.
+  virtual void OnCompositorDeviceReset()
+  {}
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_GPUProcessListener_h_
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -359,22 +359,21 @@ GPUProcessManager::OnProcessDeviceReset(
 
   if (ShouldLimitDeviceResets(mDeviceResetCount, delta)) {
     DestroyProcess();
     DisableGPUProcess("GPU processed experienced too many device resets");
 
     HandleProcessLost();
     return;
   }
-  
-  uint64_t seqNo = GetNextDeviceResetSequenceNumber();
+
+  RebuildRemoteSessions();
 
-  // We're good, do a reset like normal
-  for (auto& session : mRemoteSessions) {
-    session->NotifyDeviceReset(seqNo);
+  for (const auto& listener : mListeners) {
+    listener->OnCompositorDeviceReset();
   }
 }
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
@@ -464,36 +463,41 @@ GPUProcessManager::HandleProcessLost()
   //      associated with a window compositor or else they can't forward
   //      layer transactions. So this step both ensures that a compositor
   //      exists, and that the tab can forward layers.
   //
   //  (8) Last, if the window had no remote tabs, step (7) will not have 
   //      applied, and the window will not have a new compositor just yet.
   //      The next refresh tick and paint will ensure that one exists, again
   //      via nsIWidget::GetLayerManager.
+  RebuildRemoteSessions();
 
+  // Notify content. This will ensure that each content process re-establishes
+  // a connection to the compositor thread (whether it's in-process or in a
+  // newly launched GPU process).
+  for (const auto& listener : mListeners) {
+    listener->OnCompositorUnexpectedShutdown();
+  }
+}
+
+void
+GPUProcessManager::RebuildRemoteSessions()
+{
   // Build a list of sessions to notify, since notification might delete
   // entries from the list.
   nsTArray<RefPtr<RemoteCompositorSession>> sessions;
   for (auto& session : mRemoteSessions) {
     sessions.AppendElement(session);
   }
 
   // Notify each widget that we have lost the GPU process. This will ensure
   // that each widget destroys its layer manager and CompositorBridgeChild.
   for (const auto& session : sessions) {
     session->NotifySessionLost();
   }
-
-  // Notify content. This will ensure that each content process re-establishes
-  // a connection to the compositor thread (whether it's in-process or in a
-  // newly launched GPU process).
-  for (const auto& listener : mListeners) {
-    listener->OnCompositorUnexpectedShutdown();
-  }
 }
 
 void
 GPUProcessManager::NotifyRemoteActorDestroyed(const uint64_t& aProcessToken)
 {
   if (!NS_IsMainThread()) {
     RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
       &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -193,16 +193,18 @@ private:
   void CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
                                         ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
 
   // Called from RemoteCompositorSession. We track remote sessions so we can
   // notify their owning widgets that the session must be restarted.
   void RegisterSession(RemoteCompositorSession* aSession);
   void UnregisterSession(RemoteCompositorSession* aSession);
 
+  void RebuildRemoteSessions();
+
 private:
   GPUProcessManager();
 
   // Permanently disable the GPU process and record a message why.
   void DisableGPUProcess(const char* aMessage);
 
   // Shutdown the GPU process.
   void CleanShutdown();
--- a/gfx/layers/ipc/LayerTransactionChild.h
+++ b/gfx/layers/ipc/LayerTransactionChild.h
@@ -43,16 +43,20 @@ public:
 
   void SetForwarder(ShadowLayerForwarder* aForwarder)
   {
     mForwarder = aForwarder;
   }
 
   uint64_t GetId() const { return mId; }
 
+  void MarkDestroyed() {
+    mDestroyed = true;
+  }
+
 protected:
   explicit LayerTransactionChild(const uint64_t& aId)
     : mForwarder(nullptr)
     , mIPCOpen(false)
     , mDestroyed(false)
     , mId(aId)
   {}
   ~LayerTransactionChild() { }
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -84,16 +84,22 @@ LayerTransactionParent::RecvShutdown()
   Destroy();
   IProtocol* mgr = Manager();
   if (!Send__delete__(this)) {
     return IPC_FAIL_NO_REASON(mgr);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+LayerTransactionParent::RecvShutdownSync()
+{
+  return RecvShutdown();
+}
+
 void
 LayerTransactionParent::Destroy()
 {
   mDestroyed = true;
   mCompositables.clear();
 }
 
 class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -103,16 +103,17 @@ public:
   void AcknowledgeCompositorUpdate(uint64_t aNumber) {
     if (mPendingCompositorUpdate == Some(aNumber)) {
       mPendingCompositorUpdate = Nothing();
     }
   }
 
 protected:
   virtual mozilla::ipc::IPCResult RecvShutdown() override;
+  virtual mozilla::ipc::IPCResult RecvShutdownSync() override;
 
   virtual mozilla::ipc::IPCResult RecvPaintTime(const uint64_t& aTransactionId,
                                                 const TimeDuration& aPaintTime) override;
 
   virtual mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
   virtual mozilla::ipc::IPCResult RecvUpdate(const TransactionInfo& aInfo) override;
 
   virtual mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -124,14 +124,16 @@ parent:
 
   // Tell the compositor to notify APZ that a layer has been confirmed for an
   // input event.
   async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
 
   async RecordPaintTimes(PaintTiming timing);
 
   async Shutdown();
+  sync ShutdownSync();
+
 child:
   async __delete__();
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -1091,16 +1091,25 @@ ShadowLayerForwarder::ReleaseCompositabl
     if (!IPCOpen()) {
       return;
     }
     mShadowManager->SendReleaseCompositable(aHandle);
   }
   mCompositables.Remove(aHandle.Value());
 }
 
+void
+ShadowLayerForwarder::SynchronouslyShutdown()
+{
+  if (IPCOpen()) {
+    mShadowManager->SendShutdownSync();
+    mShadowManager->MarkDestroyed();
+  }
+}
+
 ShadowableLayer::~ShadowableLayer()
 {
   if (mShadow) {
     mForwarder->ReleaseLayer(GetShadow());
   }
 }
 
 } // namespace layers
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -314,16 +314,20 @@ public:
   void Composite();
 
   /**
    * True if this is forwarding to a LayerManagerComposite.
    */
   bool HasShadowManager() const { return !!mShadowManager; }
   LayerTransactionChild* GetShadowManager() const { return mShadowManager.get(); }
 
+  // Send a synchronous message asking the LayerTransactionParent in the
+  // compositor to shutdown.
+  void SynchronouslyShutdown();
+
   virtual void WindowOverlayChanged() { mWindowOverlayChanged = true; }
 
   /**
    * The following Alloc/Open/Destroy interfaces abstract over the
    * details of working with surfaces that are shared across
    * processes.  They provide the glue between C++ Layers and the
    * LayerComposite IPC system.
    *
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -1085,8 +1085,10 @@ description =
 [PPrinting::SavePrintSettings]
 description =
 [PHandlerService::FillHandlerInfo]
 description =
 [PHandlerService::Exists]
 description =
 [PHandlerService::GetTypeFromExtension]
 description =
+[PLayerTransaction::ShutdownSync]
+description = bug 1363126