Record possible clip rejects when recording display lists

This optimization allows us to quickly skip operations that lie
entirely outside of the known bounds of a display list. Because
of ViewGroup.setClipChildren, we must keep the operations recorded
in the display list. setClipChildren(false) is however a very
uncommon operation and we will therefore often benefit from this
new optimization.

Change-Id: I0942c864e55298e6dccd9977d15adefbce3ba3ad
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 1a11fbc..f9088ac 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -226,6 +226,11 @@
 
     while (!mReader.eof()) {
         int op = mReader.readInt();
+        if (op & OP_MAY_BE_SKIPPED_MASK) {
+            int skip = mReader.readInt();
+            ALOGD("%sSkip %d", (char*) indent, skip);
+            op &= ~OP_MAY_BE_SKIPPED_MASK;
+       }
 
         switch (op) {
             case DrawGLFunction: {
@@ -316,8 +321,9 @@
                 DisplayList* displayList = getDisplayList();
                 uint32_t width = getUInt();
                 uint32_t height = getUInt();
-                ALOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op],
-                    displayList, width, height, level + 1);
+                int32_t flags = getInt();
+                ALOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op],
+                    displayList, width, height, flags, level + 1);
                 renderer.outputDisplayList(displayList, level + 1);
             }
             break;
@@ -551,7 +557,7 @@
  * in the output() function, since that function processes the same list of opcodes for the
  * purposes of logging display list info for a given view.
  */
-bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) {
+bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) {
     bool needsInvalidate = false;
     TextContainer text;
     mReader.rewind();
@@ -572,6 +578,18 @@
     int saveCount = renderer.getSaveCount() - 1;
     while (!mReader.eof()) {
         int op = mReader.readInt();
+        if (op & OP_MAY_BE_SKIPPED_MASK) {
+            int32_t skip = mReader.readInt() * 4;
+            if (CC_LIKELY(flags & kReplayFlag_ClipChildren)) {
+                mReader.skip(skip);
+                DISPLAY_LIST_LOGD("%s%s skipping %d bytes", (char*) indent,
+                        OP_NAMES[op & ~OP_MAY_BE_SKIPPED_MASK], skip);
+                continue;
+            } else {
+                op &= ~OP_MAY_BE_SKIPPED_MASK;
+                ALOGD("%s", OP_NAMES[op]);
+            }
+        }
         logBuffer.writeCommand(level, op);
 
         switch (op) {
@@ -584,7 +602,7 @@
             }
             break;
             case Save: {
-                int rendererNum = getInt();
+                int32_t rendererNum = getInt();
                 DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum);
                 renderer.save(rendererNum);
             }
@@ -595,7 +613,7 @@
             }
             break;
             case RestoreToCount: {
-                int restoreCount = saveCount + getInt();
+                int32_t restoreCount = saveCount + getInt();
                 DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount);
                 renderer.restoreToCount(restoreCount);
             }
@@ -606,7 +624,7 @@
                 float f3 = getFloat();
                 float f4 = getFloat();
                 SkPaint* paint = getPaint(renderer);
-                int flags = getInt();
+                int32_t flags = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent,
                     OP_NAMES[op], f1, f2, f3, f4, paint, flags);
                 renderer.saveLayer(f1, f2, f3, f4, paint, flags);
@@ -617,8 +635,8 @@
                 float f2 = getFloat();
                 float f3 = getFloat();
                 float f4 = getFloat();
-                int alpha = getInt();
-                int flags = getInt();
+                int32_t alpha = getInt();
+                int32_t flags = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent,
                     OP_NAMES[op], f1, f2, f3, f4, alpha, flags);
                 renderer.saveLayerAlpha(f1, f2, f3, f4, alpha, flags);
@@ -668,7 +686,7 @@
                 float f2 = getFloat();
                 float f3 = getFloat();
                 float f4 = getFloat();
-                int regionOp = getInt();
+                int32_t regionOp = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op],
                     f1, f2, f3, f4, regionOp);
                 renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp);
@@ -678,10 +696,11 @@
                 DisplayList* displayList = getDisplayList();
                 uint32_t width = getUInt();
                 uint32_t height = getUInt();
