Optimize glyph cache texture uploads

Only upload the changed area of the glyph cache, not the entire
bitmap. Note that we can't do the full-on optimization here of copying a sub-rect
of the bitmap because of GL ES 2 limitations, but we can at least copy the
horizontal stripe containing the dirty rect, which can still be a big
savings over uploading the entire bitmap.

Issue #7158326 Bad framerates on MR1 (Mako, Manta, Prime)

Change-Id: Iab38d53202650f757ead4658cf4287bdad2b3cb9
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 86667ee..cab68f0 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -331,10 +331,14 @@
     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
         CacheTexture* cacheTexture = mCacheTextures[i];
         if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
-            uint32_t xOffset = 0;
+            // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
+            // of data. So expand the dirty rect to the encompassing horizontal stripe.
+            const Rect* dirtyRect = cacheTexture->getDirtyRect();
+            uint32_t x = 0;
+            uint32_t y = dirtyRect->top;
             uint32_t width = cacheTexture->getWidth();
-            uint32_t height = cacheTexture->getHeight();
-            void* textureData = cacheTexture->getTexture();
+            uint32_t height = dirtyRect->getHeight();
+            void* textureData = cacheTexture->getTexture() + y * width;
 
             if (cacheTexture->getTextureId() != lastTextureId) {
                 lastTextureId = cacheTexture->getTextureId();
@@ -342,12 +346,11 @@
                 glBindTexture(GL_TEXTURE_2D, lastTextureId);
             }
 #if DEBUG_FONT_RENDERER
-            ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
-                    i, xOffset, width, height);
+            ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
+                    i, x, y, width, height);
 #endif
-            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
+            glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
                     GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
-
             cacheTexture->setDirty(false);
         }
     }
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 4a3af12..f653592 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -171,6 +171,9 @@
             }
 
             mDirty = true;
+            const Rect r(*retOriginX - TEXTURE_BORDER_SIZE, *retOriginY - TEXTURE_BORDER_SIZE,
+                    *retOriginX + glyphW, *retOriginY + glyphH);
+            mDirtyRect.unionWith(r);
             mNumGlyphs++;
 
 #if DEBUG_FONT_RENDERER
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index bf1f4a9..800bfc4 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -24,6 +24,7 @@
 #include <utils/Log.h>
 
 #include "FontUtil.h"
+#include "Rect.h"
 
 namespace android {
 namespace uirenderer {
@@ -149,6 +150,10 @@
         return mHeight;
     }
 
+    inline const Rect* getDirtyRect() const {
+        return &mDirtyRect;
+    }
+
     inline uint8_t* getTexture() const {
         return mTexture;
     }
@@ -163,6 +168,9 @@
 
     inline void setDirty(bool dirty) {
         mDirty = dirty;
+        if (!dirty) {
+            mDirtyRect.setEmpty();
+        }
     }
 
     inline bool getLinearFiltering() const {
@@ -196,6 +204,7 @@
     bool mDirty;
     uint16_t mNumGlyphs;
     CacheBlock* mCacheBlocks;
+    Rect mDirtyRect;
 };
 
 }; // namespace uirenderer