Start caching masks / stroke fills for textblobs
authorjoshualitt <joshualitt@chromium.org>
Mon, 13 Apr 2015 13:33:59 +0000 (06:33 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 13 Apr 2015 13:33:59 +0000 (06:33 -0700)
BUG=skia:

Review URL: https://codereview.chromium.org/1065293003

src/gpu/GrAtlasTextContext.cpp
src/gpu/GrAtlasTextContext.h
src/gpu/GrTextBlobCache.cpp
src/gpu/GrTextBlobCache.h

index 3a1bc31..9dbe631 100644 (file)
@@ -60,10 +60,10 @@ static size_t get_vertex_stride(GrMaskFormat maskFormat) {
 };
 
 // TODO
-// More tests
-// move to SkCache
-// handle textblobs where the whole run is larger than the cache size
-// TODO implement micro speedy hash map for fast refing of glyphs
+// Gamma slotting to preserve color
+// Better reuse on regeneration
+// Telemetry tests
+// possibly consider having a regeneration ratio on the textblob itself for animated textblobs
 
 GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
                                        SkGpuDevice* gpuDevice,
@@ -93,6 +93,7 @@ bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
 
 bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
                                             const BitmapTextBlob& blob, const SkPaint& paint,
+                                            const SkMaskFilter::BlurRec& blurRec,
                                             const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
     // Color can affect the mask
     // TODO we can adjust the color within specific gamma slots
@@ -115,6 +116,22 @@ bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTr
         return true;
     }
 
+    // We only cache one masked version
+    if (blob.fKey.fHasBlur &&
+        (blob.fBlurRec.fSigma != blurRec.fSigma ||
+         blob.fBlurRec.fStyle != blurRec.fStyle ||
+         blob.fBlurRec.fQuality != blurRec.fQuality)) {
+        return true;
+    }
+
+    // Similarly, we only cache one version for each style
+    if (blob.fKey.fStyle != SkPaint::kFill_Style &&
+        (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
+         blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
+         blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
+        return true;
+    }
+
     // We can update the positions in the cachedtextblobs without regenerating the whole blob, but
     // only for integer translations.
     // This cool bit of math will determine the necessary translation to apply to the already
@@ -162,17 +179,22 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
                                       const SkPaint& skPaint, const SkMatrix& viewMatrix,
                                       const SkTextBlob* blob, SkScalar x, SkScalar y,
                                       SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
-    uint32_t uniqueID = blob->uniqueID();
     SkAutoTUnref<BitmapTextBlob> cacheBlob;
-    // TODO start caching these, mix bits into the key
+
+    SkMaskFilter::BlurRec blurRec;
+    BitmapTextBlob::Key key;
+    // It might be worth caching these things, but its not clear at this time
+    // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
+    const SkMaskFilter* mf = skPaint.getMaskFilter();
     bool canCache = !(skPaint.getPathEffect() ||
-                      skPaint.getMaskFilter() ||
-                      skPaint.getColorFilter() ||
-                      skPaint.getStyle() != SkPaint::kFill_Style ||
+                      (mf && !mf->asABlur(&blurRec)) ||
                       drawFilter);
 
     if (canCache) {
-        cacheBlob.reset(SkSafeRef(fCache->find(uniqueID)));
+        key.fUniqueID = blob->uniqueID();
+        key.fStyle = skPaint.getStyle();
+        key.fHasBlur = SkToBool(mf);
+        cacheBlob.reset(SkSafeRef(fCache->find(key)));
     }
 
     SkIRect clipRect;
@@ -182,12 +204,13 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
     SkScalar transY = 0.f;
 
     if (cacheBlob) {
-        if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, viewMatrix, x, y)) {
+        if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
             // We have to remake the blob because changes may invalidate our masks.
             // TODO we could probably get away reuse most of the time if the pointer is unique,
             // but we'd have to clear the subrun information
             fCache->remove(cacheBlob);
-            cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, kGrayTextVASize)));
+            cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
+                                                           kGrayTextVASize)));
             this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter,
                                      clipRect);
         } else {
@@ -200,7 +223,8 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
         }
     } else {
         if (canCache) {
-            cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, kGrayTextVASize)));
+            cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
+                                                           kGrayTextVASize)));
         } else {
             cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
         }
@@ -224,7 +248,6 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
     cacheBlob->fX = x;
     cacheBlob->fY = y;
     cacheBlob->fColor = skPaint.getColor();
-    cacheBlob->fStyle = skPaint.getStyle();
 
     // Regenerate textblob
     SkPaint runPaint = skPaint;
@@ -302,7 +325,6 @@ void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
     blob->fViewMatrix = viewMatrix;
     blob->fX = x;
     blob->fY = y;
-    blob->fStyle = skPaint.getStyle();
 
     SkIRect clipRect;
     clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
