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 351801 31fe465d39326229db0d29381b6a088b50fb73df
parent 351800 0cf317263531dd81bc757ece521014e7e4a9d529
child 351802 8df86e93a057fd7495a8dca8c88e02922b64f950
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs1313199
milestone52.0a1
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;