Bug 1469964 - Part 2. Make shared surfaces use external image update mechanism. r=nical
authorAndrew Osmond <aosmond@mozilla.com>
Thu, 12 Jul 2018 11:43:12 -0400
changeset 426325 b91599130fba6dff5bc575480f0698f997b49566
parent 426324 6ac886d5a6ad430ede79c0db79046cb38e4431ee
child 426326 0f760232724ff132e82ecf43c409221c3980586c
push id105206
push useraosmond@gmail.com
push dateThu, 12 Jul 2018 15:43:22 +0000
treeherdermozilla-inbound@b91599130fba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1469964
milestone63.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 1469964 - Part 2. Make shared surfaces use external image update mechanism. r=nical
gfx/2d/2D.h
gfx/layers/SourceSurfaceSharedData.h
gfx/layers/ipc/SharedSurfacesChild.cpp
image/imgFrame.cpp
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -594,26 +594,26 @@ public:
    * be used to determine if the memory needs to be cleared to 0.
    */
   virtual bool OnHeap() const
   {
     return true;
   }
 
   /**
-   * Indicates how many times the surface has been invalidated.
+   * Yields a dirty rect of what has changed since it was last called.
    */
-  virtual int32_t Invalidations() const {
-    return -1;
+  virtual Maybe<IntRect> TakeDirtyRect() {
+    return Nothing();
   }
 
   /**
-   * Increment the invalidation counter.
+   * Indicate a region which has changed in the surface.
    */
-  virtual void Invalidate() { }
+  virtual void Invalidate(const IntRect& aDirtyRect) { }
 
 protected:
   bool mIsMapped;
 };
 
 /** This is an abstract object that accepts path segments. */
 class PathSink : public RefCounted<PathSink>
 {
--- a/gfx/layers/SourceSurfaceSharedData.h
+++ b/gfx/layers/SourceSurfaceSharedData.h
@@ -114,17 +114,16 @@ class SourceSurfaceSharedData final : pu
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedData, override)
 
   SourceSurfaceSharedData()
     : mMutex("SourceSurfaceSharedData")
     , mStride(0)
     , mMapCount(0)
     , mHandleCount(0)
-    , mInvalidations(0)
     , mFormat(SurfaceFormat::UNKNOWN)
     , mClosed(false)
     , mFinalized(false)
     , mShared(false)
   {
   }
 
   /**
@@ -244,32 +243,44 @@ public:
    */
   bool IsFinalized() const
   {
     MutexAutoLock lock(mMutex);
     return mFinalized;
   }
 
   /**
-   * Indicates how many times the surface has been invalidated.
+   * Yields a dirty rect of what has changed since it was last called.
    */
-  int32_t Invalidations() const override
+  Maybe<IntRect> TakeDirtyRect() override
   {
     MutexAutoLock lock(mMutex);
-    return mInvalidations;
+    if (mDirtyRect) {
+      Maybe<IntRect> ret = std::move(mDirtyRect);
+      return ret;
+    }
+    return Nothing();
   }
 
   /**
    * Increment the invalidation counter.
    */
-  void Invalidate() override
+  void Invalidate(const IntRect& aDirtyRect) override
   {
     MutexAutoLock lock(mMutex);
-    ++mInvalidations;
-    MOZ_ASSERT(mInvalidations >= 0);
+    if (!aDirtyRect.IsEmpty()) {
+      if (mDirtyRect) {
+        mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect);
+      } else {
+        mDirtyRect = Some(aDirtyRect);
+      }
+    } else {
+      mDirtyRect = Some(IntRect(IntPoint(0, 0), mSize));
+    }
+    MOZ_ASSERT_IF(mDirtyRect, !mDirtyRect->IsEmpty());
   }
 
   /**
    * While a HandleLock exists for the given surface, the shared memory handle
    * cannot be released.
    */
   class MOZ_STACK_CLASS HandleLock final {
   public:
@@ -328,17 +339,17 @@ private:
    * and we have completed sharing will it be released.
    */
   void CloseHandleInternal();
 
   mutable Mutex mMutex;
   int32_t mStride;
   int32_t mMapCount;
   int32_t mHandleCount;
-  int32_t mInvalidations;
+  Maybe<IntRect> mDirtyRect;
   IntSize mSize;
   RefPtr<SharedMemoryBasic> mBuf;
   RefPtr<SharedMemoryBasic> mOldBuf;
   SurfaceFormat mFormat;
   bool mClosed : 1;
   bool mFinalized : 1;
   bool mShared : 1;
 };
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -18,43 +18,38 @@ namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 class SharedSurfacesChild::ImageKeyData final
 {
 public:
   ImageKeyData(WebRenderLayerManager* aManager,
-               const wr::ImageKey& aImageKey,
-               int32_t aInvalidations)
+               const wr::ImageKey& aImageKey)
     : mManager(aManager)
     , mImageKey(aImageKey)
-    , mInvalidations(aInvalidations)
   { }
 
   ImageKeyData(ImageKeyData&& aOther)
     : mManager(std::move(aOther.mManager))
     , mImageKey(aOther.mImageKey)
-    , mInvalidations(aOther.mInvalidations)
   { }
 
   ImageKeyData& operator=(ImageKeyData&& aOther)
   {
     mManager = std::move(aOther.mManager);
     mImageKey = aOther.mImageKey;
-    mInvalidations = aOther.mInvalidations;
     return *this;
   }
 
   ImageKeyData(const ImageKeyData&) = delete;
   ImageKeyData& operator=(const ImageKeyData&) = delete;
 
   RefPtr<WebRenderLayerManager> mManager;
   wr::ImageKey mImageKey;
