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 324248 900960e68528c73afb6cfb3d97400bb3311386cf
parent 324247 f96d497c78a7f3d73d8e5079e2fcb1eca80e574f
child 324249 49228a69b071bc200360aa43845b42b996759479
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersBas
bugs1300121
milestone53.0a1
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);