Bug 1504699 - Part 6. Add lifetime management for recycled frames with WebRender. r=nical
authorAndrew Osmond <aosmond@mozilla.com>
Mon, 05 Nov 2018 07:58:05 -0500
changeset 503962 ecddcae1b26630a30263749186c5250030f72dfb
parent 503961 a687664414491ef27cbca5060c83443814fb0693
child 503963 ccfa531dea0301244c99db4ff26d5610382487e2
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1504699
milestone65.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 1504699 - Part 6. Add lifetime management for recycled frames with WebRender. r=nical Here we make use of the parts added in parts 1 and 2 to hold onto recycled surfaces for as long as necessary to prevent the animated image decoder from reusing them until WebRender is done with them. Differential Revision: https://phabricator.services.mozilla.com/D10902
gfx/layers/ipc/SharedSurfacesChild.cpp
gfx/layers/ipc/SharedSurfacesChild.h
gfx/layers/wr/WebRenderBridgeChild.cpp
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -470,16 +470,39 @@ SharedSurfacesChild::UpdateAnimation(Ima
 
   SharedSurfacesAnimation* anim =
     aContainer->EnsureSharedSurfacesAnimation();
   MOZ_ASSERT(anim);
 
   return anim->SetCurrentFrame(aSurface, sharedSurface, aDirtyRect);
 }
 
+AnimationImageKeyData::AnimationImageKeyData(WebRenderLayerManager* aManager,
+                                             const wr::ImageKey& aImageKey)
+  : SharedSurfacesChild::ImageKeyData(aManager, aImageKey)
+  , mRecycling(false)
+{ }
+
+AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther)
+  : SharedSurfacesChild::ImageKeyData(std::move(aOther))
+  , mPendingRelease(std::move(aOther.mPendingRelease))
+  , mRecycling(aOther.mRecycling)
+{ }
+
+AnimationImageKeyData&
+AnimationImageKeyData::operator=(AnimationImageKeyData&& aOther)
+{
+  mPendingRelease = std::move(aOther.mPendingRelease);
+  mRecycling = aOther.mRecycling;
+  SharedSurfacesChild::ImageKeyData::operator=(std::move(aOther));
+  return *this;
+}
+
+AnimationImageKeyData::~AnimationImageKeyData() = default;
+
 SharedSurfacesAnimation::~SharedSurfacesAnimation()
 {
   MOZ_ASSERT(mKeys.IsEmpty());
 }
 
 void
 SharedSurfacesAnimation::Destroy()
 {
@@ -492,22 +515,42 @@ SharedSurfacesAnimation::Destroy()
   }
 
   if (mKeys.IsEmpty()) {
     return;
   }
 
   for (const auto& entry : mKeys) {
     MOZ_ASSERT(!entry.mManager->IsDestroyed());
+    if (entry.mRecycling) {
+      entry.mManager->DeregisterAsyncAnimation(entry.mImageKey);
+    }
     entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
   }
 
   mKeys.Clear();
 }
 
+void
+SharedSurfacesAnimation::HoldSurfaceForRecycling(AnimationImageKeyData& aEntry,
+                                                 SourceSurface* aParentSurface,
+                                                 SourceSurfaceSharedData* aSurface)
+{
+  if (aParentSurface == static_cast<SourceSurface*>(aSurface)) {
+    return;
+  }
+
+  if (!aEntry.mRecycling) {
+    aEntry.mManager->RegisterAsyncAnimation(aEntry.mImageKey, this);
+    aEntry.mRecycling = true;
+  }
+
+  aEntry.mPendingRelease.AppendElement(aParentSurface);
+}
+
 nsresult
 SharedSurfacesAnimation::SetCurrentFrame(SourceSurface* aParentSurface,
                                          SourceSurfaceSharedData* aSurface,
                                          const gfx::IntRect& aDirtyRect)
 {
   MOZ_ASSERT(aSurface);
 
   SharedSurfacesChild::SharedUserData* data = nullptr;
@@ -517,22 +560,23 @@ SharedSurfacesAnimation::SetCurrentFrame
   }
 
   MOZ_ASSERT(data);
   mId = data->Id();
 
   auto i = mKeys.Length();
   while (i > 0) {
     --i;
-    ImageKeyData& entry = mKeys[i];
+    AnimationImageKeyData& entry = mKeys[i];
     MOZ_ASSERT(!entry.mManager->IsDestroyed());
+
     entry.MergeDirtyRect(Some(aDirtyRect));
-
     Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
     if (dirtyRect) {
+      HoldSurfaceForRecycling(entry, aParentSurface, aSurface);
       auto& resourceUpdates = entry.mManager->AsyncResourceUpdates();
       resourceUpdates.UpdateExternalImage(mId, entry.mImageKey,
                                           ViewAs<ImagePixel>(dirtyRect.ref()));
     }
   }
 
   return NS_OK;
 }
