Bug 1162824 - Change box shadow cache to cache the colored blurred box shadow. r=mstange
authorMason Chang <mchang@mozilla.com>
Thu, 25 Jun 2015 12:04:21 -0700
changeset 250212 869aca2db34d2a3f5a7afe2a6449b2c91d62e604
parent 250211 28bbd1fb7ed10f2ec33b1b0129ed3ad335e5ae59
child 250213 5ddf0a252b086c7e8e468243c6251af5c0b1da57
push id61480
push usermchang@mozilla.com
push dateThu, 25 Jun 2015 19:04:33 +0000
treeherdermozilla-inbound@869aca2db34d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1162824
milestone41.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 1162824 - Change box shadow cache to cache the colored blurred box shadow. r=mstange
gfx/2d/PathHelpers.h
gfx/thebes/gfxBlur.cpp
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -187,16 +187,23 @@ struct RectCornerRadii {
   const Size& operator[](size_t aCorner) const {
     return radii[aCorner];
   }
 
   Size& operator[](size_t aCorner) {
     return radii[aCorner];
   }
 
+  bool operator==(const RectCornerRadii& aOther) const {
+    for (size_t i = 0; i < RectCorner::Count; i++) {
+      if (radii[i] != aOther.radii[i]) return false;
+    }
+    return true;
+  }
+
   void Scale(Float aXScale, Float aYScale) {
     for (int i = 0; i < RectCorner::Count; i++) {
       radii[i].Scale(aXScale, aYScale);
     }
   }
 
   const Size TopLeft() const { return radii[RectCorner::TopLeft]; }
   Size& TopLeft() { return radii[RectCorner::TopLeft]; }
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -160,88 +160,102 @@ IntSize gfxAlphaBoxBlur::CalculateBlurRa
     return IntSize(size.width, size.height);
 }
 
 struct BlurCacheKey : public PLDHashEntryHdr {
   typedef const BlurCacheKey& KeyType;
   typedef const BlurCacheKey* KeyTypePointer;
   enum { ALLOW_MEMMOVE = true };
 
-  gfxRect mRect;
+  IntSize mMinSize;
   IntSize mBlurRadius;
-  gfxRect mSkipRect;
+  gfxRGBA mShadowColor;
   BackendType mBackend;
+  RectCornerRadii mCornerRadii;
 
-  BlurCacheKey(const gfxRect& aRect, const IntSize &aBlurRadius, const gfxRect& aSkipRect, BackendType aBackend)
-    : mRect(aRect)
+  BlurCacheKey(IntSize aMinimumSize, gfxIntSize aBlurRadius,
+               RectCornerRadii* aCornerRadii, gfxRGBA aShadowColor,
+               BackendType aBackend)
+    : mMinSize(aMinimumSize)
     , mBlurRadius(aBlurRadius)
-    , mSkipRect(aSkipRect)
+    , mShadowColor(aShadowColor)
     , mBackend(aBackend)
+    , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii())
   { }
 
   explicit BlurCacheKey(const BlurCacheKey* aOther)
-    : mRect(aOther->mRect)
+    : mMinSize(aOther->mMinSize)
     , mBlurRadius(aOther->mBlurRadius)
-    , mSkipRect(aOther->mSkipRect)
+    , mShadowColor(aOther->mShadowColor)
     , mBackend(aOther->mBackend)
