Bug 930451 - Part 2: Use the new Rendering gradient cache for CSS border gradients as well. r=roc, a=
authorAndreas Gal <gal@uci.edu>
Fri, 08 Nov 2013 17:50:39 +1300
changeset 166467 a3bb77bde73f69a15e2c22904fd263ee158b4bab
parent 166466 019969972ed0d4f34a2ab5973b0fd2ebcb15ca11
child 166468 e94179cf1740a6cc7a23ee033fdf8eb7c51554f9
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs930451
milestone27.0a2
Bug 930451 - Part 2: Use the new Rendering gradient cache for CSS border gradients as well. r=roc, a=
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRenderingBorders.cpp
layout/base/nsCSSRenderingBorders.h
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -322,25 +322,23 @@ static nscolor MakeBevelColor(mozilla::c
 
 static InlineBackgroundData* gInlineBGData = nullptr;
 
 // Initialize any static variables used by nsCSSRendering.
 void nsCSSRendering::Init()
 {
   NS_ASSERTION(!gInlineBGData, "Init called twice");
   gInlineBGData = new InlineBackgroundData();
-  nsCSSBorderRenderer::Init();
 }
 
 // Clean up any global variables used by nsCSSRendering.
 void nsCSSRendering::Shutdown()
 {
   delete gInlineBGData;
   gInlineBGData = nullptr;
-  nsCSSBorderRenderer::Shutdown();
 }
 
 /**
  * Make a bevel color
  */
 static nscolor
 MakeBevelColor(mozilla::css::Side whichSide, uint8_t style,
                nscolor aBackgroundColor, nscolor aBorderColor)
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -6,163 +6,26 @@
 
 #include "nsStyleConsts.h"
 #include "nsCSSColorUtils.h"
 #include "GeckoProfiler.h"
 #include "nsExpirationTracker.h"
 #include "RoundedRect.h"
 #include "nsClassHashtable.h"
 #include "nsStyleStruct.h"
-
 #include "gfxContext.h"
-
 #include "nsCSSRenderingBorders.h"
-
 #include "mozilla/gfx/2D.h"
 #include "gfx2DGlue.h"
+#include "gfxGradientCache.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
-struct BorderGradientCacheKey : public PLDHashEntryHdr {
-  typedef const BorderGradientCacheKey& KeyType;
-  typedef const BorderGradientCacheKey* KeyTypePointer;
-
-  enum { ALLOW_MEMMOVE = true };
-
-  const uint32_t mColor1;
-  const uint32_t mColor2;
-  const BackendType mBackendType;
-
-  BorderGradientCacheKey(const Color& aColor1, const Color& aColor2,
-                         BackendType aBackendType)
-    : mColor1(aColor1.ToABGR()), mColor2(aColor2.ToABGR())
-    , mBackendType(aBackendType)
-  { }
-
-  BorderGradientCacheKey(const BorderGradientCacheKey* aOther)
-    : mColor1(aOther->mColor1), mColor2(aOther->mColor2)
-    , mBackendType(aOther->mBackendType)
-  { }
-
-  static PLDHashNumber
-  HashKey(const KeyTypePointer aKey)
-  {
-    PLDHashNumber hash = 0;
-    hash = AddToHash(hash, aKey->mColor1);
-    hash = AddToHash(hash, aKey->mColor2);
-    hash = AddToHash(hash, aKey->mBackendType);
-    return hash;
-  }
-
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    return (aKey->mColor1 == mColor1) &&
-           (aKey->mColor2 == mColor2) &&
-           (aKey->mBackendType == mBackendType);
-  }
-  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 BorderGradientCacheData {
-  BorderGradientCacheData(GradientStops* aStops, const BorderGradientCacheKey& aKey)
-    : mStops(aStops), mKey(aKey)
-  {}
-
-  BorderGradientCacheData(const BorderGradientCacheData& aOther)
-    : mStops(aOther.mStops),
-      mKey(aOther.mKey)
-  { }
-
-  nsExpirationState *GetExpirationState() {
-    return &mExpirationState;
-  }
-
-  nsExpirationState mExpirationState;
-  RefPtr<GradientStops> mStops;
-  BorderGradientCacheKey mKey;
-};
-
-/**
- * This class implements a cache with no maximum size, that retains the
- * gradient stops used to draw border corners.
- *
- * The key is formed by the two gradient stops, they're always both located
- * at an offset of 0.5. So they can generously be reused. The key also includes
- * the backend type a certain gradient was created for.
- *
- * An entry stays in the cache as long as it is used often.
- *
- * This code was pretty bluntly stolen and modified from nsCSSRendering.
- */
-class BorderGradientCache MOZ_FINAL : public nsExpirationTracker<BorderGradientCacheData,4>
-{
-  public:
-    BorderGradientCache()
-      : nsExpirationTracker<BorderGradientCacheData, 4>(GENERATION_MS)
-    {
-      mTimerPeriod = GENERATION_MS;
-    }
-
-    virtual void NotifyExpired(BorderGradientCacheData* aObject)
-    {
-      // This will free the gfxPattern.
-      RemoveObject(aObject);
-      mHashEntries.Remove(aObject->mKey);
-    }
-
-    BorderGradientCacheData* Lookup(const Color& aColor1, const Color& aColor2,
-                                    BackendType aBackendType)
-    {
-      BorderGradientCacheData* gradient =
-        mHashEntries.Get(BorderGradientCacheKey(aColor1, aColor2, aBackendType));
-
-      if (gradient) {
-        MarkUsed(gradient);
-      }
-
-      return gradient;
-    }
-
-    // Returns true if we successfully register the gradient in the cache, false
-    // otherwise.
-    bool RegisterEntry(BorderGradientCacheData* aValue)
-    {
-      nsresult rv = AddObject(aValue);
-      if (NS_FAILED(rv)) {
-        // We are OOM, and we cannot track this object. We don't want stall
-        // entries in the hash table (since the expiration tracker is responsible
-        // for removing the cache entries), so we avoid putting that entry in the
-        // table, which is a good things considering we are short on memory
-        // anyway, we probably don't want to retain things.
-        return false;
-      }
-      mHashEntries.Put(aValue->mKey, aValue);
-      return true;
-    }
-
-  protected:
-    uint32_t mTimerPeriod;
-    static const uint32_t GENERATION_MS = 4000;
-    /**
-     * FIXME use nsTHashtable to avoid duplicating the BorderGradientCacheKey.
-     * This is analogous to the issue for the generic gradient cache:
-     * https://bugzilla.mozilla.org/show_bug.cgi?id=785794
-     */
-    nsClassHashtable<BorderGradientCacheKey, BorderGradientCacheData> mHashEntries;
-};
-
 /**
  * nsCSSRendering::PaintBorder
  * nsCSSRendering::PaintOutline
  *   -> DrawBorders
  *
  * DrawBorders
  *   -> Ability to use specialized approach?
  *      |- Draw using specialized function
@@ -244,18 +107,16 @@ typedef enum {
   // adjacent corners.
   CORNER_SOLID,
 
   // Paint the corner as a dot, the size of the bigger of the adjacent
   // sides.
   CORNER_DOT
 } CornerStyle;
 
-static BorderGradientCache* gBorderGradientCache = nullptr;
-
 nsCSSBorderRenderer::nsCSSBorderRenderer(int32_t aAppUnitsPerPixel,
                                          gfxContext* aDestContext,
                                          gfxRect& aOuterRect,
                                          const uint8_t* aBorderStyles,
                                          const gfxFloat* aBorderWidths,
                                          gfxCornerSizes& aBorderRadii,
                                          const nscolor* aBorderColors,
                                          nsBorderColors* const* aCompositeColors,
@@ -286,28 +147,16 @@ nsCSSBorderRenderer::nsCSSBorderRenderer
 
   ComputeBorderCornerDimensions(mOuterRect, mInnerRect, mBorderRadii, &mBorderCornerDimensions);
 
   mOneUnitBorder = CheckFourFloatsEqual(mBorderWidths, 1.0);
   mNoBorderRadius = AllCornersZeroSize(mBorderRadii);
   mAvoidStroke = false;
 }
 
-void
-nsCSSBorderRenderer::Init()
-{
-  gBorderGradientCache = new BorderGradientCache();
-}
-
-void
-nsCSSBorderRenderer::Shutdown()
-{
-  delete gBorderGradientCache;
-}
-
 /* static */ void
 nsCSSBorderRenderer::ComputeInnerRadii(const gfxCornerSizes& aRadii,
                                        const gfxFloat *aBorderSizes,
                                        gfxCornerSizes *aInnerRadiiRet)
 {
   gfxCornerSizes& iRadii = *aInnerRadiiRet;
 
   iRadii[C_TL].width = std::max(0.0, aRadii[C_TL].width - aBorderSizes[NS_SIDE_LEFT]);
@@ -1277,54 +1126,39 @@ nsCSSBorderRenderer::CreateCornerGradien
     mBorderWidths[cornerWidth[aCorner]]  * gradientCoeff[aCorner].b;
 
   aPoint1 = Point(pat1.x, pat1.y);
   aPoint2 = Point(pat2.x, pat2.y);
 
   Color firstColor = ToColor(aFirstColor);
   Color secondColor = ToColor(aSecondColor);
 
-  BorderGradientCacheData *data =
-    gBorderGradientCache->Lookup(firstColor, secondColor, aDT->GetType());
-
-  if (!data) {
+  nsTArray<gfx::GradientStop> rawStops(2);
+  rawStops.SetLength(2);
+  // This is only guaranteed to give correct (and in some cases more correct)
+  // rendering with the Direct2D Azure and Quartz Cairo backends. For other
+  // cairo backends it could create un-antialiased border corner transitions
+  // since that at least used to be pixman's behaviour for hard stops.
+  rawStops[0].color = firstColor;
+  rawStops[0].offset = 0.5;
+  rawStops[1].color = secondColor;
+  rawStops[1].offset = 0.5;
+  RefPtr<GradientStops> gs =
+    gfxGradientCache::GetGradientStops(aDT, rawStops, EXTEND_CLAMP);
+  if (!gs) {
     // Having two corners, both with reversed color stops is pretty common
     // for certain border types. Let's optimize it!
-    data = gBorderGradientCache->Lookup(secondColor, firstColor, aDT->GetType());
-
-    if (data) {
-      Point tmp = aPoint1;
-      aPoint1 = aPoint2;
-      aPoint2 = tmp;
-    }
+    rawStops[0].color = secondColor;
+    rawStops[1].color = firstColor;
+    Point tmp = aPoint1;
+    aPoint1 = aPoint2;
+    aPoint2 = tmp;
+    gs = gfxGradientCache::GetOrCreateGradientStops(aDT, rawStops, EXTEND_CLAMP);
   }
-
-  RefPtr<GradientStops> stops;
-  if (data) {
-    stops = data->mStops;
-  } else {
-    GradientStop rawStops[2];
-    // This is only guaranteed to give correct (and in some cases more correct)
-    // rendering with the Direct2D Azure and Quartz Cairo backends. For other
-    // cairo backends it could create un-antialiased border corner transitions
-    // since that at least used to be pixman's behaviour for hard stops.
-    rawStops[0].color = firstColor;
-    rawStops[0].offset = 0.5;
-    rawStops[1].color = secondColor;
-    rawStops[1].offset = 0.5;
-    stops = aDT->CreateGradientStops(rawStops, 2);
-
-    data = new BorderGradientCacheData(stops, BorderGradientCacheKey(firstColor, secondColor, aDT->GetType()));
-
-    if (!gBorderGradientCache->RegisterEntry(data)) {
-      delete data;
-    }
-  }
-
-  return stops;
+  return gs;
 }
 
 typedef struct { gfxFloat a, b; } twoFloats;
 
 void
 nsCSSBorderRenderer::DrawSingleWidthSolidBorder()
 {
   // Easy enough to deal with.
--- a/layout/base/nsCSSRenderingBorders.h
+++ b/layout/base/nsCSSRenderingBorders.h
@@ -77,19 +77,16 @@ struct nsCSSBorderRenderer {
                       const uint8_t* aBorderStyles,
                       const gfxFloat* aBorderWidths,
                       gfxCornerSizes& aBorderRadii,
                       const nscolor* aBorderColors,
                       nsBorderColors* const* aCompositeColors,
                       int aSkipSides,
                       nscolor aBackgroundColor);
 
-  static void Init();
-  static void Shutdown();
-
   gfxCornerSizes mBorderCornerDimensions;
 
   // destination context
   gfxContext* mContext;
 
   // the rectangle of the outside and the inside of the border
   gfxRect mOuterRect;
   gfxRect mInnerRect;