Avoid 9patch cache lookups when possible

This optimization saves up to 0.3ms per frame on the Play Store's
front page, on a Nexus 4 device.

Change-Id: Iaa4ef33c6e3b37e175efd5b9eea9ef59b43f14f3
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 6839b18..028decd 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -932,13 +932,22 @@
     DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch,
             float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode)
             : DrawBoundedOp(left, top, right, bottom, 0),
-            mBitmap(bitmap), mPatch(patch), mAlpha(alpha), mMode(mode) {
+            mBitmap(bitmap), mPatch(patch), mAlpha(alpha), mMode(mode),
+            mGenerationId(0), mMesh(NULL) {
         mEntry = Caches::getInstance().assetAtlas.getEntry(bitmap);
     };
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
-        // NOTE: not calling the virtual method, which takes a paint
-        return renderer.drawPatch(mBitmap, mPatch, mEntry, mLocalBounds.left, mLocalBounds.top,
+        if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) {
+            PatchCache& cache = renderer.getCaches().patchCache;
+            mMesh = cache.get(mEntry, mBitmap->width(), mBitmap->height(),
+                    mLocalBounds.right - mLocalBounds.left, mLocalBounds.bottom - mLocalBounds.top,
+                    mPatch);
+            mGenerationId = cache.getGenerationId();
+        }
+        // We're not calling the public variant of drawPatch() here
+        // This method won't perform the quickReject() since we've already done it at this point
+        return renderer.drawPatch(mBitmap, mMesh, mEntry, mLocalBounds.left, mLocalBounds.top,
                 mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
     }
 
@@ -957,8 +966,12 @@
 private:
     SkBitmap* mBitmap;
     Res_png_9patch* mPatch;
+
     int mAlpha;
     SkXfermode::Mode mMode;
+
+    uint32_t mGenerationId;
+    const Patch* mMesh;
     AssetAtlas::Entry* mEntry;
 };
 
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 79e0b0c..cfb1e97e 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -467,7 +467,7 @@
             mat4 texTransform(layer->getTexTransform());
 
             mat4 invert;
-            invert.translate(0.0f, 1.0f, 0.0f);
+            invert.translate(0.0f, 1.0f);
             invert.scale(1.0f, -1.0f, 1.0f);
             layer->getTexTransform().multiply(invert);
 
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 75e280c..df744be 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -128,10 +128,27 @@
 
     void multiply(float v);
 
-    void translate(float x, float y, float z) {
-        Matrix4 u;
-        u.loadTranslate(x, y, z);
-        multiply(u);
+    void translate(float x, float y) {
+        if ((getType() & sGeometryMask) == kTypeTranslate) {
+            data[kTranslateX] += x;
+            data[kTranslateY] += y;
+        } else {
+            // Doing a translation will only affect the translate bit of the type
+            // Save the type
+            uint32_t type = mType;
+
+            Matrix4 u;
+            u.loadTranslate(x, y, 0.0f);
+            multiply(u);
+
+            // Restore the type and fix the translate bit
+            mType = type;
+            if (data[kTranslateX] != 0.0f || data[kTranslateY] != 0.0f) {
+                mType |= kTypeTranslate;
+            } else {
+                mType &= ~kTypeTranslate;
+            }
+        }
     }
 
     void scale(float sx, float sy, float sz) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f5343b1..d95a62c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1407,7 +1407,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::translate(float dx, float dy) {
-    currentTransform().translate(dx, dy, 0.0f);
+    currentTransform().translate(dx, dy);
 }
 
 void OpenGLRenderer::rotate(float degrees) {
@@ -2337,20 +2337,25 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    return drawPatch(bitmap, patch, mCaches.assetAtlas.getEntry(bitmap),
-            left, top, right, bottom, alpha, mode);
-}
-
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
-        AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
-        int alpha, SkXfermode::Mode mode) {
     if (quickReject(left, top, right, bottom)) {
         return DrawGlInfo::kStatusDone;
     }
 
+    AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
     const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
             right - left, bottom - top, patch);
 
+    return drawPatch(bitmap, mesh, entry, left, top, right, bottom, alpha, mode);
+}
+
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh,
+        AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
+        int alpha, SkXfermode::Mode mode) {
+
+    if (quickReject(left, top, right, bottom)) {
+        return DrawGlInfo::kStatusDone;
+    }
+
     if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
         mCaches.activeTexture(0);
         Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index e9ea2f32..ce4ce42 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -266,7 +266,7 @@
             float* vertices, int* colors, SkPaint* paint);
     virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
             float left, float top, float right, float bottom, SkPaint* paint);
-    status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, AssetAtlas::Entry* entry,
+    status_t drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
             float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode);
     virtual status_t drawColor(int color, SkXfermode::Mode mode);
     virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index c6ed275..c23e991 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -30,7 +30,9 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-PatchCache::PatchCache(): mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity) {
+PatchCache::PatchCache():
+        mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
+        mMeshBuffer(0), mGenerationId(0) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) {
         INIT_LOGD("  Setting patch cache size to %skB", property);
@@ -39,8 +41,6 @@
         INIT_LOGD("  Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE);
         mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE);
     }
-    mSize = 0;
-    mMeshBuffer = 0;
 }
 
 PatchCache::~PatchCache() {
@@ -58,7 +58,7 @@
     caches.resetVertexPointers();
 
     if (created) {
-        glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+        createVertexBuffer();
     }
 }
 
@@ -99,6 +99,12 @@
     mCache.clear();
 }
 
+void PatchCache::createVertexBuffer() {
+    glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+    mSize = 0;
+    mGenerationId++;
+}
+
 const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
         const uint32_t bitmapWidth, const uint32_t bitmapHeight,
         const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
@@ -127,8 +133,7 @@
             uint32_t size = newMesh->getSize();
             if (mSize + size > mMaxSize) {
                 clearCache();
-                glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
-                mSize = 0;
+                createVertexBuffer();
             }
 
             newMesh->offset = (GLintptr) mSize;
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 530dad0..1829b89 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -70,8 +70,13 @@
         return mMeshBuffer;
     }
 
+    uint32_t getGenerationId() const {
+        return mGenerationId;
+    }
+
 private:
     void clearCache();
+    void createVertexBuffer();
 
     struct PatchDescription {
         PatchDescription(): mPatch(NULL), mBitmapWidth(0), mBitmapHeight(0),
@@ -122,9 +127,11 @@
     uint32_t mMaxSize;
     uint32_t mSize;
 
+    LruCache<PatchDescription, Patch*> mCache;
+
     GLuint mMeshBuffer;
 
-    LruCache<PatchDescription, Patch*> mCache;
+    uint32_t mGenerationId;
 }; // class PatchCache
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index c127d68..9e4670e 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -172,7 +172,7 @@
             // up and to the left.
             // This offset value is based on an assumption that some hardware may use as
             // little as 12.4 precision, so we offset by slightly more than 1/16.
-            p.translate(.065, .065, 0);
+            p.translate(.065, .065);
             glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
         }
         mProjection = projectionMatrix;