-                DISPLAY_LIST_LOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op],
-                    displayList, width, height, level + 1);
+                int32_t flags = getInt();
+                DISPLAY_LIST_LOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op],
+                    displayList, width, height, flags, level + 1);
                 needsInvalidate |= renderer.drawDisplayList(displayList, width, height,
-                        dirty, level + 1);
+                        dirty, flags, level + 1);
             }
             break;
             case DrawLayer: {
@@ -730,7 +749,7 @@
             }
             break;
             case DrawBitmapMesh: {
-                int verticesCount = 0;
+                int32_t verticesCount = 0;
                 uint32_t colorsCount = 0;
 
                 SkBitmap* bitmap = getBitmap();
@@ -738,7 +757,7 @@
                 uint32_t meshHeight = getInt();
                 float* vertices = getFloats(verticesCount);
                 bool hasColors = getInt();
-                int* colors = hasColors ? getInts(colorsCount) : NULL;
+                int32_t* colors = hasColors ? getInts(colorsCount) : NULL;
                 SkPaint* paint = getPaint(renderer);
 
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
@@ -771,8 +790,8 @@
             }
             break;
             case DrawColor: {
-                int color = getInt();
-                int xferMode = getInt();
+                int32_t color = getInt();
+                int32_t xferMode = getInt();
                 DISPLAY_LIST_LOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode);
                 renderer.drawColor(color, (SkXfermode::Mode) xferMode);
             }
@@ -829,7 +848,7 @@
                 float f4 = getFloat();
                 float f5 = getFloat();
                 float f6 = getFloat();
-                int i1 = getInt();
+                int32_t i1 = getInt();
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p",
                     (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint);
@@ -844,7 +863,7 @@
             }
             break;
             case DrawLines: {
-                int count = 0;
+                int32_t count = 0;
                 float* points = getFloats(count);
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
@@ -852,7 +871,7 @@
             }
             break;
             case DrawPoints: {
-                int count = 0;
+                int32_t count = 0;
                 float* points = getFloats(count);
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
@@ -861,7 +880,7 @@
             break;
             case DrawText: {
                 getText(&text);
-                int count = getInt();
+                int32_t count = getInt();
                 float x = getFloat();
                 float y = getFloat();
                 SkPaint* paint = getPaint(renderer);
@@ -873,8 +892,8 @@
             break;
             case DrawPosText: {
                 getText(&text);
-                int count = getInt();
-                int positionsCount = 0;
+                int32_t count = getInt();
+                int32_t positionsCount = 0;
                 float* positions = getFloats(positionsCount);
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent,
@@ -913,7 +932,7 @@
                 float radius = getFloat();
                 float dx = getFloat();
                 float dy = getFloat();
-                int color = getInt();
+                int32_t color = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op],
                     radius, dx, dy, color);
                 renderer.setupShadow(radius, dx, dy, color);
@@ -925,8 +944,8 @@
             }
             break;
             case SetupPaintFilter: {
-                int clearBits = getInt();
-                int setBits = getInt();
+                int32_t clearBits = getInt();
+                int32_t setBits = getInt();
                 DISPLAY_LIST_LOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op],
                         clearBits, setBits);
                 renderer.setupPaintFilter(clearBits, setBits);
@@ -949,7 +968,8 @@
 // Base structure
 ///////////////////////////////////////////////////////////////////////////////
 
-DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE), mHasDrawOps(false) {
+DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE),
+        mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false), mHasDrawOps(false) {
 }
 
 DisplayListRenderer::~DisplayListRenderer() {
@@ -1019,6 +1039,7 @@
 
 void DisplayListRenderer::finish() {
     insertRestoreToCount();
+    insertTranlate();
     OpenGLRenderer::finish();
 }
 
@@ -1043,15 +1064,18 @@
 
 void DisplayListRenderer::restore() {
     if (mRestoreSaveCount < 0) {
-        addOp(DisplayList::Restore);
-    } else {
-        mRestoreSaveCount--;
+        restoreToCount(getSaveCount() - 1);
+        return;
     }
+
+    mRestoreSaveCount--;
+    insertTranlate();
     OpenGLRenderer::restore();
 }
 
 void DisplayListRenderer::restoreToCount(int saveCount) {
     mRestoreSaveCount = saveCount;
+    insertTranlate();
     OpenGLRenderer::restoreToCount(saveCount);
 }
 
@@ -1074,8 +1098,10 @@
 }
 
 void DisplayListRenderer::translate(float dx, float dy) {
-    addOp(DisplayList::Translate);
-    addPoint(dx, dy);
+    mHasTranslate = true;
+    mTranslateX += dx;
+    mTranslateY += dy;
+    insertRestoreToCount();
     OpenGLRenderer::translate(dx, dy);
 }
 