@@ -557,71 +601,98 @@ SharedSurfacesAnimation::UpdateKey(Sourc
   }
 
   // We iterate through all of the items to ensure we clean up the old
   // WebRenderLayerManager references. Most of the time there will be few
   // entries and this should not be particularly expensive compared to the
   // cost of duplicating image keys. In an ideal world, we would generate a
   // single key for the surface, and it would be usable on all of the
   // renderer instances. For now, we must allocate a key for each WR bridge.
-  wr::ImageKey key;
   bool found = false;
   auto i = mKeys.Length();
   while (i > 0) {
     --i;
-    ImageKeyData& entry = mKeys[i];
+    AnimationImageKeyData& entry = mKeys[i];
     MOZ_ASSERT(!entry.mManager->IsDestroyed());
     if (entry.mManager == aManager) {
       WebRenderBridgeChild* wrBridge = aManager->WrBridge();
       MOZ_ASSERT(wrBridge);
 
       // Even if the manager is the same, its underlying WebRenderBridgeChild
       // can change state. If our namespace differs, then our old key has
       // already been discarded.
       bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace;
       if (!ownsKey) {
         entry.mImageKey = wrBridge->GetNextImageKey();
+        HoldSurfaceForRecycling(entry, aParentSurface, aSurface);
         aResources.AddExternalImage(mId, entry.mImageKey);
       } else {
-        Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
-        if (dirtyRect) {
-          aResources.UpdateExternalImage(mId, entry.mImageKey,
-                                         ViewAs<ImagePixel>(dirtyRect.ref()));
-        }
+        MOZ_ASSERT(entry.mDirtyRect.isNothing());
       }
 
       aKey = entry.mImageKey;
       found = true;
       break;
     }
   }
 
   if (!found) {
     aKey = aManager->WrBridge()->GetNextImageKey();
-    ImageKeyData data(aManager, aKey);
+    AnimationImageKeyData data(aManager, aKey);
+    HoldSurfaceForRecycling(data, aParentSurface, aSurface);
     mKeys.AppendElement(std::move(data));
     aResources.AddExternalImage(mId, aKey);
   }
 
   return NS_OK;
 }
 
 void
 SharedSurfacesAnimation::ReleasePreviousFrame(WebRenderLayerManager* aManager,
                                               const wr::ExternalImageId& aId)
 {
+  MOZ_ASSERT(aManager);
+
+  auto i = mKeys.Length();
+  while (i > 0) {
+    --i;
+    AnimationImageKeyData& entry = mKeys[i];
+    MOZ_ASSERT(!entry.mManager->IsDestroyed());
+    if (entry.mManager == aManager) {
+      size_t k;
+      for (k = 0; k < entry.mPendingRelease.Length(); ++k) {
+        auto sharedSurface =
+          SharedSurfacesChild::AsSourceSurfaceSharedData(entry.mPendingRelease[k]);
+        MOZ_ASSERT(sharedSurface);
+
+        Maybe<wr::ExternalImageId> extId =
+          SharedSurfacesChild::GetExternalId(sharedSurface);
+        if (extId && extId.ref() == aId) {
+          break;
+        }
+      }
+
+      if (k == entry.mPendingRelease.Length()) {
+        continue;
+      }
+
+      entry.mPendingRelease.RemoveElementsAt(0, k + 1);
+      break;
+    }
+  }
 }
 
 void
 SharedSurfacesAnimation::Invalidate(WebRenderLayerManager* aManager)
 {
   auto i = mKeys.Length();
   while (i > 0) {
     --i;
     AnimationImageKeyData& entry = mKeys[i];
     if (entry.mManager == aManager) {
       mKeys.RemoveElementAt(i);
+      break;
     }
   }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/SharedSurfacesChild.h
+++ b/gfx/layers/ipc/SharedSurfacesChild.h
@@ -12,16 +12,28 @@
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/StaticPtr.h"          // for StaticRefPtr
 #include "mozilla/gfx/UserData.h"       // for UserDataKey
 #include "mozilla/webrender/WebRenderTypes.h" // for wr::ImageKey
 #include "nsTArray.h"                   // for AutoTArray
 
 namespace mozilla {
+namespace layers {
+class AnimationImageKeyData;
+} // namespace mozilla
+} // namespace layers
+
+template<>
+struct nsTArray_CopyChooser<mozilla::layers::AnimationImageKeyData>
+{
+  typedef nsTArray_CopyWithConstructors<mozilla::layers::AnimationImageKeyData> Type;
+};
+
+namespace mozilla {
 namespace gfx {
 class SourceSurfaceSharedData;
 } // namespace gfx
 
 namespace wr {
 class IpcResourceUpdateQueue;
 } // namespace wr
 
@@ -86,22 +98,16 @@ public:
    */
   static gfx::SourceSurfaceSharedData*
   AsSourceSurfaceSharedData(gfx::SourceSurface* aSurface);
 
   static nsresult UpdateAnimation(ImageContainer* aContainer,
                                   gfx::SourceSurface* aSurface,
                                   const gfx::IntRect& aDirtyRect);
 
-private:
-  SharedSurfacesChild() = delete;
-  ~SharedSurfacesChild() = delete;
-
-  friend class SharedSurfacesAnimation;
-
   class ImageKeyData {
   public:
     ImageKeyData(WebRenderLayerManager* aManager,
                  const wr::ImageKey& aImageKey);
     ~ImageKeyData();
 
     ImageKeyData(ImageKeyData&& aOther);
     ImageKeyData& operator=(ImageKeyData&& aOther);
@@ -115,16 +121,22 @@ private:
       return std::move(mDirtyRect);
     }
 
     RefPtr<WebRenderLayerManager> mManager;
     Maybe<gfx::IntRect> mDirtyRect;
     wr::ImageKey mImageKey;
   };
 
+private:
+  SharedSurfacesChild() = delete;
+  ~SharedSurfacesChild() = delete;
+
+  friend class SharedSurfacesAnimation;
+
   class SharedUserData final {
   public:
     SharedUserData()
       : mShared(false)
     { }
 
     explicit SharedUserData(const wr::ExternalImageId& aId)
       : mId(aId)
@@ -179,16 +191,31 @@ private:
                       bool aReleaseId,
                       nsTArray<ImageKeyData>& aKeys);
 
   static void DestroySharedUserData(void* aClosure);
 
   static gfx::UserDataKey sSharedKey;
 };
 
