DisplayList draw operation reordering

bug:8037003

The reordering enables similar operations to draw together, minimizing the
OpenGL state change operations that go inbetween draws. Eventually, multiple
complete canvas draw operations will be merged (into a single glDrawArrays call,
for example)

Reorders DisplayList draw operations when:

-They can move backwards in the command stream to be after similar
operations without violating draw ordering

-The OpenGLRenderer is in a simple, replayable state (no complex clip,
or filter/shadow etc)

Also adds two system properties to control the deferral/reordering:
    "debug.hwui.disable_draw_defer"
    "debug.hwui.disable_draw_reorder"
which can be set to "true" to control the display list manipulation

Change-Id: I5e89f3cb0ea2d2afd3e15c64d7f32b8406777a32
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index db64c99..5f2a4d5 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -12,6 +12,7 @@
 		GammaFontRenderer.cpp \
 		Caches.cpp \
 		DisplayList.cpp \
+		DeferredDisplayList.cpp \
 		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		Dither.cpp \
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 20eb5e1..9a6494f 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -74,6 +74,9 @@
 // Turn on to enable additional debugging in the font renderers
 #define DEBUG_FONT_RENDERER 0
 
+// Turn on to log draw operation batching and deferral information
+#define DEBUG_DEFER 0
+
 // Turn on to dump display list state
 #define DEBUG_DISPLAY_LIST 0
 
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
new file mode 100644
index 0000000..8962964
--- /dev/null
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/Trace.h>
+
+#include "Debug.h"
+#include "DisplayListOp.h"
+#include "OpenGLRenderer.h"
+
+#if DEBUG_DEFER
+    #define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+    #define DEFER_LOGD(...)
+#endif
+
+namespace android {
+namespace uirenderer {
+
+class DrawOpBatch {
+public:
+    DrawOpBatch() {
+        mOps.clear();
+    }
+
+    ~DrawOpBatch() {
+        mOps.clear();
+    }
+
+    void add(DrawOp* op) {
+        // NOTE: ignore empty bounds special case, since we don't merge across those ops
+        mBounds.unionWith(op->state.mBounds);
+        mOps.add(op);
+    }
+
+    bool intersects(Rect& rect) {
+        if (!rect.intersects(mBounds)) return false;
+        for (unsigned int i = 0; i < mOps.size(); i++) {
+            if (rect.intersects(mOps[i]->state.mBounds)) {
+#if DEBUG_DEFER
+                DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i],
+                        mOps[i]->state.mBounds.left, mOps[i]->state.mBounds.top,
+                        mOps[i]->state.mBounds.right, mOps[i]->state.mBounds.bottom);
+                mOps[i]->output(2);
+#endif
+                return true;
+            }
+        }
+        return false;
+    }
+
+    Vector<DrawOp*> mOps;
+private:
+    Rect mBounds;
+};
+
+void DeferredDisplayList::clear() {
+    for (int i = 0; i < kOpBatch_Count; i++) {
+        mBatchIndices[i] = -1;
+    }
+    for (unsigned int i = 0; i < mBatches.size(); i++) {
+        delete mBatches[i];
+    }
+    mBatches.clear();
+}
+
+void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) {
+    if (CC_UNLIKELY(disallowReorder)) {
+        if (!mBatches.isEmpty()) {
+            mBatches[0]->add(op);
+            return;
+        }
+        DrawOpBatch* b = new DrawOpBatch();
+        b->add(op);
+        mBatches.add(b);
+        return;
+    }
+
+    // disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge
+    // the new op into it
+    DrawOpBatch* targetBatch = NULL;
+    int batchId = op->getBatchId();
+
+    if (!mBatches.isEmpty()) {
+        if (op->state.mBounds.isEmpty()) {
+            // don't know the bounds for op, so add to last batch and start from scratch on next op
+            mBatches.top()->add(op);
+            for (int i = 0; i < kOpBatch_Count; i++) {
+                mBatchIndices[i] = -1;
+            }
+#if DEBUG_DEFER
+            DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
+            op->output(2);
+#endif
+            return;
+        }
+
+        if (batchId >= 0 && mBatchIndices[batchId] != -1) {
+            int targetIndex = mBatchIndices[batchId];
+            targetBatch = mBatches[targetIndex];
+            // iterate back toward target to see if anything drawn since should overlap the new op
+            for (int i = mBatches.size() - 1; i > targetIndex; i--) {
+                DrawOpBatch* overBatch = mBatches[i];
+                if (overBatch->intersects(op->state.mBounds)) {
+                    targetBatch = NULL;
+#if DEBUG_DEFER
+                    DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
+                            targetIndex, i);
+                    op->output(2);
+#endif
+                    break;
+                }
+            }
+        }
+    }
+    if (!targetBatch) {
+        targetBatch = new DrawOpBatch();
+        mBatches.add(targetBatch);
+        if (batchId >= 0) {
+            mBatchIndices[batchId] = mBatches.size() - 1;
+        }
+    }
+    targetBatch->add(op);
+}
+
+status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
+        uint32_t level) {
+    ATRACE_CALL();
+    status_t status = DrawGlInfo::kStatusDone;
+
+    if (isEmpty()) return status; // nothing to flush
+
+    DEFER_LOGD("--flushing");
+    DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
+    int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    int opCount = 0;
+    for (unsigned int i = 0; i < mBatches.size(); i++) {
+        DrawOpBatch* batch = mBatches[i];
+        for (unsigned int j = 0; j < batch->mOps.size(); j++) {
+            DrawOp* op = batch->mOps[j];
+
+            renderer.restoreDisplayState(op->state);
+
+#if DEBUG_DEFER
+            op->output(2);
+#endif
+            status |= op->applyDraw(renderer, dirty, level,
+                    op->state.mMultipliedAlpha >= 0, op->state.mMultipliedAlpha);
+            opCount++;
+        }
+    }
+
+    DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount);
+    renderer.restoreToCount(restoreTo);
+    renderer.setDrawModifiers(restoreDrawModifiers);
+    clear();
+    return status;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
new file mode 100644
index 0000000..4fcb297
--- /dev/null
+++ b/libs/hwui/DeferredDisplayList.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
+#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
+
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+#include "Matrix.h"
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class DrawOp;
+class DrawOpBatch;
+class OpenGLRenderer;
+class SkiaShader;
+
+class DeferredDisplayList {
+public:
+    DeferredDisplayList() { clear(); }
+    ~DeferredDisplayList() { clear(); }
+
+    enum OpBatchId {
+        kOpBatch_None = -1, // Don't batch
+        kOpBatch_Bitmap,
+        kOpBatch_Patch,
+        kOpBatch_AlphaVertices,
+        kOpBatch_Vertices,
+        kOpBatch_AlphaMaskTexture,
+        kOpBatch_Text,
+        kOpBatch_ColorText,
+
+        kOpBatch_Count, // Add other batch ids before this
+    };
+
+    bool isEmpty() { return mBatches.isEmpty(); }
+
+    /**
+     * Plays back all of the draw ops recorded into batches to the renderer.
+     * Adjusts the state of the renderer as necessary, and restores it when complete
+     */
+    status_t flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
+            uint32_t level);
+
+    /**
+     * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if
+     * disallowReorder is false, respecting draw order when overlaps occur
+     */
+    void add(DrawOp* op, bool disallowReorder);
+
+private:
+    void clear();
+
+
+    Vector<DrawOpBatch*> mBatches;
+    int mBatchIndices[kOpBatch_Count];
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index a52ea98..8aac628 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "Debug.h"
 #include "DisplayList.h"
 #include "DisplayListOp.h"
 #include "DisplayListLogBuffer.h"
