Recreate widget compositors when the GPU process dies. (bug 1300936 part 2, r=mattwoodrow)
authorDavid Anderson <danderson@mozilla.com>
Tue, 20 Sep 2016 01:16:03 -0700
changeset 314601 5ac08fb77360da01e694dd3fb5fef07a64173298
parent 314600 320c9cc8fc52abac4589b185a1897b7270e5943e
child 314602 9a6f5fac405ffd3ab59163bf4c5ec866a1bb142b
push id30732
push usercbook@mozilla.com
push dateWed, 21 Sep 2016 10:04:03 +0000
treeherdermozilla-central@560b2c805bf7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1300936
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
Recreate widget compositors when the GPU process dies. (bug 1300936 part 2, r=mattwoodrow)
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
gfx/ipc/RemoteCompositorSession.cpp
gfx/ipc/RemoteCompositorSession.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -238,16 +238,28 @@ GPUProcessManager::OnProcessLaunchComple
 }
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
+
+  // 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.
+  for (const auto& session : sessions) {
+    session->NotifySessionLost();
+  }
 }
 
 void
 GPUProcessManager::NotifyRemoteActorDestroyed(const uint64_t& aProcessToken)
 {
   if (!NS_IsMainThread()) {
     RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
       &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
@@ -259,17 +271,17 @@ GPUProcessManager::NotifyRemoteActorDest
     // This token is for an older process; we can safely ignore it.
     return;
   }
 
   // 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();
+  OnProcessUnexpectedShutdown(mProcess);
 }
 
 void
 GPUProcessManager::CleanShutdown()
 {
   if (!mProcess) {
     return;
   }
@@ -400,17 +412,17 @@ GPUProcessManager::CreateRemoteSession(n
     PAPZCTreeManagerChild* papz = child->SendPAPZCTreeManagerConstructor(0);
     if (!papz) {
       return nullptr;
     }
     apz = static_cast<APZCTreeManagerChild*>(papz);
   }
 
   RefPtr<RemoteCompositorSession> session =
-    new RemoteCompositorSession(child, widget, apz, aRootLayerTreeId);
+    new RemoteCompositorSession(aWidget, child, widget, apz, aRootLayerTreeId);
   return session.forget();
 #else
   gfxCriticalNote << "Platform does not support out-of-process compositing";
   return nullptr;
 #endif
 }
 
 bool
@@ -584,10 +596,22 @@ GPUProcessManager::EnsureVsyncIOThread()
 }
 
 void
 GPUProcessManager::ShutdownVsyncIOThread()
 {
   mVsyncIOThread = nullptr;
 }
 
