Bug 1313199 - Sync a device reset from GPU process to main process. r=dvander
authorRyan Hunt <rhunt@eqrion.net>
Sun, 06 Nov 2016 13:01:52 -0600
changeset 321756 31fe465d39326229db0d29381b6a088b50fb73df
parent 321755 0cf317263531dd81bc757ece521014e7e4a9d529
child 321757 8df86e93a057fd7495a8dca8c88e02922b64f950
push id30934
push usercbook@mozilla.com
push dateWed, 09 Nov 2016 15:38:21 +0000
treeherdermozilla-central@336759fad462 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs1313199
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
Bug 1313199 - Sync a device reset from GPU process to main process. r=dvander
gfx/ipc/GPUChild.cpp
gfx/ipc/GPUChild.h
gfx/ipc/GPUParent.cpp
gfx/ipc/GPUParent.h
gfx/ipc/GPUProcessHost.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
gfx/ipc/PGPU.ipdl
gfx/ipc/RemoteCompositorSession.cpp
gfx/ipc/RemoteCompositorSession.h
gfx/layers/d3d11/CompositorD3D11.cpp
gfx/thebes/DeviceManagerDx.cpp
gfx/thebes/DeviceManagerDx.h
widget/nsBaseWidget.h
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "GPUChild.h"
 #include "gfxConfig.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
+#include "GPUProcessManager.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/CheckerboardReportService.h"
 #include "mozilla/gfx/gfxVars.h"
 #if defined(XP_WIN)
 # include "mozilla/gfx/DeviceManagerDx.h"
 #endif
 #include "mozilla/ipc/CrashReporterHost.h"
 
