Bug 1300121 - Make PersistentBufferProviderShared::BorrowDrawTarget more robust agaist bad edge cases and if all fails, fallback to the basic provider. r=Bas
authorNicolas Silva <nsilva@mozilla.com>
Tue, 11 Oct 2016 13:23:11 +0200
changeset 324218 900960e68528c73afb6cfb3d97400bb3311386cf
parent 324217 f96d497c78a7f3d73d8e5079e2fcb1eca80e574f
child 324219 49228a69b071bc200360aa43845b42b996759479
push id34648
push usercbook@mozilla.com
push dateFri, 25 Nov 2016 15:31:22 +0000
treeherderautoland@c4d075822fbf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs1300121
milestone53.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 1300121 - Make PersistentBufferProviderShared::BorrowDrawTarget more robust agaist bad edge cases and if all fails, fallback to the basic provider. r=Bas
dom/canvas/CanvasRenderingContext2D.cpp
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/PersistentBufferProvider.h
gfx/layers/client/CanvasClient.h
gfx/layers/client/ClientCanvasLayer.cpp
gfx/layers/client/ClientCanvasLayer.h
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1636,17 +1636,21 @@ CanvasRenderingContext2D::EnsureTarget(c
       !TrySkiaGLTarget(newTarget, newProvider)) {
     // Fall back to software.
     mode = RenderingMode::SoftwareBackendMode;
   }
 
   if (mode == RenderingMode::SoftwareBackendMode &&
       !TrySharedTarget(newTarget, newProvider) &&
       !TryBasicTarget(newTarget, newProvider)) {
-    gfxCriticalError() << "Failed borrow shared and basic targets.";
+
+    gfxCriticalError(
+      CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))
+    ) << "Failed borrow shared and basic targets.";
+
     SetErrorState();
     return mode;
   }
 
 
   MOZ_ASSERT(newTarget);
   MOZ_ASSERT(newProvider);
 
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "PersistentBufferProvider.h"
 
 #include "Layers.h"
+#include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/gfx/Logging.h"
 #include "pratom.h"
 #include "gfxPlatform.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
@@ -237,18 +238,16 @@ PersistentBufferProviderShared::BorrowDr
     mFwd->GetActiveResourceTracker().AddObject(this);
   }
 
   if (mDrawTarget) {
     RefPtr<gfx::DrawTarget> dt(mDrawTarget);
     return dt.forget();
   }
 
-  mFront = Nothing();
-
   auto previousBackBuffer = mBack;
 
   TextureClient* tex = GetTexture(mBack);
 
   // First try to reuse the current back buffer. If we can do that it means
   // we can skip copying its content to the new back buffer.
   if (tex && tex->IsReadLocked()) {
     // The back buffer is currently used by the compositor, we can't draw
@@ -266,27 +265,42 @@ PersistentBufferProviderShared::BorrowDr
       }
     }
   }
 
   if (!tex) {
     // We have to allocate a new texture.
     if (mTextures.length() >= 4) {
       // We should never need to buffer that many textures, something's wrong.
-      MOZ_ASSERT(false);
       // In theory we throttle the main thread when the compositor can't keep up,
       // so we shoud never get in a situation where we sent 4 textures to the
-      // compositor and the latter as not released any of them.
-      // This seems to happen, however, in some edge cases such as just after a
-      // device reset (cf. Bug 1291163).
-      // It would be pretty bad to keep piling textures up at this point so we
-      // call NotifyInactive to remove some of our textures.
-      NotifyInactive();
-      // Give up now. The caller can fall-back to a non-shared buffer provider.
-      return nullptr;
+      // compositor and the latter has not released any of them.
+      // In practice, though, the throttling mechanism appears to have some issues,
+      // especially when switching between layer managers (during tab-switch).
+      // To make sure we don't get too far ahead of the compositor, we send a
+      // sync ping to the compositor thread...
+      mFwd->SyncWithCompositor();
+      // ...and try again.
+      for (uint32_t i = 0; i < mTextures.length(); ++i) {
+        if (!mTextures[i]->IsReadLocked()) {
+          gfxCriticalNote << "Managed to allocate after flush.";
+          mBack = Some(i);
+          tex = mTextures[i];
+          break;
+        }
+      }
+
+      if (!tex) {
+        gfxCriticalError() << "Unexpected BufferProvider over-production.";
+        // It would be pretty bad to keep piling textures up at this point so we
+        // call NotifyInactive to remove some of our textures.
+        NotifyInactive();
+        // Give up now. The caller can fall-back to a non-shared buffer provider.
+        return nullptr;
+      }
     }
 
     RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
       mFwd, mFormat, mSize,
       BackendSelector::Canvas,
       TextureFlags::DEFAULT,
       TextureAllocationFlags::ALLOC_DEFAULT
     );