@@ -1118,12 +1144,15 @@
 }
 
 bool DisplayListRenderer::drawDisplayList(DisplayList* displayList,
-        uint32_t width, uint32_t height, Rect& dirty, uint32_t level) {
+        uint32_t width, uint32_t height, Rect& dirty, int32_t flags, uint32_t level) {
     // dirty is an out parameter and should not be recorded,
     // it matters only when replaying the display list
-    addOp(DisplayList::DrawDisplayList);
+    const bool reject = quickReject(0.0f, 0.0f, width, height);
+    uint32_t* location = addOp(DisplayList::DrawDisplayList, reject);
     addDisplayList(displayList);
     addSize(width, height);
+    addInt(flags);
+    addSkip(location);
     return false;
 }
 
@@ -1134,30 +1163,38 @@
     addPaint(paint);
 }
 
-void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
-        SkPaint* paint) {
-    addOp(DisplayList::DrawBitmap);
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
+    const bool reject = quickReject(left, top, left + bitmap->width(), top + bitmap->height());
+    uint32_t* location = addOp(DisplayList::DrawBitmap, reject);
     addBitmap(bitmap);
     addPoint(left, top);
     addPaint(paint);
+    addSkip(location);
 }
 
-void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
-        SkPaint* paint) {
-    addOp(DisplayList::DrawBitmapMatrix);
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
+    Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
+    const mat4 transform(*matrix);
+    transform.mapRect(r);
+
+    const bool reject = quickReject(r.left, r.top, r.right, r.bottom);
+    uint32_t* location = addOp(DisplayList::DrawBitmapMatrix, reject);
     addBitmap(bitmap);
     addMatrix(matrix);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
         float srcRight, float srcBottom, float dstLeft, float dstTop,
         float dstRight, float dstBottom, SkPaint* paint) {
-    addOp(DisplayList::DrawBitmapRect);
+    const bool reject = quickReject(dstLeft, dstTop, dstRight, dstBottom);
+    uint32_t* location = addOp(DisplayList::DrawBitmapRect, reject);
     addBitmap(bitmap);
     addBounds(srcLeft, srcTop, srcRight, srcBottom);
     addBounds(dstLeft, dstTop, dstRight, dstBottom);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
@@ -1179,13 +1216,15 @@
 void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
         const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
         float left, float top, float right, float bottom, SkPaint* paint) {
-    addOp(DisplayList::DrawPatch);
+    const bool reject = quickReject(left, top, right, bottom);
+    uint32_t* location = addOp(DisplayList::DrawPatch, reject);
     addBitmap(bitmap);
     addInts(xDivs, width);
     addInts(yDivs, height);
     addUInts(colors, numColors);
     addBounds(left, top, right, bottom);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -1196,17 +1235,23 @@
 
 void DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
         SkPaint* paint) {
-    addOp(DisplayList::DrawRect);
+    const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
+            quickReject(left, top, right, bottom);
+    uint32_t* location = addOp(DisplayList::DrawRect, reject);
     addBounds(left, top, right, bottom);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, SkPaint* paint) {
-    addOp(DisplayList::DrawRoundRect);
+    const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
+            quickReject(left, top, right, bottom);
+    uint32_t* location = addOp(DisplayList::DrawRoundRect, reject);
     addBounds(left, top, right, bottom);
     addPoint(rx, ry);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
@@ -1233,9 +1278,15 @@
 }
 
 void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
-    addOp(DisplayList::DrawPath);
+    float left, top, offset;
+    uint32_t width, height;
+    computePathBounds(path, paint, left, top, offset, width, height);
+
+    const bool reject = quickReject(left - offset, top - offset, width, height);
+    uint32_t* location = addOp(DisplayList::DrawPath, reject);
     addPath(path);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