@@ -146,16 +147,23 @@ GPUChild::RecvAccumulateChildHistogram(I
 
 bool
 GPUChild::RecvAccumulateChildKeyedHistogram(InfallibleTArray<KeyedAccumulation>&& aAccumulations)
 {
   Telemetry::AccumulateChildKeyed(GeckoProcessType_GPU, aAccumulations);
   return true;
 }
 
+bool
+GPUChild::RecvNotifyDeviceReset()
+{
+  mHost->mListener->OnProcessDeviceReset(mHost);
+  return true;
+}
+
 void
 GPUChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (aWhy == AbnormalShutdown) {
 #ifdef MOZ_CRASHREPORTER
     if (mCrashReporter) {
       mCrashReporter->GenerateCrashReport(OtherPid());
       mCrashReporter = nullptr;
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -38,16 +38,17 @@ public:
   bool RecvInitComplete(const GPUDeviceData& aData) override;
   bool RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override;
   bool RecvInitCrashReporter(Shmem&& shmem) override;
   bool RecvAccumulateChildHistogram(InfallibleTArray<Accumulation>&& aAccumulations) override;
   bool RecvAccumulateChildKeyedHistogram(InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   bool RecvGraphicsError(const nsCString& aError) override;
   bool RecvNotifyUiObservers(const nsCString& aTopic) override;
+  bool RecvNotifyDeviceReset() override;
 
   static void Destroy(UniquePtr<GPUChild>&& aChild);
 
 private:
   GPUProcessHost* mHost;
   UniquePtr<ipc::CrashReporterHost> mCrashReporter;
   bool mGPUReady;
 };
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -109,16 +109,40 @@ GPUParent::Init(base::ProcessId aParentP
   LayerTreeOwnerTracker::Initialize();
   mozilla::ipc::SetThisProcessName("GPU Process");
 #ifdef XP_WIN
   wmf::MFStartup();
 #endif
   return true;
 }
 
+void
+GPUParent::NotifyDeviceReset()
+{
+  if (!NS_IsMainThread()) {
+    NS_DispatchToMainThread(NS_NewRunnableFunction([] () -> void {
+      GPUParent::GetSingleton()->NotifyDeviceReset();
+    }));
+    return;
+  }
+
+  // Reset and reinitialize the compositor devices
+#ifdef XP_WIN
+  if (!DeviceManagerDx::Get()->MaybeResetAndReacquireDevices()) {
+    // If the device doesn't need to be reset then the device
+    // has already been reset by a previous NotifyDeviceReset message.
+    return;
+  }
+#endif
+
+  // Notify the main process that there's been a device reset
+  // and that they should reset their compositors and repaint
+  Unused << SendNotifyDeviceReset();
+}
+
 bool
 GPUParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs,
                     nsTArray<GfxVarUpdate>&& vars,
                     const DevicePrefs& devicePrefs)
 {
   const nsTArray<gfxPrefs::Pref*>& globalPrefs = gfxPrefs::all();
   for (auto& setting : prefs) {
     gfxPrefs::Pref* pref = globalPrefs[setting.index()];
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -20,16 +20,17 @@ public:
   GPUParent();
   ~GPUParent();
 
   static GPUParent* GetSingleton();
 
   bool Init(base::ProcessId aParentPid,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
+  void NotifyDeviceReset();
 
   bool RecvInit(nsTArray<GfxPrefSetting>&& prefs,
                 nsTArray<GfxVarUpdate>&& vars,
                 const DevicePrefs& devicePrefs) override;
   bool RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
   bool RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   bool RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
   bool RecvUpdatePref(const GfxPrefSetting& pref) override;
--- a/gfx/ipc/GPUProcessHost.h
+++ b/gfx/ipc/GPUProcessHost.h
@@ -38,16 +38,19 @@ public:
     virtual void OnProcessLaunchComplete(GPUProcessHost* aHost)
     {}
 
     // The GPUProcessHost has unexpectedly shutdown or had its connection
     // severed. This is not called if an error occurs after calling
     // Shutdown().
     virtual void OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
     {}
+
+    virtual void OnProcessDeviceReset(GPUProcessHost* aHost)
+    {}
   };
 
 public:
   explicit GPUProcessHost(Listener* listener);
   ~GPUProcessHost();
 
   // Launch the subprocess asynchronously. On failure, false is returned.
   // Otherwise, true is returned, and the OnLaunchComplete listener callback
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -253,16 +253,24 @@ GPUProcessManager::OnProcessLaunchComple
   nsTArray<LayerTreeIdMapping> mappings;
   LayerTreeOwnerTracker::Get()->Iterate([&](uint64_t aLayersId, base::ProcessId aProcessId) {
     mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId));
   });
   mGPUChild->SendAddLayerTreeIdMapping(mappings);
 }
 
 void
+GPUProcessManager::OnProcessDeviceReset(GPUProcessHost* aHost)
+{
+  for (auto& session : mRemoteSessions) {
+    session->NotifyDeviceReset();
+  }
+}
+
+void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
 
   if (mNumProcessAttempts > uint32_t(gfxPrefs::GPUProcessDevMaxRestarts())) {
     DisableGPUProcess("GPU processed crashed too many times");
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -116,16 +116,17 @@ public:
   // associated resources that live only on the compositor thread.
   //
   // Must run on the content main thread.
   uint64_t AllocateLayerTreeId();
 
 
   void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
   void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
+  void OnProcessDeviceReset(GPUProcessHost* aHost) override;
 
   // Notify the GPUProcessManager that a top-level PGPU protocol has been
   // terminated. This may be called from any thread.
   void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
 
   void AddListener(GPUProcessListener* aListener);
   void RemoveListener(GPUProcessListener* aListener);
 
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -93,12 +93,14 @@ child:
 
   // Have a message be broadcasted to the UI process by the UI process
   // observer service.
   async NotifyUiObservers(nsCString aTopic);
 
   // Messages for reporting telemetry to the UI process.
   async AccumulateChildHistogram(Accumulation[] accumulations);
   async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations);
+
+  async NotifyDeviceReset();
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/RemoteCompositorSession.cpp
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -34,16 +34,23 @@ RemoteCompositorSession::RemoteComposito
 
 RemoteCompositorSession::~RemoteCompositorSession()
 {
   // This should have been shutdown first.
   MOZ_ASSERT(!mCompositorBridgeChild);
 }
 
 void
+RemoteCompositorSession::NotifyDeviceReset()
+{
+  MOZ_ASSERT(mWidget);
+  mWidget->OnRenderingDeviceReset();
+}
+
+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);
 }
--- a/gfx/ipc/RemoteCompositorSession.h
+++ b/gfx/ipc/RemoteCompositorSession.h
@@ -26,16 +26,17 @@ public:
   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;
   void Shutdown() override;
 
+  void NotifyDeviceReset();
   void NotifySessionLost();
 
 private:
   nsBaseWidget* mWidget;
   RefPtr<APZCTreeManagerChild> mAPZ;
   RefPtr<GeckoContentController> mContentController;
 };
 
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -7,16 +7,17 @@
 
 #include "TextureD3D11.h"
 #include "CompositorD3D11Shaders.h"
 
 #include "gfxWindowsPlatform.h"
 #include "nsIWidget.h"
 #include "mozilla/gfx/D3D11Checks.h"
 #include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/GPUParent.h"
 #include "mozilla/layers/ImageHost.h"
 #include "mozilla/layers/ContentHost.h"
 #include "mozilla/layers/Effects.h"
 #include "nsWindowsHelpers.h"
 #include "gfxPrefs.h"
 #include "gfxConfig.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxUtils.h"
