Avoid thrashing the glyph cache during the precache phase

It is unlikely, but possible, to draw so many glyphs in a frame
(especially of the glyphs are quite large) that the cache starts flushing
itself to fit the later glyphs in. This causes unnecessary thrashing, because
when we actually draw the frame, we will again need to flush to fit the
earlier glyphs in, and then flush again to fit the later ones in.

It is better to avoid thrashing the cache at the precache phase, and wait
until we actually draw the glyphs that do not fit to do any eviction of
the earlier glyphs.

This change simply notes when we are in the preaching phase, and avoids flushing
the cache when a glyph does not fit.

Issue #7081725 avoid thrashing cache during DisplayList recording

Change-Id: I230410ab5b478091b1032fa99dc1752acf868bbe
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 27e198c..6b08e7f 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -328,19 +328,19 @@
             glyph->mCacheTexture);
 }
 
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
     CachedGlyphInfo* cachedGlyph = NULL;
     ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
     if (index >= 0) {
         cachedGlyph = mCachedGlyphs.valueAt(index);
     } else {
-        cachedGlyph = cacheGlyph(paint, textUnit);
+        cachedGlyph = cacheGlyph(paint, textUnit, precaching);
     }
 
     // Is the glyph still in texture cache?
     if (!cachedGlyph->mIsValid) {
         const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
-        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
+        updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
     }
 
     return cachedGlyph;
@@ -438,7 +438,7 @@
             break;
         }
 
-        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
 
         glyphsCount++;
     }
@@ -529,7 +529,8 @@
     }
 }
 
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+        bool precaching) {
     glyph->mAdvanceX = skiaGlyph.fAdvanceX;
     glyph->mAdvanceY = skiaGlyph.fAdvanceY;
     glyph->mBitmapLeft = skiaGlyph.fLeft;
@@ -542,7 +543,7 @@
 
     // Get the bitmap for the glyph
     paint->findImage(skiaGlyph);
-    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
+    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
 
     if (!glyph->mIsValid) {
         return;
@@ -567,7 +568,7 @@
     mState->mUploadTexture = true;
 }
 
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
     CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
     mCachedGlyphs.add(glyph, newGlyph);
 
@@ -575,7 +576,7 @@
     newGlyph->mGlyphIndex = skiaGlyph.fID;
     newGlyph->mIsValid = false;
 
-    updateGlyphCache(paint, skiaGlyph, newGlyph);
+    updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
 
     return newGlyph;
 }
@@ -762,7 +763,7 @@
 }
 
 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
-        uint32_t* retOriginX, uint32_t* retOriginY) {
+        uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
     checkInit();
     cachedGlyph->mIsValid = false;
     // If the glyph is too tall, don't cache it
@@ -779,15 +780,16 @@
 
     CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
 
-    // If the new glyph didn't fit, flush the state so far and invalidate everything
     if (!cacheTexture) {
-        flushAllAndInvalidate();
+        if (!precaching) {
+            // If the new glyph didn't fit and we are not just trying to precache it,
+            // clear out the cache and try again
+            flushAllAndInvalidate();
+            cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
+        }
 
-        // Try to fit it again
-        cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
-
-        // if we still don't fit, something is wrong and we shouldn't draw
         if (!cacheTexture) {
+            // either the glyph didn't fit or we're precaching and will cache it when we draw
             return;
         }
     }
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index febae17..8d0d21d 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -243,8 +243,9 @@
 
     void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
 
-    CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
-    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
+    CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
+    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+            bool precaching);
 
     void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
             uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
@@ -258,7 +259,7 @@
     void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
             SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
 
-    CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
+    CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
 
     static glyph_t nextGlyph(const uint16_t** srcPtr) {
         const uint16_t* src = *srcPtr;
@@ -364,7 +365,7 @@
     void initTextTexture();
     CacheTexture* createCacheTexture(int width, int height, bool allocate);
     void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
-            uint32_t *retOriginX, uint32_t *retOriginY);
+            uint32_t *retOriginX, uint32_t *retOriginY, bool precaching);
     CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
 
     void flushAllAndInvalidate();