@@ -386,7 +387,8 @@
     }
 }
 
-status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) {
+status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level,
+        DeferredDisplayList* deferredList) {
     status_t drawGlStatus = DrawGlInfo::kStatusDone;
 
 #if DEBUG_DISPLAY_LIST
@@ -401,6 +403,12 @@
     int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "",
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+
+    if (mAlpha < 1 && !mCaching && CC_LIKELY(deferredList)) {
+        // flush before a saveLayerAlpha/setAlpha
+        // TODO: make this cleaner
+        drawGlStatus |= deferredList->flush(renderer, dirty, flags, level);
+    }
     setViewProperties(renderer, level);
 
     if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
@@ -418,8 +426,13 @@
         Caches::getInstance().eventMark(strlen(op->name()), op->name());
 #endif
 
-        drawGlStatus |= op->replay(renderer, dirty, flags,
-                saveCount, level, mCaching, mMultipliedAlpha);
+        if (deferredList) {
+            drawGlStatus |= op->replay(renderer, dirty, flags,
+                    saveCount, level, mCaching, mMultipliedAlpha, *deferredList);
+        } else {
+            drawGlStatus |= op->replay(renderer, dirty, flags,
+                    saveCount, level, mCaching, mMultipliedAlpha);
+        }
         logBuffer.writeCommand(level, op->name());
     }
 
@@ -429,6 +442,11 @@
 
     DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", (level + 1) * 2, "", this, mName.string(),
             drawGlStatus);