@@ -397,16 +411,22 @@ PersistentBufferProviderShared::ReturnSn
   if (front) {
     front->Unlock();
   }
 }
 
 void
 PersistentBufferProviderShared::NotifyInactive()
 {
+  ClearCachedResources();
+}
+
+void
+PersistentBufferProviderShared::ClearCachedResources()
+{
   RefPtr<TextureClient> front = GetTexture(mFront);
   RefPtr<TextureClient> back = GetTexture(mBack);
 
   // Clear all textures (except the front and back ones that we just kept).
   mTextures.clear();
 
   if (back) {
     if (mTextures.append(back)) {
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -61,16 +61,18 @@ public:
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) = 0;
 
   virtual TextureClient* GetTextureClient() { return nullptr; }
 
   virtual void OnShutdown() {}
 
   virtual bool SetForwarder(ShadowLayerForwarder* aFwd) { return true; }
 
+  virtual void ClearCachedResources() {}
+
   /**
    * Return true if this provider preserves the drawing state (clips, transforms,
    * etc.) across frames. In practice this means users of the provider can skip
    * popping all of the clips at the end of the frames and pushing them back at
    * the beginning of the following frames, which can be costly (cf. bug 1294351).
    */
   virtual bool PreservesDrawingState() const = 0;
 };
@@ -132,16 +134,18 @@ public:
   virtual TextureClient* GetTextureClient() override;
 
   virtual void NotifyInactive() override;
 
   virtual void OnShutdown() override { Destroy(); }
 
   virtual bool SetForwarder(ShadowLayerForwarder* aFwd) override;
 
+  virtual void ClearCachedResources() override;
+
   virtual bool PreservesDrawingState() const override { return false; }
 protected:
   PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                  ShadowLayerForwarder* aFwd,
                                  RefPtr<TextureClient>& aTexture);
 
   ~PersistentBufferProviderShared();
 
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -97,17 +97,17 @@ public:
 
   TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::IMAGE, mTextureFlags);
   }
 
   virtual void Clear() override
   {
-    mBackBuffer = mFrontBuffer = nullptr;
+    mBackBuffer = mFrontBuffer = mBufferProviderTexture = nullptr;
   }
 
   virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
 
   virtual void UpdateFromTexture(TextureClient* aBuffer) override;
 
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -26,16 +26,19 @@ using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace layers {
 
 ClientCanvasLayer::~ClientCanvasLayer()
 {
   MOZ_COUNT_DTOR(ClientCanvasLayer);
+  if (mBufferProvider) {
+    mBufferProvider->ClearCachedResources();
+  }
   if (mCanvasClient) {
     mCanvasClient->OnDetach();
     mCanvasClient = nullptr;
   }
 }
 
 void
 ClientCanvasLayer::Initialize(const Data& aData)
--- a/gfx/layers/client/ClientCanvasLayer.h
+++ b/gfx/layers/client/ClientCanvasLayer.h
@@ -51,38 +51,47 @@ public:
   }
 
   virtual void Initialize(const Data& aData) override;
 
   virtual void RenderLayer() override;
 
   virtual void ClearCachedResources() override
   {
+    if (mBufferProvider) {
+      mBufferProvider->ClearCachedResources();
+    }
     if (mCanvasClient) {
       mCanvasClient->Clear();
     }
   }
 
   virtual void HandleMemoryPressure() override
   {
+    if (mBufferProvider) {
+      mBufferProvider->ClearCachedResources();
+    }
     if (mCanvasClient) {
       mCanvasClient->HandleMemoryPressure();
     }
   }
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
   {
     aAttrs = CanvasLayerAttributes(mSamplingFilter, mBounds);
   }
 
   virtual Layer* AsLayer()  override { return this; }
   virtual ShadowableLayer* AsShadowableLayer()  override { return this; }
 
   virtual void Disconnect() override
   {
+    if (mBufferProvider) {
+      mBufferProvider->ClearCachedResources();
+    }
     mCanvasClient = nullptr;
     ClientLayer::Disconnect();
   }
 
   virtual CompositableClient* GetCompositableClient() override
   {
     return mCanvasClient;
   }
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -1045,10 +1045,19 @@ ShadowLayerForwarder::GetCompositorBridg
   }
   if (!mShadowManager) {
     return nullptr;
   }
   mCompositorBridgeChild = static_cast<CompositorBridgeChild*>(mShadowManager->Manager());
   return mCompositorBridgeChild;
 }
 
+void
+ShadowLayerForwarder::SyncWithCompositor()
+{
+  auto compositorBridge = GetCompositorBridgeChild();
+  if (compositorBridge && compositorBridge->IPCOpen()) {
+    compositorBridge->SendSyncWithCompositor();
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -387,16 +387,26 @@ public:
 
   bool InForwarderThread() override {
     return NS_IsMainThread();
   }
 
   // Returns true if aSurface wraps a Shmem.
   static bool IsShmem(SurfaceDescriptor* aSurface);
 
+  /**
+   * Sends a synchronous ping to the compsoitor.
+   *
+   * This is bad for performance and should only be called as a last resort if the
+   * compositor may be blocked for a long period of time, to avoid that the content
+   * process accumulates resource allocations that the compositor is not consuming
+   * and releasing.
+   */
+  void SyncWithCompositor();
+
   TextureForwarder* GetTextureForwarder() override { return GetCompositorBridgeChild(); }
   LayersIPCActor* GetLayersIPCActor() override { return this; }
 
   ActiveResourceTracker& GetActiveResourceTracker() { return *mActiveResourceTracker.get(); }
 protected:
   virtual ~ShadowLayerForwarder();
 
   explicit ShadowLayerForwarder(ClientLayerManager* aClientLayerManager);