+void
+GPUProcessManager::RegisterSession(RemoteCompositorSession* aSession)
+{
+  mRemoteSessions.AppendElement(aSession);
+}
+
+void
+GPUProcessManager::UnregisterSession(RemoteCompositorSession* aSession)
+{
+  mRemoteSessions.RemoveElement(aSession);
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -24,45 +24,49 @@ class nsBaseWidget;
 namespace mozilla {
 namespace layers {
 class IAPZCTreeManager;
 class CompositorSession;
 class ClientLayerManager;
 class CompositorUpdateObserver;
 class PCompositorBridgeChild;
 class PImageBridgeChild;
+class RemoteCompositorSession;
 } // namespace layers
 namespace widget {
 class CompositorWidget;
 } // namespace widget
 namespace dom {
 class ContentParent;
 class TabParent;
 } // namespace dom
 namespace ipc {
 class GeckoChildProcessHost;
 } // namespace ipc
 namespace gfx {
 
 class GPUChild;
+class PVRManagerChild;
 class VsyncBridgeChild;
 class VsyncIOThreadHolder;
-class PVRManagerChild;
 
 // 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
 {
+  friend class layers::RemoteCompositorSession;
+
   typedef layers::ClientLayerManager ClientLayerManager;
   typedef layers::CompositorSession CompositorSession;
   typedef layers::IAPZCTreeManager IAPZCTreeManager;
   typedef layers::CompositorUpdateObserver CompositorUpdateObserver;
   typedef layers::PCompositorBridgeChild PCompositorBridgeChild;
   typedef layers::PImageBridgeChild PImageBridgeChild;
+  typedef layers::RemoteCompositorSession RemoteCompositorSession;
 
 public:
   static void Initialize();
   static void Shutdown();
   static GPUProcessManager* Get();
 
   ~GPUProcessManager();
 
@@ -128,16 +132,21 @@ private:
 
   bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                      ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
                                 ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
   bool CreateContentVRManager(base::ProcessId aOtherProcess,
                               ipc::Endpoint<PVRManagerChild>* 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);
+
 private:
   GPUProcessManager();
 
   // Permanently disable the GPU process and record a message why.
   void DisableGPUProcess(const char* aMessage);
 
   // Shutdown the GPU process.
   void CleanShutdown();
@@ -174,16 +183,18 @@ private:
   friend class Observer;
 
 private:
   RefPtr<Observer> mObserver;
   ipc::TaskFactory<GPUProcessManager> mTaskFactory;
   RefPtr<VsyncIOThreadHolder> mVsyncIOThread;
   uint64_t mNextLayerTreeId;
 
+  nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions;
+
   // Fields that are associated with the current GPU process.
   GPUProcessHost* mProcess;
   MOZ_INIT_OUTSIDE_CTOR uint64_t mProcessToken;
   GPUChild* mGPUChild;
   RefPtr<VsyncBridgeChild> mVsyncBridge;
 };
 
 } // namespace gfx
--- a/gfx/ipc/RemoteCompositorSession.cpp
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -1,32 +1,51 @@
 /* -*- 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 "RemoteCompositorSession.h"
-
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
+#include "nsBaseWidget.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 using namespace widget;
 
-RemoteCompositorSession::RemoteCompositorSession(CompositorBridgeChild* aChild,
+RemoteCompositorSession::RemoteCompositorSession(nsBaseWidget* aWidget,
+                                                 CompositorBridgeChild* aChild,
                                                  CompositorWidgetDelegate* aWidgetDelegate,
                                                  APZCTreeManagerChild* aAPZ,
                                                  const uint64_t& aRootLayerTreeId)
- : CompositorSession(aWidgetDelegate, aChild, aRootLayerTreeId)
- , mAPZ(aAPZ)
+ : CompositorSession(aWidgetDelegate, aChild, aRootLayerTreeId),
+   mWidget(aWidget),
+   mAPZ(aAPZ)
+{
+  GPUProcessManager::Get()->RegisterSession(this);
+}
+
+RemoteCompositorSession::~RemoteCompositorSession()
 {
+  // This should have been shutdown first.
+  MOZ_ASSERT(!mCompositorBridgeChild);
+}
+
+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.
+  MOZ_ASSERT(mWidget);
+  mWidget->NotifyRemoteCompositorSessionLost(this);
 }
 
 CompositorBridgeParent*
 RemoteCompositorSession::GetInProcessBridge() const
 {
   return nullptr;
 }
 
@@ -43,12 +62,14 @@ RemoteCompositorSession::GetAPZCTreeMana
 }
 
 void
 RemoteCompositorSession::Shutdown()
 {
   mCompositorBridgeChild->Destroy();
   mCompositorBridgeChild = nullptr;
   mCompositorWidgetDelegate = nullptr;
+  mWidget = nullptr;
+  GPUProcessManager::Get()->UnregisterSession(this);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/ipc/RemoteCompositorSession.h
+++ b/gfx/ipc/RemoteCompositorSession.h
@@ -11,26 +11,31 @@
 #include "Units.h"
 
 namespace mozilla {
 namespace layers {
 
 class RemoteCompositorSession final : public CompositorSession
 {
 public:
-  RemoteCompositorSession(CompositorBridgeChild* aChild,
+  RemoteCompositorSession(nsBaseWidget* aWidget,
+                          CompositorBridgeChild* aChild,
                           CompositorWidgetDelegate* aWidgetDelegate,
                           APZCTreeManagerChild* aAPZ,
                           const uint64_t& aRootLayerTreeId);
+  ~RemoteCompositorSession() override;
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
   RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
   void Shutdown() override;
 
+  void NotifySessionLost();
+
 private:
+  nsBaseWidget* mWidget;
   RefPtr<APZCTreeManagerChild> mAPZ;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // include_mozilla_gfx_ipc_RemoteCompositorSession_h
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1365,16 +1365,22 @@ void nsBaseWidget::CreateCompositor(int 
 
   if (mWindowType == eWindowType_toplevel) {
     // Only track compositors for top-level windows, since other window types
     // may use the basic compositor.
     gfxPlatform::GetPlatform()->NotifyCompositorCreated(mLayerManager->GetCompositorBackendType());
   }
 }
 
+void nsBaseWidget::NotifyRemoteCompositorSessionLost(CompositorSession* aSession)
+{
+  MOZ_ASSERT(aSession == mCompositorSession);
+  DestroyLayerManager();
+}
+
 bool nsBaseWidget::ShouldUseOffMainThreadCompositing()
 {
   return gfxPlatform::UsesOffMainThreadCompositing();
 }
 
 LayerManager* nsBaseWidget::GetLayerManager(PLayerTransactionChild* aShadowManager,
                                             LayersBackend aBackendHint,
                                             LayerManagerPersistence aPersistence)
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -187,16 +187,24 @@ public:
                                          nsIScreen* aScreen = nullptr) override;
   void                    InfallibleMakeFullScreen(bool aFullScreen,
                                                    nsIScreen* aScreen = nullptr);
 
   virtual LayerManager*   GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                                           LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                                           LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT) override;
 
+  // A remote compositor session tied to this window has been lost and IPC
+  // messages will no longer work. The widget must clean up any lingering
+  // resources and possibly schedule another paint.
+  //
+  // A reference to the session object is held until this function has
+  // returned.
+  void NotifyRemoteCompositorSessionLost(mozilla::layers::CompositorSession* aSession);
+
   mozilla::CompositorVsyncDispatcher* GetCompositorVsyncDispatcher();
   void            CreateCompositorVsyncDispatcher();
   virtual void            CreateCompositor();
   virtual void            CreateCompositor(int aWidth, int aHeight);
   virtual void            PrepareWindowEffects() override {}
   virtual void            UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override {}
   virtual void            SetModal(bool aModal) override {}
   virtual uint32_t        GetMaxTouchPoints() const override;