Start caching masks / stroke fills for textblobs
BUG=skia:
Review URL: https://codereview.chromium.org/1065293003
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp
index 3a1bc31..9dbe631 100644
--- a/src/gpu/GrAtlasTextContext.cpp
+++ b/src/gpu/GrAtlasTextContext.cpp
@@ -60,10 +60,10 @@
};
// 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::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 @@
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 @@
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 @@
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 @@
}
} 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 @@
cacheBlob->fX = x;
cacheBlob->fY = y;
cacheBlob->fColor = skPaint.getColor();
- cacheBlob->fStyle = skPaint.getStyle();
// Regenerate textblob
SkPaint runPaint = skPaint;
@@ -302,7 +325,6 @@
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 @@
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;
diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h
index 7859c04..c999e1f 100644
--- a/src/gpu/GrAtlasTextContext.h
+++ b/src/gpu/GrAtlasTextContext.h
@@ -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 @@
* 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 @@
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 @@
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 @@
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,
diff --git a/src/gpu/GrTextBlobCache.cpp b/src/gpu/GrTextBlobCache.cpp
index a5ef07c..fe0ccf6 100644
--- a/src/gpu/GrTextBlobCache.cpp
+++ b/src/gpu/GrTextBlobCache.cpp
@@ -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;
diff --git a/src/gpu/GrTextBlobCache.h b/src/gpu/GrTextBlobCache.h
index 0d0320e..a110a03 100644
--- a/src/gpu/GrTextBlobCache.h
+++ b/src/gpu/GrTextBlobCache.h
@@ -40,22 +40,34 @@
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 @@
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 @@
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;