+
+    if (!level && CC_LIKELY(deferredList)) {
+        drawGlStatus |= deferredList->flush(renderer, dirty, flags, level);
+    }
+
     return drawGlStatus;
 }
 
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 70a9755..d06827d 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -44,6 +44,7 @@
 namespace android {
 namespace uirenderer {
 
+class DeferredDisplayList;
 class DisplayListOp;
 class DisplayListRenderer;
 class OpenGLRenderer;
@@ -83,7 +84,8 @@
 
     void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
 
-    status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0);
+    status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0,
+            DeferredDisplayList* deferredList = NULL);
 
     void output(uint32_t level = 0);
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 78b432c..8e80647 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -22,6 +22,7 @@
 #include <private/hwui/DrawGlInfo.h>
 
 #include "OpenGLRenderer.h"
+#include "DeferredDisplayList.h"
 #include "DisplayListRenderer.h"
 #include "utils/LinearAllocator.h"
 
@@ -43,7 +44,6 @@
 #define OP_LOGS(s) OP_LOG("%s", s)
 #define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
 
-
 namespace android {
 namespace uirenderer {
 
@@ -74,11 +74,15 @@
         kOpLogFlag_JSON = 0x2 // TODO: add?
     };
 
-    //TODO: for draw batching, DrawOps should override a virtual sub-method, with
-    // DrawOps::apply deferring operations to a different list if possible
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
             uint32_t level, bool caching, int multipliedAlpha) = 0;
 
+    // same as replay above, but draw operations will defer into the deferredList if possible
+    // NOTE: colorfilters, paintfilters, shaders, shadow, and complex clips prevent deferral
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha,
+            DeferredDisplayList& deferredList) = 0;
+
     virtual void output(int level, uint32_t flags = 0) = 0;
 
     // NOTE: it would be nice to declare constants and overriding the implementation in each op to
@@ -98,7 +102,28 @@
         return DrawGlInfo::kStatusDone;
     }
 
+    /**
+     * State operations are applied directly to the renderer, but can cause the deferred drawing op
+     * list to flush
+     */
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+        status_t status = DrawGlInfo::kStatusDone;
+        if (requiresDrawOpFlush()) {
+            // will be setting renderer state that affects ops in deferredList, so flush list first
+            status |= deferredList.flush(renderer, dirty, flags, level);
+        }
+        applyState(renderer, saveCount);
+        return status;
+    }
+
     virtual void applyState(OpenGLRenderer& renderer, int saveCount) = 0;
+
+    /**
+     * Returns true if it affects renderer drawing state in such a way to break deferral
+     * see OpenGLRenderer::disallowDeferral()
+     */
+    virtual bool requiresDrawOpFlush() { return false; }
 };
 
 class DrawOp : public DisplayListOp {
@@ -115,6 +140,33 @@
         return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
     }
 
+    /** Draw operations are stored in the deferredList with information necessary for playback */
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+        if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
+            return DrawGlInfo::kStatusDone;
+        }
+
+        if (renderer.disallowDeferral()) {
+            // dispatch draw immediately, since the renderer's state is too complex for deferral
+            return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
+        }
+
+        if (!caching) multipliedAlpha = -1;
+        state.mMultipliedAlpha = multipliedAlpha;
+        if (!getLocalBounds(state.mBounds)) {
+            // empty bounds signify bounds can't be calculated
+            state.mBounds.setEmpty();
+        }
+
+        if (!renderer.storeDisplayState(state)) {
+            // op wasn't quick-rejected, so defer
+            deferredList.add(this, renderer.disallowReorder());
+        }
+
+        return DrawGlInfo::kStatusDone;
+    }
+
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) = 0;
 
@@ -125,6 +177,19 @@
     void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
     bool getQuickRejected() { return mQuickRejected; }
 
+    /** Batching disabled by default, turned on for individual ops */
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_None;
+    }
+
+    float strokeWidthOutset() { return mPaint->getStrokeWidth() * 0.5f; }
+
+public:
+    /**
+     * Stores the relevant canvas state of the object between deferral and replay (if the canvas
+     * state supports being stored) See OpenGLRenderer::simpleClipAndState()
+     */
+    DeferredDisplayState state;
 protected:
     SkPaint* getPaint(OpenGLRenderer& renderer) {
         return renderer.filterPaint(mPaint);
@@ -191,6 +256,8 @@
     }
 
     virtual const char* name() { return "RestoreToCount"; }
+    // Note: don't have to return true for requiresDrawOpFlush - even though restore can create a
+    // complex clip, the clip and matrix are overridden by DeferredDisplayList::flush()
 
 private:
     int mCount;
@@ -211,6 +278,7 @@
     }
 
     virtual const char* name() { return "SaveLayer"; }