@@ -413,7 +435,6 @@ void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
                                        const SkPoint& offset, const SkIRect& regionClipBounds) {
     int glyphCount = skPaint.countText(text, byteLength);
     SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
-    blob->fStyle = skPaint.getStyle();
     blob->fViewMatrix = viewMatrix;
 
     SkIRect clipRect;
index 7859c04..c999e1f 100644 (file)
@@ -14,6 +14,7 @@
 #include "GrGeometryProcessor.h"
 #include "SkDescriptor.h"
 #include "GrMemoryPool.h"
+#include "SkMaskFilter.h"
 #include "SkTextBlob.h"
 #include "SkTInternalLList.h"
 
@@ -84,7 +85,6 @@ private:
          * glyph cache to get the path at flush time, or hold onto the path in the cache, which
          * would greatly increase the memory of these cached items.
          */
-
         struct Run {
             Run() : fColor(GrColor_ILLEGAL), fInitialized(false), fDrawAsPaths(false) {
                 fVertexBounds.setLargestInverted();
@@ -132,9 +132,14 @@ private:
         SkMatrix fViewMatrix;
         SkScalar fX;
         SkScalar fY;
-        SkPaint::Style fStyle;
         int fRunCount;
-        uint32_t fUniqueID;
+        SkMaskFilter::BlurRec fBlurRec;
+        struct StrokeInfo {
+            SkScalar fFrameWidth;
+            SkScalar fMiterLimit;
+            SkPaint::Join fJoin;
+        };
+        StrokeInfo fStrokeInfo;
         GrMemoryPool* fPool;
 
         // all glyph / vertex offsets are into these pools.
@@ -142,12 +147,26 @@ private:
         GrGlyph::PackedID* fGlyphIDs;
         Run* fRuns;
 
-        static const uint32_t& GetKey(const BitmapTextBlob& blob) {
-            return blob.fUniqueID;
+        struct Key {
+            Key() {
+                memset(this, 0, sizeof(Key));
+            }
+            uint32_t fUniqueID;
+            SkPaint::Style fStyle;
+            bool fHasBlur;
+
+            bool operator==(const Key& other) const {
+                return 0 == memcmp(this, &other, sizeof(Key));
+            }
+        };
+        Key fKey;
+
+        static const Key& GetKey(const BitmapTextBlob& blob) {
+            return blob.fKey;
         }
 
-        static uint32_t Hash(const uint32_t& key) {
-            return SkChecksum::Mix(key);
+        static uint32_t Hash(const Key& key) {
+            return SkChecksum::Murmur3(&key, sizeof(Key));
         }
 
         void operator delete(void* p) {
@@ -200,6 +219,7 @@ private:
     inline SkGlyphCache* setupCache(Run*, const SkPaint&, const SkMatrix& viewMatrix);
     static inline bool MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
                                           const BitmapTextBlob&, const SkPaint&,
+                                          const SkMaskFilter::BlurRec&,
                                           const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
     void regenerateTextBlob(BitmapTextBlob* bmp, const SkPaint& skPaint, const SkMatrix& viewMatrix,
                             const SkTextBlob* blob, SkScalar x, SkScalar y,
index a5ef07c..fe0ccf6 100644 (file)
@@ -10,7 +10,7 @@
 static const int kVerticesPerGlyph = 4;
 
 GrTextBlobCache::~GrTextBlobCache() {
-    SkTDynamicHash<BitmapTextBlob, uint32_t>::Iter iter(&fCache);
+    SkTDynamicHash<BitmapTextBlob, BitmapTextBlob::Key>::Iter iter(&fCache);
     while (!iter.done()) {
         (&(*iter))->unref();
         ++iter;
index 0d0320e..a110a03 100644 (file)
@@ -40,22 +40,34 @@ public:
         return cacheBlob;
     }
 
-    BitmapTextBlob* createCachedBlob(const SkTextBlob* blob, size_t maxVAStride) {
+    BitmapTextBlob* createCachedBlob(const SkTextBlob* blob,
+                                     const BitmapTextBlob::Key& key,
+                                     const SkMaskFilter::BlurRec& blurRec,
+                                     const SkPaint& paint,
+                                     size_t maxVAStride) {
         int glyphCount = 0;
         int runCount = 0;
         BlobGlyphCount(&glyphCount, &runCount, blob);
         BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
-        cacheBlob->fUniqueID = blob->uniqueID();
+        cacheBlob->fKey = key;
+        if (key.fHasBlur) {
+            cacheBlob->fBlurRec = blurRec;
+        }
+        if (key.fStyle != SkPaint::kFill_Style) {
+            cacheBlob->fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
+            cacheBlob->fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
+            cacheBlob->fStrokeInfo.fJoin = paint.getStrokeJoin();
+        }
         this->add(cacheBlob);
         return cacheBlob;
     }
 
-    BitmapTextBlob* find(uint32_t uniqueID) {
-        return fCache.find(uniqueID);
+    BitmapTextBlob* find(const BitmapTextBlob::Key& key) {
+        return fCache.find(key);
     }
 
     void remove(BitmapTextBlob* blob) {
-        fCache.remove(blob->fUniqueID);
+        fCache.remove(blob->fKey);
         fBlobList.remove(blob);
         blob->unref();
     }
@@ -71,7 +83,7 @@ public:
             BitmapTextBlob* lruBlob = iter.get();
             SkASSERT(lruBlob);
             while (fPool.size() > kBudget && (lruBlob = iter.get()) && lruBlob != blob) {
-                fCache.remove(lruBlob->fUniqueID);
+                fCache.remove(lruBlob->fKey);
 
                 // Backup the iterator before removing and unrefing the blob
                 iter.prev();
@@ -120,7 +132,7 @@ private:
     static const int kMinGrowthSize = 1 << 17;
     static const int kBudget = 1 << 22;
     BitmapBlobList fBlobList;
-    SkTDynamicHash<BitmapTextBlob, uint32_t> fCache;
+    SkTDynamicHash<BitmapTextBlob, BitmapTextBlob::Key> fCache;
     GrMemoryPool fPool;
     PFOverBudgetCB fCallback;
     void* fData;