+    , mCornerRadii(aOther->mCornerRadii)
   { }
 
   static PLDHashNumber
   HashKey(const KeyTypePointer aKey)
   {
-    PLDHashNumber hash = HashBytes(&aKey->mRect.x, 4 * sizeof(gfxFloat));
+    PLDHashNumber hash = 0;
+    hash = AddToHash(hash, aKey->mMinSize.width, aKey->mMinSize.height);
     hash = AddToHash(hash, aKey->mBlurRadius.width, aKey->mBlurRadius.height);
-    hash = AddToHash(hash, HashBytes(&aKey->mSkipRect.x, 4 * sizeof(gfxFloat)));
+
+    hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.r, sizeof(gfxFloat)));
+    hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.g, sizeof(gfxFloat)));
+    hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.b, sizeof(gfxFloat)));
+    hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.a, sizeof(gfxFloat)));
+
+    for (int i = 0; i < 4; i++) {
+    hash = AddToHash(hash, aKey->mCornerRadii[i].width, aKey->mCornerRadii[i].height);
+    }
+
     hash = AddToHash(hash, (uint32_t)aKey->mBackend);
     return hash;
   }
 
   bool KeyEquals(KeyTypePointer aKey) const
   {
-    if (aKey->mRect.IsEqualInterior(mRect) &&
+    if (aKey->mMinSize == mMinSize &&
         aKey->mBlurRadius == mBlurRadius &&
-        aKey->mSkipRect.IsEqualInterior(mSkipRect) &&
+        aKey->mCornerRadii == mCornerRadii &&
+        aKey->mShadowColor == mShadowColor &&
         aKey->mBackend == mBackend) {
       return true;
-    }
-    return false;
+     }
+
+     return false;
   }
   static KeyTypePointer KeyToPointer(KeyType aKey)
   {
     return &aKey;
   }
 };
 
 /**
  * This class is what is cached. It need to be allocated in an object separated
  * to the cache entry to be able to be tracked by the nsExpirationTracker.
  * */
 struct BlurCacheData {
-  BlurCacheData(SourceSurface* aBlur, const IntPoint& aTopLeft, const gfxRect& aDirtyRect, const BlurCacheKey& aKey)
+  BlurCacheData(SourceSurface* aBlur, IntMargin aExtendDestBy, const BlurCacheKey& aKey)
     : mBlur(aBlur)
-    , mTopLeft(aTopLeft)
-    , mDirtyRect(aDirtyRect)
+    , mExtendDest(aExtendDestBy)
     , mKey(aKey)
   {}
 
   BlurCacheData(const BlurCacheData& aOther)
     : mBlur(aOther.mBlur)
-    , mTopLeft(aOther.mTopLeft)
-    , mDirtyRect(aOther.mDirtyRect)
+    , mExtendDest(aOther.mExtendDest)
     , mKey(aOther.mKey)
   { }
 
   nsExpirationState *GetExpirationState() {
     return &mExpirationState;
   }
 
   nsExpirationState mExpirationState;
   RefPtr<SourceSurface> mBlur;
-  IntPoint mTopLeft;
-  gfxRect mDirtyRect;
+  IntMargin mExtendDest;
   BlurCacheKey mKey;
 };
 
 /**
  * This class implements a cache with no maximum size, that retains the
  * SourceSurfaces used to draw the blurs.
  *
  * An entry stays in the cache as long as it is used often.
@@ -255,29 +269,27 @@ class BlurCache final : public nsExpirat
     }
 
     virtual void NotifyExpired(BlurCacheData* aObject)
     {
       RemoveObject(aObject);
       mHashEntries.Remove(aObject->mKey);
     }
 
-    BlurCacheData* Lookup(const gfxRect& aRect,
-                          const IntSize& aBlurRadius,
-                          const gfxRect& aSkipRect,
-                          BackendType aBackendType,
-                          const gfxRect* aDirtyRect)
+    BlurCacheData* Lookup(const IntSize aMinSize,
+                          const gfxIntSize& aBlurRadius,
+                          RectCornerRadii* aCornerRadii,
+                          const gfxRGBA& aShadowColor,
+                          BackendType aBackendType)
     {
       BlurCacheData* blur =
-        mHashEntries.Get(BlurCacheKey(aRect, aBlurRadius, aSkipRect, aBackendType));
-
+        mHashEntries.Get(BlurCacheKey(aMinSize, aBlurRadius,
+                                      aCornerRadii, aShadowColor,
+                                      aBackendType));
       if (blur) {
-        if (aDirtyRect && !blur->mDirtyRect.Contains(*aDirtyRect)) {
-          return nullptr;
-        }
         MarkUsed(blur);
       }
 
       return blur;
     }
 
     // Returns true if we successfully register the blur in the cache, false
     // otherwise.
@@ -302,158 +314,120 @@ class BlurCache final : public nsExpirat
      * FIXME use nsTHashtable to avoid duplicating the BlurCacheKey.
      * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
      */
     nsClassHashtable<BlurCacheKey, BlurCacheData> mHashEntries;
 };
 
 static BlurCache* gBlurCache = nullptr;
 
