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