@@ -987,16 +988,23 @@ CompositorD3D11::BeginFrame(const nsIntR
     *aRenderBoundsOut = IntRect();
     return;
   }
 
   if (mDevice->GetDeviceRemovedReason() != S_OK) {
     gfxCriticalNote << "GFX: D3D11 skip BeginFrame with device-removed.";
     ReadUnlockTextures();
     *aRenderBoundsOut = IntRect();
+
+    // If we are in the GPU process then the main process doesn't
+    // know that a device reset has happened and needs to be informed
+    if (XRE_IsGPUProcess()) {
+      GPUParent::GetSingleton()->NotifyDeviceReset();
+    }
+
     return;
   }
 
   LayoutDeviceIntSize oldSize = mSize;
 
   // Failed to create a render target or the view.
   if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView ||
       mSize.width <= 0 || mSize.height <= 0) {
--- a/gfx/thebes/DeviceManagerDx.cpp
+++ b/gfx/thebes/DeviceManagerDx.cpp
@@ -578,16 +578,42 @@ DeviceManagerDx::ResetDevices()
   mAdapter = nullptr;
   mCompositorDevice = nullptr;
   mContentDevice = nullptr;
   mDeviceStatus = Nothing();
   Factory::SetDirect3D11Device(nullptr);
 }
 
 bool
+DeviceManagerDx::MaybeResetAndReacquireDevices()
+{
+  DeviceResetReason resetReason;
+  if (!GetAnyDeviceRemovedReason(&resetReason)) {
+    return false;
+  }
+
+  Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
+
+  bool createCompositorDevice = !!mCompositorDevice;
+  bool createContentDevice = !!mContentDevice;
+
+  ResetDevices();
+
+  if (createCompositorDevice && !CreateCompositorDevices()) {
+    // Just stop, don't try anything more
+    return true;
+  }
+  if (createContentDevice) {
+    CreateContentDevices();
+  }
+
+  return true;
+}
+
+bool
 DeviceManagerDx::ContentAdapterIsParentAdapter(ID3D11Device* device)
 {
   DXGI_ADAPTER_DESC desc;
   if (!D3D11Checks::GetDxgiDesc(device, &desc)) {
     gfxCriticalNote << "Could not query device DXGI adapter info";
     return false;
   }
 
--- a/gfx/thebes/DeviceManagerDx.h
+++ b/gfx/thebes/DeviceManagerDx.h
@@ -75,16 +75,21 @@ public:
 
   void ResetDevices();
   void InitializeDirectDraw();
 
   // Call GetDeviceRemovedReason on each device until one returns
   // a failure.
   bool GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason);
 
+  // Reset and reacquire the devices if a reset has happened.
+  // Returns whether a reset occurred not whether reacquiring
+  // was successful.
+  bool MaybeResetAndReacquireDevices();
+
   // Test whether we can acquire a DXGI 1.2-compatible adapter. This should
   // only be called on startup before devices are initialized.
   bool CheckRemotePresentSupport();
 
 private:
   IDXGIAdapter1 *GetDXGIAdapter();
 
   void DisableD3D11AfterCrash();
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -53,16 +53,17 @@ class BasicLayerManager;
 class CompositorBridgeChild;
 class CompositorBridgeParent;
 class IAPZCTreeManager;
 class GeckoContentController;
 class APZEventState;
 class CompositorSession;
 class ImageContainer;
 struct ScrollableLayerGuid;
+class RemoteCompositorSession;
 } // namespace layers
 
 namespace widget {
 class CompositorWidgetDelegate;
 class InProcessCompositorWidget;
 class WidgetRenderingContext;
 } // namespace widget
 
@@ -107,16 +108,17 @@ public:
  * class, but it gives them a head start.)
  */
 
 class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference
 {
   friend class nsAutoRollup;
   friend class DispatchWheelEventOnMainThread;
   friend class mozilla::widget::InProcessCompositorWidget;
+  friend class mozilla::layers::RemoteCompositorSession;
 
 protected:
   typedef base::Thread Thread;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::layers::BasicLayerManager BasicLayerManager;
   typedef mozilla::layers::BufferMode BufferMode;
   typedef mozilla::layers::CompositorBridgeChild CompositorBridgeChild;