+    virtual bool requiresDrawOpFlush() { return true; }
 
 private:
     Rect mArea;
@@ -232,6 +300,8 @@
     }
 
     virtual const char* name() { return "SaveLayerAlpha"; }
+    virtual bool requiresDrawOpFlush() { return true; }
+
 private:
     Rect mArea;
     int mAlpha;
@@ -391,6 +461,7 @@
     }
 
     virtual const char* name() { return "ClipPath"; }
+    virtual bool requiresDrawOpFlush() { return true; }
 
 private:
     SkPath* mPath;
@@ -413,6 +484,7 @@
     }
 
     virtual const char* name() { return "ClipRegion"; }
+    virtual bool requiresDrawOpFlush() { return true; }
 
 private:
     SkRegion* mRegion;
@@ -582,6 +654,9 @@
     }
 
     virtual const char* name() { return "DrawBitmap"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 protected:
     SkBitmap* mBitmap;
@@ -606,6 +681,9 @@
     }
 
     virtual const char* name() { return "DrawBitmap"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -632,6 +710,9 @@
     }
 
     virtual const char* name() { return "DrawBitmapRect"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -654,6 +735,9 @@
     }
 
     virtual const char* name() { return "DrawBitmapData"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 };
 
 class DrawBitmapMeshOp : public DrawOp {
@@ -674,6 +758,9 @@
     }
 
     virtual const char* name() { return "DrawBitmapMesh"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -708,6 +795,9 @@
     }
 
     virtual const char* name() { return "DrawPatch"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Patch;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -748,15 +838,21 @@
             : DrawBoundedOp(left, top, right, bottom, paint) {};
 
     bool getLocalBounds(Rect& localBounds) {
+        localBounds.set(mLocalBounds);
         if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
-            float outset = mPaint->getStrokeWidth() * 0.5f;
-            localBounds.set(mLocalBounds.left - outset, mLocalBounds.top - outset,
-                    mLocalBounds.right + outset, mLocalBounds.bottom + outset);
-        } else {
-            localBounds.set(mLocalBounds);
+            localBounds.outset(strokeWidthOutset());
         }
         return true;
     }
+
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        if (mPaint->getPathEffect()) {
+            return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+        }
+        return mPaint->isAntiAlias() ?
+                DeferredDisplayList::kOpBatch_AlphaVertices :
+                DeferredDisplayList::kOpBatch_Vertices;
+    }
 };
 
 class DrawRectOp : public DrawStrokableOp {
@@ -793,6 +889,10 @@
 
     virtual const char* name() { return "DrawRects"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Vertices;
+    }
+
 private:
     const float* mRects;
     int mCount;
@@ -912,22 +1012,24 @@
 
     virtual const char* name() { return "DrawPath"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+    }
 private:
     SkPath* mPath;
 };
 
-class DrawLinesOp : public DrawOp {
+class DrawLinesOp : public DrawBoundedOp {
 public:
     DrawLinesOp(float* points, int count, SkPaint* paint)
-            : DrawOp(paint), mPoints(points), mCount(count) {
-        /* TODO: inherit from DrawBoundedOp and calculate localbounds something like:
+            : DrawBoundedOp(paint), mPoints(points), mCount(count) {
         for (int i = 0; i < count; i += 2) {
             mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
             mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
             mLocalBounds.top = fminf(mLocalBounds.top, points[i+1]);
             mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i+1]);
         }
-        */
+        mLocalBounds.outset(strokeWidthOutset());
     }
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
@@ -941,6 +1043,12 @@
 
     virtual const char* name() { return "DrawLines"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return mPaint->isAntiAlias() ?
+                DeferredDisplayList::kOpBatch_AlphaVertices :
+                DeferredDisplayList::kOpBatch_Vertices;
+    }
+
 protected:
     float* mPoints;
     int mCount;
@@ -971,6 +1079,12 @@
     virtual void output(int level, uint32_t flags) {
         OP_LOG("Draw some text, %d bytes", mBytesCount);
     }
+
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return mPaint->getColor() == 0xff000000 ?
+                DeferredDisplayList::kOpBatch_Text :
+                DeferredDisplayList::kOpBatch_ColorText;
+    }
 protected:
     const char* mText;
     int mBytesCount;
@@ -1042,6 +1156,12 @@
 
     virtual const char* name() { return "DrawText"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return mPaint->getColor() == 0xff000000 ?
+                DeferredDisplayList::kOpBatch_Text :
+                DeferredDisplayList::kOpBatch_ColorText;
+    }
+
 private:
     const char* mText;
     int mBytesCount;
@@ -1083,9 +1203,21 @@
 public:
     DrawDisplayListOp(DisplayList* displayList, int flags)
             : DrawOp(0), mDisplayList(displayList), mFlags(flags) {}
+
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+        if (mDisplayList && mDisplayList->isRenderable()) {
+            return mDisplayList->replay(renderer, dirty, mFlags, level + 1, &deferredList);
+        }
+        return DrawGlInfo::kStatusDone;
+    }
+
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) {
-        return renderer.drawDisplayList(mDisplayList, dirty, mFlags, level + 1);
+        if (mDisplayList && mDisplayList->isRenderable()) {
+            return mDisplayList->replay(renderer, dirty, mFlags, level + 1);
+        }
+        return DrawGlInfo::kStatusDone;
     }
 
     virtual void output(int level, uint32_t flags) {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 31291b1..710f12f 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,6 +21,7 @@
 #include <private/hwui/DrawGlInfo.h>
 
 #include "DisplayList.h"
+#include "DeferredDisplayList.h"
 #include "DisplayListLogBuffer.h"
 #include "DisplayListOp.h"
 #include "DisplayListRenderer.h"
@@ -239,7 +240,7 @@
 }
 
 status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