-SourceSurface*
-GetCachedBlur(DrawTarget *aDT,
-              const gfxRect& aRect,
-              const IntSize& aBlurRadius,
-              const gfxRect& aSkipRect,
-              const gfxRect& aDirtyRect,
-              IntPoint* aTopLeft)
-{
-  if (!gBlurCache) {
-    gBlurCache = new BlurCache();
-  }
-  BlurCacheData* cached = gBlurCache->Lookup(aRect, aBlurRadius, aSkipRect,
-                                             aDT->GetBackendType(),
-                                             &aDirtyRect);
-  if (cached) {
-    *aTopLeft = cached->mTopLeft;
-    return cached->mBlur;
-  }
-  return nullptr;
-}
-
-void
-CacheBlur(DrawTarget *aDT,
-          const gfxRect& aRect,
-          const IntSize& aBlurRadius,
-          const gfxRect& aSkipRect,
-          SourceSurface* aBlur,
-          const IntPoint& aTopLeft,
-          const gfxRect& aDirtyRect)
-{
-  // If we already had a cached value with this key, but an incorrect dirty region then just update
-  // the existing entry
-  if (BlurCacheData* cached = gBlurCache->Lookup(aRect, aBlurRadius, aSkipRect,
-                                                 aDT->GetBackendType(),
-                                                 nullptr)) {
-    cached->mBlur = aBlur;
-    cached->mTopLeft = aTopLeft;
-    cached->mDirtyRect = aDirtyRect;
-    return;
-  }
-
-  BlurCacheKey key(aRect, aBlurRadius, aSkipRect, aDT->GetBackendType());
-  BlurCacheData* data = new BlurCacheData(aBlur, aTopLeft, aDirtyRect, key);
-  if (!gBlurCache->RegisterEntry(data)) {
-    delete data;
-  }
-}
-
-void
-gfxAlphaBoxBlur::ShutdownBlurCache()
-{
-  delete gBlurCache;
-  gBlurCache = nullptr;
-}
-
 static IntSize
-ComputeMinimalSizeForShadowShape(RectCornerRadii* aCornerRadii,
-                                 gfxIntSize aBlurRadius,
-                                 IntMargin& aSlice)
+ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii,
+                             gfxIntSize aBlurRadius,
+                             IntMargin& aSlice,
+                             const IntSize& aRectSize)
 {
   float cornerWidth = 0;
   float cornerHeight = 0;
   if (aCornerRadii) {
     RectCornerRadii corners = *aCornerRadii;
     for (size_t i = 0; i < 4; i++) {
       cornerWidth = std::max(cornerWidth, corners[i].width);
       cornerHeight = std::max(cornerHeight, corners[i].height);
     }
   }
 
   aSlice = IntMargin(ceil(cornerHeight) + aBlurRadius.height,
                      ceil(cornerWidth) + aBlurRadius.width,
                      ceil(cornerHeight) + aBlurRadius.height,
                      ceil(cornerWidth) + aBlurRadius.width);
 
-  // Include 1 pixel for the stretchable strip in the middle.
-  return IntSize(aSlice.LeftRight() + 1,
-                 aSlice.TopBottom() + 1);
+  IntSize minSize(aSlice.LeftRight() + 1,
+                      aSlice.TopBottom() + 1);
+
+  // If aRectSize is smaller than minSize, the border-image approach won't
+  // work; there's no way to squeeze parts of the min box-shadow source
+  // image such that the result looks correct. So we need to adjust minSize
+  // in such a way that we can later draw it without stretching in the affected
+  // dimension. We also need to adjust "slice" to ensure that we're not trying
+  // to slice away more than we have.
+  if (aRectSize.width < minSize.width) {
+    minSize.width = aRectSize.width;
+    aSlice.left = 0;
+    aSlice.right = 0;
+  }
+  if (aRectSize.height < minSize.height) {
+    minSize.height = aRectSize.height;
+    aSlice.top = 0;
+    aSlice.bottom = 0;
+  }
+
+  MOZ_ASSERT(aSlice.LeftRight() <= minSize.width);
+  MOZ_ASSERT(aSlice.TopBottom() <= minSize.height);
+  return minSize;
+}
+
+void
+CacheBlur(DrawTarget& aDT,
+          const IntSize& aMinSize,
+          const gfxIntSize& aBlurRadius,
+          RectCornerRadii* aCornerRadii,
+          const gfxRGBA& aShadowColor,
+          IntMargin aExtendDest,
+          SourceSurface* aBoxShadow)
+{
+  BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT.GetBackendType());
+  BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key);
+  if (!gBlurCache->RegisterEntry(data)) {
+    delete data;
+  }
 }
 
 // Blurs a small surface and creates the mask.
 static TemporaryRef<SourceSurface>
 CreateBlurMask(const IntSize& aRectSize,
                RectCornerRadii* aCornerRadii,
                gfxIntSize aBlurRadius,
                IntMargin& aExtendDestBy,
                IntMargin& aSliceBorder,
                DrawTarget& aDestDrawTarget)
 {
   IntMargin slice;
-  IntSize minimalSize =
-    ComputeMinimalSizeForShadowShape(aCornerRadii, aBlurRadius, slice);
+  gfxAlphaBoxBlur blur;
+  IntSize minSize =
+    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, slice, aRectSize);
+  IntRect minRect(IntPoint(), minSize);
 