-  int32_t mInvalidations;
 };
 
 class SharedSurfacesChild::SharedUserData final
 {
 public:
   explicit SharedUserData(const wr::ExternalImageId& aId)
     : mId(aId)
     , mShared(false)
@@ -114,17 +109,17 @@ public:
   void MarkShared()
   {
     MOZ_ASSERT(!mShared);
     mShared = true;
   }
 
   wr::ImageKey UpdateKey(WebRenderLayerManager* aManager,
                          wr::IpcResourceUpdateQueue& aResources,
-                         int32_t aInvalidations)
+                         const Maybe<IntRect>& aDirtyRect)
   {
     MOZ_ASSERT(aManager);
     MOZ_ASSERT(!aManager->IsDestroyed());
 
     // 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
@@ -141,33 +136,32 @@ public:
       } else 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.mInvalidations != aInvalidations) {
-          if (ownsKey) {
-            aManager->AddImageKeyForDiscard(entry.mImageKey);
-          }
-          entry.mInvalidations = aInvalidations;
+        if (!ownsKey) {
           entry.mImageKey = wrBridge->GetNextImageKey();
           aResources.AddExternalImage(mId, entry.mImageKey);
+        } else if (aDirtyRect) {
+          aResources.UpdateExternalImage(mId, entry.mImageKey,
+                                         ViewAs<ImagePixel>(aDirtyRect.ref()));
         }
 
         key = entry.mImageKey;
         found = true;
       }
     }
 
     if (!found) {
       key = aManager->WrBridge()->GetNextImageKey();
-      ImageKeyData data(aManager, key, aInvalidations);
+      ImageKeyData data(aManager, key);
       mKeys.AppendElement(std::move(data));
       aResources.AddExternalImage(mId, key);
     }
 
     return key;
   }
 
 private:
@@ -318,22 +312,22 @@ SharedSurfacesChild::Share(SourceSurface
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSurface);
   MOZ_ASSERT(aManager);
 
   // Each time the surface changes, the producers of SourceSurfaceSharedData
   // surfaces promise to increment the invalidation counter each time the
   // surface has changed. We can use this counter to determine whether or not
   // we should upate our paired ImageKey.
-  int32_t invalidations = aSurface->Invalidations();
+  Maybe<IntRect> dirtyRect = aSurface->TakeDirtyRect();
   SharedUserData* data = nullptr;
   nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data);
   if (NS_SUCCEEDED(rv)) {
     MOZ_ASSERT(data);
-    aKey = data->UpdateKey(aManager, aResources, invalidations);
+    aKey = data->UpdateKey(aManager, aResources, dirtyRect);
   }
 
   return rv;
 }
 
 /* static */ nsresult
 SharedSurfacesChild::Share(ImageContainer* aContainer,
                            WebRenderLayerManager* aManager,
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -614,41 +614,69 @@ imgFrame::ImageUpdated(const nsIntRect& 
   return ImageUpdatedInternal(aUpdateRect);
 }
 
 nsresult
 imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
 {
   mMonitor.AssertCurrentThreadOwns();
 
-  mDecoded.UnionRect(mDecoded, aUpdateRect);
-
   // Clamp to the frame rect to ensure that decoder bugs don't result in a
   // decoded rect that extends outside the bounds of the frame rect.
-  mDecoded.IntersectRect(mDecoded, mFrameRect);
+  IntRect updateRect = mFrameRect.Intersect(aUpdateRect);
+  if (updateRect.IsEmpty()) {
+    return NS_OK;
+  }
+
+  mDecoded.UnionRect(mDecoded, updateRect);
+
+  // Paletted images cannot invalidate.
+  if (mPalettedImageData) {
+    return NS_OK;
+  }
 
   // Update our invalidation counters for any consumers watching for changes
   // in the surface.
   if (mRawSurface) {
-    mRawSurface->Invalidate();
+    mRawSurface->Invalidate(updateRect);
   }
   if (mLockedSurface && mRawSurface != mLockedSurface) {
-    mLockedSurface->Invalidate();
+    mLockedSurface->Invalidate(updateRect);
   }
   return NS_OK;
 }
 
 void
 imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
                  bool aFinalize /* = true */)
 {
   MonitorAutoLock lock(mMonitor);
   MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
 
-  ImageUpdatedInternal(GetRect());
+  if (mPalettedImageData) {
+    ImageUpdatedInternal(mFrameRect);
+  } else if (!mDecoded.IsEqualEdges(mFrameRect)) {
+    // The decoder should have produced rows starting from either the bottom or
+    // the top of the image. We need to calculate the region for which we have
+    // not yet invalidated.
+    IntRect delta(0, 0, mFrameRect.width, 0);
+    if (mDecoded.y == 0) {
+      delta.y = mDecoded.height;
+      delta.height = mFrameRect.height - mDecoded.height;
+    } else if (mDecoded.y + mDecoded.height == mFrameRect.height) {
+      delta.height = mFrameRect.height - mDecoded.y;
+    } else {
+      MOZ_ASSERT_UNREACHABLE("Decoder only updated middle of image!");
+      delta = mFrameRect;
+    }
+
+    ImageUpdatedInternal(delta);
+  }
+
+  MOZ_ASSERT(mDecoded.IsEqualEdges(mFrameRect));
 
   if (aFinalize) {
     FinalizeSurfaceInternal();
   }
 
   mFinished = true;
 
   // The image is now complete, wake up anyone who's waiting.