-        Rect& dirty, int32_t flags, uint32_t level) {
+        Rect& dirty, int32_t flags) {
     // dirty is an out parameter and should not be recorded,
     // it matters only when replaying the display list
 
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 97a2508..bff3b97 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -47,15 +47,12 @@
 // Display list
 ///////////////////////////////////////////////////////////////////////////////
 
+class DeferredDisplayList;
 class DisplayListRenderer;
 class DisplayListOp;
 class DrawOp;
 class StateOp;
 
-///////////////////////////////////////////////////////////////////////////////
-// Renderer
-///////////////////////////////////////////////////////////////////////////////
-
 /**
  * Records drawing commands in a display list for latter playback.
  */
@@ -98,8 +95,7 @@
     virtual bool clipPath(SkPath* path, SkRegion::Op op);
     virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
 
-    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
-            uint32_t level = 0);
+    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags);
     virtual status_t drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 57d16ae..d11558f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -32,6 +32,7 @@
 #include <ui/Rect.h>
 
 #include "OpenGLRenderer.h"
+#include "DeferredDisplayList.h"
 #include "DisplayListRenderer.h"
 #include "PathTessellator.h"
 #include "Properties.h"
@@ -111,16 +112,18 @@
 
 OpenGLRenderer::OpenGLRenderer():
         mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
-    mShader = NULL;
-    mColorFilter = NULL;
-    mHasShadow = false;
-    mHasDrawFilter = false;
+    mDrawModifiers.mShader = NULL;
+    mDrawModifiers.mColorFilter = NULL;
+    mDrawModifiers.mHasShadow = false;
+    mDrawModifiers.mHasDrawFilter = false;
 
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
     mFirstSnapshot = new Snapshot;
 
     mScissorOptimizationDisabled = false;
+    mDrawDeferDisabled = false;
+    mDrawReorderDisabled = false;
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -137,6 +140,20 @@
     } else {
         INIT_LOGD("  Scissor optimization enabled");
     }
+
+    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
+        mDrawDeferDisabled = !strcasecmp(property, "true");
+        INIT_LOGD("  Draw defer %s", mDrawDeferDisabled ? "disabled" : "enabled");
+    } else {
+        INIT_LOGD("  Draw defer enabled");
+    }
+
+    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
+        mDrawReorderDisabled = !strcasecmp(property, "true");
+        INIT_LOGD("  Draw reorder %s", mDrawReorderDisabled ? "disabled" : "enabled");
+    } else {
+        INIT_LOGD("  Draw reorder enabled");
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -770,7 +787,7 @@
     layer->layer.set(bounds);
     layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
             bounds.getWidth() / float(layer->getWidth()), 0.0f);
-    layer->setColorFilter(mColorFilter);
+    layer->setColorFilter(mDrawModifiers.mColorFilter);
     layer->setBlend(true);
     layer->setDirty(false);
 