-  // If aRectSize is smaller than minimalSize, the border-image approach won't
-  // work; there's no way to squeeze parts of the minimal box-shadow source
-  // image such that the result looks correct. So we need to adjust minimalSize
-  // in such a way that we can later draw it without stretching in the affected
-  // dimension. We also need to adjust "slice" to ensure that we're not trying
-  // to slice away more than we have.
-  if (aRectSize.width < minimalSize.width) {
-    minimalSize.width = aRectSize.width;
-    slice.left = 0;
-    slice.right = 0;
-  }
-  if (aRectSize.height < minimalSize.height) {
-    minimalSize.height = aRectSize.height;
-    slice.top = 0;
-    slice.bottom = 0;
-  }
+  gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), gfxIntSize(),
+                                  aBlurRadius, nullptr, nullptr);
 
-  MOZ_ASSERT(slice.LeftRight() <= minimalSize.width);
-  MOZ_ASSERT(slice.TopBottom() <= minimalSize.height);
-
-  IntRect minimalRect(IntPoint(), minimalSize);
-
-  gfxAlphaBoxBlur blur;
-  gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minimalRect)), gfxIntSize(),
-                                  aBlurRadius, nullptr, nullptr);
   if (!blurCtx) {
     return nullptr;
   }
 
   DrawTarget* blurDT = blurCtx->GetDrawTarget();
   ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
 
   if (aCornerRadii) {
     RefPtr<Path> roundedRect =
-      MakePathForRoundedRect(*blurDT, Rect(minimalRect), *aCornerRadii);
+      MakePathForRoundedRect(*blurDT, Rect(minRect), *aCornerRadii);
     blurDT->Fill(roundedRect, black);
   } else {
-    blurDT->FillRect(Rect(minimalRect), black);
+    blurDT->FillRect(Rect(minRect), black);
   }
 
   IntPoint topLeft;
   RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft);
 
-  IntRect expandedMinimalRect(topLeft, result->GetSize());
-  aExtendDestBy = expandedMinimalRect - minimalRect;
+  IntRect expandedMinRect(topLeft, result->GetSize());
+  aExtendDestBy = expandedMinRect - minRect;
   aSliceBorder = slice + aExtendDestBy;
 
