Add sanity check to GrAtlasTextBlob
BUG=skia:
Review URL: https://codereview.chromium.org/1250693002
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 95a9f4d..598064a 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -72,6 +72,7 @@
'<(skia_src_path)/gpu/GrAllocator.h',
'<(skia_src_path)/gpu/GrAtlas.cpp',
'<(skia_src_path)/gpu/GrAtlas.h',
+ '<(skia_src_path)/gpu/GrAtlasTextBlob.cpp',
'<(skia_src_path)/gpu/GrAtlasTextBlob.h',
'<(skia_src_path)/gpu/GrAtlasTextContext.cpp',
'<(skia_src_path)/gpu/GrAtlasTextContext.h',
diff --git a/src/gpu/GrAtlasTextBlob.cpp b/src/gpu/GrAtlasTextBlob.cpp
new file mode 100644
index 0000000..d357145
--- /dev/null
+++ b/src/gpu/GrAtlasTextBlob.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAtlasTextBlob.h"
+
+#ifdef CACHE_SANITY_CHECK
+void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {
+ SkASSERT(l.fSize == r.fSize);
+ SkASSERT(l.fPool == r.fPool);
+
+ SkASSERT(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
+ SkASSERT(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
+ SkASSERT(l.fBlurRec.fQuality == r.fBlurRec.fQuality);
+
+ SkASSERT(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
+ SkASSERT(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
+ SkASSERT(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
+
+ SkASSERT(l.fBigGlyphs.count() == r.fBigGlyphs.count());
+ for (int i = 0; i < l.fBigGlyphs.count(); i++) {
+ const BigGlyph& lBigGlyph = l.fBigGlyphs[i];
+ const BigGlyph& rBigGlyph = r.fBigGlyphs[i];
+
+ SkASSERT(lBigGlyph.fPath == rBigGlyph.fPath);
+ // We can't assert that these have the same translations
+ }
+
+ SkASSERT(l.fKey == r.fKey);
+ SkASSERT(l.fViewMatrix.cheapEqualTo(r.fViewMatrix));
+ SkASSERT(l.fPaintColor == r.fPaintColor);
+ SkASSERT(l.fMaxMinScale == r.fMaxMinScale);
+ SkASSERT(l.fMinMaxScale == r.fMinMaxScale);
+ SkASSERT(l.fTextType == r.fTextType);
+
+ SkASSERT(l.fRunCount == r.fRunCount);
+ for (int i = 0; i < l.fRunCount; i++) {
+ const Run& lRun = l.fRuns[i];
+ const Run& rRun = r.fRuns[i];
+
+ if (lRun.fStrike.get()) {
+ SkASSERT(rRun.fStrike.get());
+ SkASSERT(GrBatchTextStrike::GetKey(*lRun.fStrike) ==
+ GrBatchTextStrike::GetKey(*rRun.fStrike));
+
+ } else {
+ SkASSERT(!rRun.fStrike.get());
+ }
+
+ if (lRun.fTypeface.get()) {
+ SkASSERT(rRun.fTypeface.get());
+ SkASSERT(SkTypeface::Equal(lRun.fTypeface, rRun.fTypeface));
+ } else {
+ SkASSERT(!rRun.fTypeface.get());
+ }
+
+ // TODO BOUNDS ARE ALL MESSED UP
+ //SkASSERT(lRun.fVertexBounds == rRun.fVertexBounds);
+
+ SkASSERT(lRun.fDescriptor.getDesc());
+ SkASSERT(rRun.fDescriptor.getDesc());
+ SkASSERT(lRun.fDescriptor.getDesc()->equals(*rRun.fDescriptor.getDesc()));
+
+ if (lRun.fOverrideDescriptor.get()) {
+ SkASSERT(lRun.fOverrideDescriptor->getDesc());
+ SkASSERT(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());;
+ SkASSERT(lRun.fOverrideDescriptor->getDesc()->equals(
+ *rRun.fOverrideDescriptor->getDesc()));
+ } else {
+ SkASSERT(!rRun.fOverrideDescriptor.get());
+ }
+
+ // color can be changed
+ //SkASSERT(lRun.fColor == rRun.fColor);
+ SkASSERT(lRun.fInitialized == rRun.fInitialized);
+ SkASSERT(lRun.fDrawAsPaths == rRun.fDrawAsPaths);
+
+ SkASSERT(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
+ for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
+ const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j];
+ const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j];
+
+ SkASSERT(lSubRun.fVertexStartIndex == rSubRun.fVertexStartIndex);
+ SkASSERT(lSubRun.fVertexEndIndex == rSubRun.fVertexEndIndex);
+ SkASSERT(lSubRun.fGlyphStartIndex == rSubRun.fGlyphStartIndex);
+ SkASSERT(lSubRun.fGlyphEndIndex == rSubRun.fGlyphEndIndex);
+ SkASSERT(lSubRun.fTextRatio == rSubRun.fTextRatio);
+ SkASSERT(lSubRun.fMaskFormat == rSubRun.fMaskFormat);
+ SkASSERT(lSubRun.fDrawAsDistanceFields == rSubRun.fDrawAsDistanceFields);
+ SkASSERT(lSubRun.fUseLCDText == rSubRun.fUseLCDText);
+
+ //We can't compare the bulk use tokens with this method
+ /*
+ SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate.count() ==
+ rSubRun.fBulkUseToken.fPlotsToUpdate.count());
+ SkASSERT(lSubRun.fBulkUseToken.fPlotAlreadyUpdated ==
+ rSubRun.fBulkUseToken.fPlotAlreadyUpdated);
+ for (int k = 0; k < lSubRun.fBulkUseToken.fPlotsToUpdate.count(); k++) {
+ SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate[k] ==
+ rSubRun.fBulkUseToken.fPlotsToUpdate[k]);
+ }*/
+ }
+ }
+}
+
+#endif
diff --git a/src/gpu/GrAtlasTextBlob.h b/src/gpu/GrAtlasTextBlob.h
index a69e430..1f37c67 100644
--- a/src/gpu/GrAtlasTextBlob.h
+++ b/src/gpu/GrAtlasTextBlob.h
@@ -10,11 +10,17 @@
#include "GrBatchAtlas.h"
#include "GrBatchFontCache.h"
+#include "GrColor.h"
#include "SkDescriptor.h"
#include "SkMaskFilter.h"
#include "GrMemoryPool.h"
+#include "SkSurfaceProps.h"
#include "SkTInternalLList.h"
+// With this flag enabled, the GrAtlasTextContext will, as a sanity check, regenerate every blob
+// that comes in to verify the integrity of its cache
+//#define CACHE_SANITY_CHECK // VERY SLOW
+
/*
* A GrAtlasTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
* on the GPU. These are initially created with valid positions and colors, but invalid
@@ -26,6 +32,8 @@
* the GrAtlas will not evict anything the Blob needs.
*
* Note: This struct should really be named GrCachedAtasTextBlob, but that is too verbose.
+ *
+ * *WARNING* If you add new fields to this struct, then you may need to to update AssertEqual
*/
struct GrAtlasTextBlob : public SkRefCnt {
SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrAtlasTextBlob);
@@ -211,6 +219,11 @@
bool hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
+
+#ifdef CACHE_SANITY_CHECK
+ static void AssertEqual(const GrAtlasTextBlob&, const GrAtlasTextBlob&);
+ size_t fSize;
+#endif
};
#endif
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp
index 8009e92..0e901ae 100644
--- a/src/gpu/GrAtlasTextContext.cpp
+++ b/src/gpu/GrAtlasTextContext.cpp
@@ -410,10 +410,25 @@
} else {
// If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
// offsets
+ // TODO bounds are wrong
cacheBlob->fViewMatrix = viewMatrix;
cacheBlob->fX = x;
cacheBlob->fY = y;
fCache->makeMRU(cacheBlob);
+#ifdef CACHE_SANITY_CHECK
+ {
+ int glyphCount = 0;
+ int runCount = 0;
+ GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob);
+ SkAutoTUnref<GrAtlasTextBlob> sanityBlob(fCache->createBlob(glyphCount, runCount,
+ kGrayTextVASize));
+ GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skPaint);
+ this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor(), viewMatrix,
+ blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
+ GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
+ }
+
+#endif
}
} else {
if (canCache) {
@@ -426,7 +441,6 @@
blob, x, y, drawFilter, clipRect, rt, clip, grPaint);
}
- cacheBlob->fPaintColor = skPaint.getColor();
this->flush(blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
clip, viewMatrix, clipBounds, x, y, transX, transY);
}
@@ -477,6 +491,7 @@
SkDrawFilter* drawFilter, const SkIRect& clipRect,
GrRenderTarget* rt, const GrClip& clip,
const GrPaint& paint) {
+ cacheBlob->fPaintColor = skPaint.getColor();
cacheBlob->fViewMatrix = viewMatrix;
cacheBlob->fX = x;
cacheBlob->fY = y;
diff --git a/src/gpu/GrTextBlobCache.cpp b/src/gpu/GrTextBlobCache.cpp
index b1a13a8..dd7ddc1 100644
--- a/src/gpu/GrTextBlobCache.cpp
+++ b/src/gpu/GrTextBlobCache.cpp
@@ -13,8 +13,7 @@
this->freeAll();
}
-GrAtlasTextBlob* GrTextBlobCache::createBlob(int glyphCount, int runCount,
- size_t maxVASize) {
+GrAtlasTextBlob* GrTextBlobCache::createBlob(int glyphCount, int runCount, size_t maxVASize) {
// We allocate size for the GrAtlasTextBlob itself, plus size for the vertices array,
// and size for the glyphIds array.
size_t verticesCount = glyphCount * kVerticesPerGlyph * maxVASize;
@@ -23,7 +22,15 @@
glyphCount * sizeof(GrGlyph**) +
sizeof(GrAtlasTextBlob::Run) * runCount;
- GrAtlasTextBlob* cacheBlob = SkNEW_PLACEMENT(fPool.allocate(size), GrAtlasTextBlob);
+ void* allocation = fPool.allocate(size);
+#ifdef CACHE_SANITY_CHECK
+ sk_bzero(allocation, size);
+#endif
+
+ GrAtlasTextBlob* cacheBlob = SkNEW_PLACEMENT(allocation, GrAtlasTextBlob);
+#ifdef CACHE_SANITY_CHECK
+ cacheBlob->fSize = size;
+#endif
// setup offsets for vertices / glyphs
cacheBlob->fVertices = sizeof(GrAtlasTextBlob) + reinterpret_cast<unsigned char*>(cacheBlob);
diff --git a/src/gpu/GrTextBlobCache.h b/src/gpu/GrTextBlobCache.h
index ca12411..4c9cb14 100644
--- a/src/gpu/GrTextBlobCache.h
+++ b/src/gpu/GrTextBlobCache.h
@@ -38,15 +38,10 @@
return cacheBlob;
}
- GrAtlasTextBlob* createCachedBlob(const SkTextBlob* blob,
- const GrAtlasTextBlob::Key& key,
- const SkMaskFilter::BlurRec& blurRec,
- const SkPaint& paint,
- size_t maxVAStride) {
- int glyphCount = 0;
- int runCount = 0;
- BlobGlyphCount(&glyphCount, &runCount, blob);
- GrAtlasTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
+ static void SetupCacheBlobKey(GrAtlasTextBlob* cacheBlob,
+ const GrAtlasTextBlob::Key& key,
+ const SkMaskFilter::BlurRec& blurRec,
+ const SkPaint& paint) {
cacheBlob->fKey = key;
if (key.fHasBlur) {
cacheBlob->fBlurRec = blurRec;
@@ -56,6 +51,18 @@
cacheBlob->fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
cacheBlob->fStrokeInfo.fJoin = paint.getStrokeJoin();
}
+ }
+
+ GrAtlasTextBlob* createCachedBlob(const SkTextBlob* blob,
+ const GrAtlasTextBlob::Key& key,
+ const SkMaskFilter::BlurRec& blurRec,
+ const SkPaint& paint,
+ size_t maxVAStride) {
+ int glyphCount = 0;
+ int runCount = 0;
+ BlobGlyphCount(&glyphCount, &runCount, blob);
+ GrAtlasTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
+ SetupCacheBlobKey(cacheBlob, key, blurRec, paint);
this->add(cacheBlob);
return cacheBlob;
}
@@ -115,15 +122,15 @@
void freeAll();
-private:
// TODO move to SkTextBlob
- void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
+ static void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
SkTextBlob::RunIterator itCounter(blob);
for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
*glyphCount += itCounter.glyphCount();
}
}
+private:
typedef SkTInternalLList<GrAtlasTextBlob> BitmapBlobList;
// Budget was chosen to be ~4 megabytes. The min alloc and pre alloc sizes in the pool are