@@ -1204,6 +1221,40 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// State Deferral
+///////////////////////////////////////////////////////////////////////////////
+
+bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state) {
+    const Rect& currentClip = *(mSnapshot->clipRect);
+    const mat4& currentMatrix = *(mSnapshot->transform);
+
+    // state only has bounds initialized in local coordinates
+    if (!state.mBounds.isEmpty()) {
+        currentMatrix.mapRect(state.mBounds);
+        if (!state.mBounds.intersect(currentClip)) {
+            // quick rejected
+            return true;
+        }
+    }
+
+    state.mClip.set(currentClip);
+    state.mMatrix.load(currentMatrix);
+    state.mDrawModifiers = mDrawModifiers;
+    return false;
+}
+
+void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) {
+    mSnapshot->transform->load(state.mMatrix);
+
+    // NOTE: a clip RECT will be saved and restored, but DeferredDisplayState doesn't support
+    // complex clips. In the future, we should add support for deferral of operations clipped by
+    // these. for now, we don't defer with complex clips (see OpenGLRenderer::disallowDeferral())
+    mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
+    dirtyClip();
+    mDrawModifiers = state.mDrawModifiers;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Transforms
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1452,7 +1503,7 @@
     if (clear) clearLayerRegions();
     // Make sure setScissor & setStencil happen at the beginning of
     // this method
-    if (mDirtyClip) {
+    if (mDirtyClip && mCaches.scissorEnabled) {
         setScissorFromClip();
         setStencilFromClip();
     }
@@ -1524,14 +1575,14 @@
 }
 
 void OpenGLRenderer::setupDrawShader() {
-    if (mShader) {
-        mShader->describe(mDescription, mExtensions);
+    if (mDrawModifiers.mShader) {
+        mDrawModifiers.mShader->describe(mDescription, mExtensions);
     }
 }
 
 void OpenGLRenderer::setupDrawColorFilter() {
-    if (mColorFilter) {
-        mColorFilter->describe(mDescription, mExtensions);
+    if (mDrawModifiers.mColorFilter) {
+        mDrawModifiers.mColorFilter->describe(mDescription, mExtensions);
     }
 }
 
@@ -1547,16 +1598,19 @@
     // When the blending mode is kClear_Mode, we need to use a modulate color
     // argb=1,0,0,0
     accountForClear(mode);
-    chooseBlending((mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode,
-            mDescription, swapSrcDst);
+    bool blend = (mColorSet && mColorA < 1.0f) ||
+            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend());
+    chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
 void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) {
     // When the blending mode is kClear_Mode, we need to use a modulate color
     // argb=1,0,0,0
     accountForClear(mode);
-    chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()) ||
-            (mColorFilter && mColorFilter->blend()), mode, mDescription, swapSrcDst);
+    blend |= (mColorSet && mColorA < 1.0f) ||
+            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
+            (mDrawModifiers.mColorFilter && mDrawModifiers.mColorFilter->blend());
+    chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
 void OpenGLRenderer::setupDrawProgram() {
@@ -1609,7 +1663,7 @@
 }
 
 void OpenGLRenderer::setupDrawColorUniforms() {
-    if ((mColorSet && !mShader) || (mShader && mSetShaderColor)) {
+    if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
         mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
@@ -1621,23 +1675,25 @@
 }
 
 void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
-    if (mShader) {
+    if (mDrawModifiers.mShader) {
         if (ignoreTransform) {
             mModelView.loadInverse(*mSnapshot->transform);
         }
-        mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &mTextureUnit);
+        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
+                mModelView, *mSnapshot, &mTextureUnit);
     }
 }
 
 void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
-    if (mShader) {
-        mShader->setupProgram(mCaches.currentProgram, mIdentity, *mSnapshot, &mTextureUnit);
+    if (mDrawModifiers.mShader) {
+        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
+                mIdentity, *mSnapshot, &mTextureUnit);
     }
 }
 
 void OpenGLRenderer::setupDrawColorFilterUniforms() {
-    if (mColorFilter) {
-        mColorFilter->setupProgram(mCaches.currentProgram);
+    if (mDrawModifiers.mColorFilter) {
+        mDrawModifiers.mColorFilter->setupProgram(mCaches.currentProgram);
     }
 }
 
@@ -1726,21 +1782,24 @@
 // Drawing
 ///////////////////////////////////////////////////////////////////////////////
 
-status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList,
-        Rect& dirty, int32_t flags, uint32_t level) {
-
+status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags) {
     // 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, flags, level);
+        if (CC_UNLIKELY(mDrawDeferDisabled)) {
+            return displayList->replay(*this, dirty, flags, 0);
+        }
+
+        DeferredDisplayList deferredList;
+        return displayList->replay(*this, dirty, flags, 0, &deferredList);
     }
 
     return DrawGlInfo::kStatusDone;
 }
 