@@ -1252,11 +1303,8 @@
 
 void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, SkPaint* paint, float length) {
-    if (count <= 0) return;
-    addOp(DisplayList::DrawText);
-    addText(text, bytesCount);
-    addInt(count);
-    addPoint(x, y);
+    if (!text || count <= 0) return;
+
     // TODO: We should probably make a copy of the paint instead of modifying
     //       it; modifying the paint will change its generationID the first
     //       time, which might impact caches. More investigation needed to
@@ -1265,13 +1313,27 @@
     //       its own copy as it does right now.
     // Beware: this needs Glyph encoding (already done on the Paint constructor)
     paint->setAntiAlias(true);
+    if (length < 0.0f) length = paint->measureText(text, bytesCount);
+
+    bool reject = false;
+    if (CC_LIKELY(paint->getTextAlign() == SkPaint::kLeft_Align)) {
+        SkPaint::FontMetrics metrics;
+        paint->getFontMetrics(&metrics, 0.0f);
+        reject = quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom);
+    }
+
+    uint32_t* location = addOp(DisplayList::DrawText, reject);
+    addText(text, bytesCount);
+    addInt(count);
+    addPoint(x, y);
     addPaint(paint);
-    addFloat(length < 0.0f ? paint->measureText(text, bytesCount) : length);
+    addFloat(length);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count,
         const float* positions, SkPaint* paint) {
-    if (count <= 0) return;
+    if (!text || count <= 0) return;
     addOp(DisplayList::DrawPosText);
     addText(text, bytesCount);
     addInt(count);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 46506e4..6dd47be 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -42,6 +42,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #define MIN_WRITER_SIZE 4096
+#define OP_MAY_BE_SKIPPED_MASK 0xff000000
 
 // Debug
 #if DEBUG_DISPLAY_LIST
@@ -110,13 +111,18 @@
         DrawGLFunction,
     };
 
+    // See flags defined in DisplayList.java
+    enum ReplayFlag {
+        kReplayFlag_ClipChildren = 0x1
+    };
+
     static const char* OP_NAMES[];
 
     void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
 
     ANDROID_API size_t getSize();
 
-    bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
+    bool replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0);
 
     void output(OpenGLRenderer& renderer, uint32_t level = 0);
 
@@ -167,11 +173,11 @@
         return (SkiaColorFilter*) getInt();
     }
 
-    inline int getIndex() {
+    inline int32_t getIndex() {
         return mReader.readInt();
     }
 
-    inline int getInt() {
+    inline int32_t getInt() {
         return mReader.readInt();
     }
 
@@ -209,7 +215,7 @@
         return (uint32_t*) mReader.skip(count * sizeof(uint32_t));
     }
 
-    float* getFloats(int& count) {
+    float* getFloats(int32_t& count) {
         count = getInt();
         return (float*) mReader.skip(count * sizeof(float));
     }
@@ -279,7 +285,7 @@
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
     virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
-            Rect& dirty, uint32_t level = 0);
+            Rect& dirty, int32_t flags, uint32_t level = 0);
     virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
@@ -358,13 +364,45 @@
         }
     }
 
-    inline void addOp(DisplayList::Op drawOp) {
+    void insertTranlate() {
+        if (mHasTranslate) {
+            if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
+                mWriter.writeInt(DisplayList::Translate);
+                addPoint(mTranslateX, mTranslateY);
+                mTranslateX = mTranslateY = 0.0f;
+            }
+            mHasTranslate = false;
+        }
+    }
+
+    inline void addOp(const DisplayList::Op drawOp) {
         insertRestoreToCount();
+        insertTranlate();
         mWriter.writeInt(drawOp);
         mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList;
     }
 
-    inline void addInt(int value) {
+    uint32_t* addOp(const DisplayList::Op drawOp, const bool reject) {
+        insertRestoreToCount();
+        insertTranlate();
+        mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList;
+        if (reject) {
+            mWriter.writeInt(OP_MAY_BE_SKIPPED_MASK | drawOp);
+            mWriter.writeInt(0);
+            uint32_t* location = reject ? mWriter.peek32(mWriter.size() - 4) : NULL;
+            return location;
+        }
+        mWriter.writeInt(drawOp);
+        return NULL;
+    }
+
+    inline void addSkip(uint32_t* location) {
+        if (location) {
+            *location = (int32_t) (mWriter.peek32(mWriter.size() - 4) - location);
+        }
+    }
+
+    inline void addInt(int32_t value) {
         mWriter.writeInt(value);
     }
 
@@ -391,9 +429,9 @@
         mWriter.writeScalar(value);
     }
 