-  MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinimalRect.width);
-  MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinimalRect.height);
+  MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
+  MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);
 
   return result.forget();
 }
 
 static TemporaryRef<SourceSurface>
 CreateBoxShadow(DrawTarget& aDT, SourceSurface* aBlurMask, const gfxRGBA& aShadowColor)
 {
   IntSize blurredSize = aBlurMask->GetSize();
@@ -462,16 +436,61 @@ CreateBoxShadow(DrawTarget& aDT, SourceS
     platform->CreateOffscreenContentDrawTarget(blurredSize, SurfaceFormat::B8G8R8A8);
   MOZ_ASSERT(boxShadowDT->GetType() == aDT.GetType());
 
   ColorPattern shadowColor(ToDeviceColor(aShadowColor));
   boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
   return boxShadowDT->Snapshot();
 }
 
+SourceSurface*
+GetBlur(DrawTarget& aDT,
+        const IntSize& aRectSize,
+        const gfxIntSize& aBlurRadius,
+        RectCornerRadii* aCornerRadii,
+        const gfxRGBA& aShadowColor,
+        IntMargin& aExtendDestBy,
+        IntMargin& aSlice)
+{
+  if (!gBlurCache) {
+    gBlurCache = new BlurCache();
+  }
+
+  IntSize minSize =
+    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize);
+
+  BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
+                                             aCornerRadii, aShadowColor,
+                                             aDT.GetBackendType());
+  if (cached) {
+    // See CreateBlurMask() for these values
+    aExtendDestBy = cached->mExtendDest;
+    aSlice = aSlice + aExtendDestBy;
+    return cached->mBlur;
+  }
+
+  RefPtr<SourceSurface> blurMask =
+    CreateBlurMask(aRectSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, aDT);
+
+  if (!blurMask) {
+    return nullptr;
+  }
+
+  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(aDT, blurMask, aShadowColor);
+  CacheBlur(aDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, aExtendDestBy, boxShadow);
+  return boxShadow;
+}
+
+void
+gfxAlphaBoxBlur::ShutdownBlurCache()
+{
+  delete gBlurCache;
+  gBlurCache = nullptr;
+}
+
 static Rect
 RectWithEdgesTRBL(Float aTop, Float aRight, Float aBottom, Float aLeft)
 {
   return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop);
 }
 
 static void
 RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface,
@@ -520,39 +539,39 @@ DrawCorner(DrawTarget& aDT, SourceSurfac
  * with the given shadow color to create a minimally-sized box shadow of the
  * right color. Finally, we cut out the 9 parts from the box-shadow source and
  * paint each part in the right place, stretching the non-corner parts to fill
  * the space between the corners.
  */
 
 
 /* static */ void
-gfxAlphaBoxBlur::BlurRectangle(gfxContext *aDestinationCtx,
+gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
                                const gfxRect& aRect,
                                RectCornerRadii* aCornerRadii,
                                const gfxPoint& aBlurStdDev,
                                const gfxRGBA& aShadowColor,
                                const gfxRect& aDirtyRect,
                                const gfxRect& aSkipRect)
 {
   DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget();
   IntSize blurRadius = CalculateBlurRadius(aBlurStdDev);
 
   IntRect rect = RoundedToInt(ToRect(aRect));
   IntMargin extendDestBy;
   IntMargin slice;
-  RefPtr<SourceSurface> blurMask =
-    CreateBlurMask(rect.Size(), aCornerRadii, blurRadius, extendDestBy, slice,
-                   destDrawTarget);
-  if (!blurMask) {
+
+  RefPtr<SourceSurface> boxShadow = GetBlur(destDrawTarget,
+                                            rect.Size(), blurRadius,
+                                            aCornerRadii, aShadowColor,
+                                            extendDestBy, slice);
+  if (!boxShadow) {
     return;
   }
 
-  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(destDrawTarget, blurMask, aShadowColor);
-
   destDrawTarget.PushClipRect(ToRect(aDirtyRect));
 
   // Copy the right parts from boxShadow into destDrawTarget. The middle parts
   // will be stretched, border-image style.
 
   Rect srcOuter(Point(), Size(boxShadow->GetSize()));
   Rect srcInner = srcOuter;
   srcInner.Deflate(Margin(slice));