-void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) {
+void OpenGLRenderer::outputDisplayList(DisplayList* displayList) {
     if (displayList) {
-        displayList->output(level);
+        displayList->output(0);
     }
 }
 
@@ -1990,7 +2049,7 @@
     // Apply a scale transform on the canvas only when a shader is in use
     // Skia handles the ratio between the dst and src rects as a scale factor
     // when a shader is set
-    bool useScaleTransform = mShader && scaled;
+    bool useScaleTransform = mDrawModifiers.mShader && scaled;
     bool ignoreTransform = false;
 
     if (CC_LIKELY(mSnapshot->transform->isPureTranslate() && !useScaleTransform)) {
@@ -2445,15 +2504,15 @@
     //       if shader-based correction is enabled
     mCaches.dropShadowCache.setFontRenderer(fontRenderer);
     const ShadowTexture* shadow = mCaches.dropShadowCache.get(
-            paint, text, bytesCount, count, mShadowRadius, positions);
+            paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions);
     const AutoTexture autoCleanup(shadow);
 
-    const float sx = x - shadow->left + mShadowDx;
-    const float sy = y - shadow->top + mShadowDy;
+    const float sx = x - shadow->left + mDrawModifiers.mShadowDx;
+    const float sy = y - shadow->top + mDrawModifiers.mShadowDy;
 
-    const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
-    int shadowColor = mShadowColor;
-    if (mShader) {
+    const int shadowAlpha = ((mDrawModifiers.mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
+    int shadowColor = mDrawModifiers.mShadowColor;
+    if (mDrawModifiers.mShader) {
         shadowColor = 0xffffffff;
     }
 
@@ -2501,7 +2560,7 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    if (CC_UNLIKELY(mHasShadow)) {
+    if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
                 alpha, mode, 0.0f, 0.0f);
     }
@@ -2592,7 +2651,7 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    if (CC_UNLIKELY(mHasShadow)) {
+    if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
                 oldX, oldY);
     }
@@ -2748,8 +2807,8 @@
     mCaches.activeTexture(0);
 
     if (CC_LIKELY(!layer->region.isEmpty())) {
-        SkiaColorFilter* oldFilter = mColorFilter;
-        mColorFilter = layer->getColorFilter();
+        SkiaColorFilter* oldFilter = mDrawModifiers.mColorFilter;
+        mDrawModifiers.mColorFilter = layer->getColorFilter();
 
         if (layer->region.isRect()) {
             composeLayerRect(layer, layer->regionRect);
@@ -2788,7 +2847,7 @@
 #endif
         }
 
-        mColorFilter = oldFilter;
+        mDrawModifiers.mColorFilter = oldFilter;
 
         if (layer->debugDrawUpdate) {
             layer->debugDrawUpdate = false;
@@ -2809,13 +2868,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetShader() {
-    mShader = NULL;
+    mDrawModifiers.mShader = NULL;
 }
 
 void OpenGLRenderer::setupShader(SkiaShader* shader) {
-    mShader = shader;
-    if (mShader) {
-        mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
+    mDrawModifiers.mShader = shader;
+    if (mDrawModifiers.mShader) {
+        mDrawModifiers.mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
     }
 }
 
@@ -2824,11 +2883,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetColorFilter() {
-    mColorFilter = NULL;
+    mDrawModifiers.mColorFilter = NULL;
 }
 
 void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
-    mColorFilter = filter;
+    mDrawModifiers.mColorFilter = filter;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2836,15 +2895,15 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetShadow() {
-    mHasShadow = false;
+    mDrawModifiers.mHasShadow = false;
 }
 
 void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
-    mHasShadow = true;
-    mShadowRadius = radius;
-    mShadowDx = dx;
-    mShadowDy = dy;
-    mShadowColor = color;
+    mDrawModifiers.mHasShadow = true;
+    mDrawModifiers.mShadowRadius = radius;
+    mDrawModifiers.mShadowDx = dx;
+    mDrawModifiers.mShadowDy = dy;
+    mDrawModifiers.mShadowColor = color;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2852,22 +2911,23 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetPaintFilter() {
-    mHasDrawFilter = false;
+    mDrawModifiers.mHasDrawFilter = false;
 }
 
 void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
-    mHasDrawFilter = true;
-    mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
-    mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
+    mDrawModifiers.mHasDrawFilter = true;
+    mDrawModifiers.mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
+    mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
 }
 
 SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
-    if (CC_LIKELY(!mHasDrawFilter || !paint)) return paint;
+    if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) return paint;
 
     uint32_t flags = paint->getFlags();
 
     mFilteredPaint = *paint;
-    mFilteredPaint.setFlags((flags & ~mPaintFilterClearBits) | mPaintFilterSetBits);
+    mFilteredPaint.setFlags((flags & ~mDrawModifiers.mPaintFilterClearBits) |
+            mDrawModifiers.mPaintFilterSetBits);
 
     return &mFilteredPaint;
 }
@@ -2967,7 +3027,7 @@
 
     int color = paint->getColor();
     // If a shader is set, preserve only the alpha
-    if (mShader) {
+    if (mDrawModifiers.mShader) {
         color |= 0x00ffffff;
     }
     SkXfermode::Mode mode = getXfermode(paint->getXfermode());
@@ -3040,7 +3100,7 @@
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         int color, SkXfermode::Mode mode, bool ignoreTransform) {
     // If a shader is set, preserve only the alpha
-    if (mShader) {
+    if (mDrawModifiers.mShader) {
         color |= 0x00ffffff;
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index ad80d36..e12083c 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -48,6 +48,34 @@
 namespace android {
 namespace uirenderer {
 
+struct DrawModifiers {
+    SkiaShader* mShader;
+    SkiaColorFilter* mColorFilter;
+
+    // Drop shadow
+    bool mHasShadow;
+    float mShadowRadius;
+    float mShadowDx;
+    float mShadowDy;
+    int mShadowColor;
+
+    // Draw filters
+    bool mHasDrawFilter;
+    int mPaintFilterClearBits;
+    int mPaintFilterSetBits;
+};
+
+struct DeferredDisplayState {
+    Rect mBounds; // local bounds, mapped with matrix to be in screen space coordinates, clipped.
+    int mMultipliedAlpha; // -1 if invalid (because caching not set)
+
+    // the below are set and used by the OpenGLRenderer at record and deferred playback
+    Rect mClip;
+    mat4 mMatrix;
+    SkiaShader* mShader;
+    DrawModifiers mDrawModifiers;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Renderer
 ///////////////////////////////////////////////////////////////////////////////
@@ -182,9 +210,8 @@
     virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
     virtual Rect* getClipRect();
 
-    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
-            uint32_t level = 0);
-    virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0);
+    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags);
+    virtual void outputDisplayList(DisplayList* displayList);
     virtual status_t drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
@@ -233,6 +260,23 @@
 
     SkPaint* filterPaint(SkPaint* paint);
 
+    bool disallowDeferral() {
+        // returns true if the OpenGLRenderer's state can be completely represented by
+        // a DeferredDisplayState object
+        return !mSnapshot->clipRegion->isEmpty() ||
+                mSnapshot->alpha < 1.0 ||
+                (mSnapshot->flags & Snapshot::kFlagIsLayer) ||
+                (mSnapshot->flags & Snapshot::kFlagFboTarget); // ensure we're not in a layer
+    }
+
+    bool disallowReorder() { return mDrawReorderDisabled; }
+
+    bool storeDisplayState(DeferredDisplayState& state);
+    void restoreDisplayState(const DeferredDisplayState& state);
+
+    const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
+    void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
+
     ANDROID_API bool isCurrentTransformSimple() {
         return mSnapshot->transform->isSimple();
     }
@@ -868,26 +912,11 @@
     // State used to define the clipping region
     sp<Snapshot> mTilingSnapshot;
 
-    // Shaders
-    SkiaShader* mShader;
-
-    // Color filters
-    SkiaColorFilter* mColorFilter;
-
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
 
-    // Drop shadow
-    bool mHasShadow;
-    float mShadowRadius;
-    float mShadowDx;
-    float mShadowDy;
-    int mShadowColor;
-
-    // Draw filters
-    bool mHasDrawFilter;
-    int mPaintFilterClearBits;
-    int mPaintFilterSetBits;
+    // shader, filters, and shadow
+    DrawModifiers mDrawModifiers;
     SkPaint mFilteredPaint;
 
     // Various caches
@@ -925,6 +954,8 @@
     // See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in
     // Properties.h
     bool mScissorOptimizationDisabled;
+    bool mDrawDeferDisabled;
+    bool mDrawReorderDisabled;
 
     // No-ops start/endTiling when set
     bool mSuppressTiling;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 0c75242..f3957cf 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -82,6 +82,19 @@
  */
 #define PROPERTY_DISABLE_SCISSOR_OPTIMIZATION "ro.hwui.disable_scissor_opt"
 
+/**
+ * Disables draw operation deferral if set to "true", forcing draw
+ * commands to be issued to OpenGL in order, and processed in sequence
+ * with state-manipulation canvas commands.
+ */
+#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
+
+/**
+ * Used to disable draw operation reordering when deferring draw operations
+ * Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
+ */
+#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
+
 // These properties are defined in mega-bytes
 #define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
 #define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 5f4bb5a..f50ac3c 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -159,6 +159,13 @@
         bottom += dy;
     }
 
+    void outset(float delta) {
+        left -= delta;
+        top -= delta;
+        right += delta;
+        bottom += delta;
+    }
+
     void snapToPixelBoundaries() {
         left = floorf(left + 0.5f);
         top = floorf(top + 0.5f);