+class AnimationImageKeyData final : public SharedSurfacesChild::ImageKeyData
+{
+public:
+  AnimationImageKeyData(WebRenderLayerManager* aManager,
+               const wr::ImageKey& aImageKey);
+
+  ~AnimationImageKeyData();
+
+  AnimationImageKeyData(AnimationImageKeyData&& aOther);
+  AnimationImageKeyData& operator=(AnimationImageKeyData&& aOther);
+
+  AutoTArray<RefPtr<gfx::SourceSurface>, 2> mPendingRelease;
+  bool mRecycling;
+};
+
 /**
  * This helper class owns a single ImageKey which will map to different external
  * image IDs representing different frames in an animation.
  */
 class SharedSurfacesAnimation final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSurfacesAnimation)
@@ -238,18 +265,20 @@ public:
    * Destroy any state information bound for the given layer manager. Any
    * image keys are already invalid.
    */
   void Invalidate(WebRenderLayerManager* aManager);
 
 private:
   ~SharedSurfacesAnimation();
 
-  typedef SharedSurfacesChild::ImageKeyData ImageKeyData;
+  void HoldSurfaceForRecycling(AnimationImageKeyData& aEntry,
+                               gfx::SourceSurface* aParentSurface,
+                               gfx::SourceSurfaceSharedData* aSurface);
 
-  AutoTArray<ImageKeyData, 1> mKeys;
+  AutoTArray<AnimationImageKeyData, 1> mKeys;
   wr::ExternalImageId mId;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -569,16 +569,19 @@ WebRenderBridgeChild::RecvWrUpdated(cons
   mFontInstanceKeys.Clear();
   mFontKeys.Clear();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeChild::RecvWrReleasedImages(nsTArray<wr::ExternalImageKeyPair>&& aPairs)
 {
+  if (mManager) {
+    mManager->WrReleasedImages(aPairs);
+  }
   return IPC_OK();
 }
 
 void
 WebRenderBridgeChild::BeginClearCachedResources()
 {
   mIsInClearCachedResources = true;
   // Clear display list and animtaions at parent side before clearing cached