-    void addFloats(const float* values, int count) {
+    void addFloats(const float* values, int32_t count) {
         mWriter.writeInt(count);
-        for (int i = 0; i < count; i++) {
+        for (int32_t i = 0; i < count; i++) {
             mWriter.writeScalar(values[i]);
         }
     }
@@ -513,6 +551,11 @@
     SkWriter32 mWriter;
 
     int mRestoreSaveCount;
+
+    float mTranslateX;
+    float mTranslateY;
+    bool mHasTranslate;
+
     bool mHasDrawOps;
 
     friend class DisplayList;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index afae70f..55e2ca5 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1321,7 +1321,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
-        Rect& dirty, uint32_t level) {
+        Rect& dirty, int32_t flags, uint32_t level) {
     if (quickReject(0.0f, 0.0f, width, height)) {
         return false;
     }
@@ -1329,7 +1329,7 @@
     // All the usual checks and setup operations (quickReject, setupDraw, etc.)
     // will be performed by the display list itself
     if (displayList && displayList->isRenderable()) {
-        return displayList->replay(*this, dirty, level);
+        return displayList->replay(*this, dirty, flags, level);
     }
 
     return false;
@@ -2189,8 +2189,7 @@
     SkPaint::FontMetrics metrics;
     paint->getFontMetrics(&metrics, 0.0f);
     // If no length was specified, just perform the hit test on the Y axis
-    if (quickReject(x, y + metrics.fTop,
-            x + (length >= 0.0f ? length : INT_MAX / 2), y + metrics.fBottom)) {
+    if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
         return;
     }
 
@@ -2298,6 +2297,7 @@
 
     mCaches.activeTexture(0);
 
+    // TODO: Perform early clip test before we rasterize the path
     const PathTexture* texture = mCaches.pathCache.get(path, paint);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 3c2d09e..3f63c3fe 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -98,7 +98,7 @@
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
     virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
-            Rect& dirty, uint32_t level = 0);
+            Rect& dirty, int32_t flags, uint32_t level = 0);
     virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0);
     virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index e893f7a..e09c243 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -24,6 +24,23 @@
 namespace android {
 namespace uirenderer {
 
+// Defined in ShapeCache.h
+void computePathBounds(const SkPath *path, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+    const SkRect& bounds = path->getBounds();
+
+    const float pathWidth = fmax(bounds.width(), 1.0f);
+    const float pathHeight = fmax(bounds.height(), 1.0f);
+
+    left = bounds.fLeft;
+    top = bounds.fTop;
+
+    offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+
+    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Path cache
 ///////////////////////////////////////////////////////////////////////////////
@@ -69,6 +86,9 @@
     PathCacheEntry entry(path, paint);
     PathTexture* texture = mCache.get(entry);
 
+    float left, top, offset;
+    uint32_t width, height;
+
     if (!texture) {
         texture = addTexture(entry, path, paint);
     } else if (path->getGenerationID() != texture->generation) {
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
index 30ce690..f180e94 100644
--- a/libs/hwui/ShapeCache.h
+++ b/libs/hwui/ShapeCache.h
@@ -489,18 +489,16 @@
     }
 }
 
+void computePathBounds(const SkPath *path, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+
 template<class Entry>
 PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
         const SkPaint* paint) {
-    const SkRect& bounds = path->getBounds();
 
-    const float pathWidth = fmax(bounds.width(), 1.0f);
-    const float pathHeight = fmax(bounds.height(), 1.0f);
-
-    const float offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
-
-    const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
-    const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+    float left, top, offset;
+    uint32_t width, height;
+    computePathBounds(path, paint, left, top, offset, width, height);
 
     if (width > mMaxTextureSize || height > mMaxTextureSize) {
         ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
@@ -517,8 +515,8 @@
     }
 
     PathTexture* texture = new PathTexture;
-    texture->left = bounds.fLeft;
-    texture->top = bounds.fTop;
+    texture->left = left;
+    texture->top = top;
     texture->offset = offset;
     texture->width = width;
     texture->height = height;
@@ -542,7 +540,7 @@
     SkSafeUnref(pathPaint.setXfermode(mode));
 
     SkCanvas canvas(bitmap);
-    canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
+    canvas.translate(-left + offset, -top + offset);
     canvas.drawPath(*path, pathPaint);
 
     generateTexture(bitmap, texture);