peephole optimize save/restore brackets that contain no draw calls.
Review URL: https://codereview.appspot.com/6443149

git-svn-id: http://skia.googlecode.com/svn/trunk@5347 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index afa2deb..b4c4bbc 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -57,24 +57,28 @@
 }
 
 int SkPictureRecord::save(SaveFlags flags) {
+    // record the offset to us, making it non-positive to distinguish a save
+    // from a clip entry.
+    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
+    
     addDraw(SAVE);
     addInt(flags);
 
-    fRestoreOffsetStack.push(0);
-
     validate();
     return this->INHERITED::save(flags);
 }
 
 int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
                                SaveFlags flags) {
+    // record the offset to us, making it non-positive to distinguish a save
+    // from a clip entry.
+    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
+    
     addDraw(SAVE_LAYER);
     addRectPtr(bounds);
     addPaintPtr(paint);
     addInt(flags);
 
-    fRestoreOffsetStack.push(0);
-
     if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
         fFirstSavedLayerIndex = fRestoreOffsetStack.count();
     }
@@ -95,6 +99,108 @@
     return fFirstSavedLayerIndex != kNoSavedLayerIndex;
 }
 
+// Return the size of the specified drawType's recorded block, or 0 if this verb
+// is variable sized, and therefore not known.
+static inline uint32_t getSkipableSize(unsigned drawType) {
+    static const uint8_t gSizes[LAST_DRAWTYPE_ENUM + 1] = {
+        0,  // UNUSED,
+        4,  // CLIP_PATH,
+        4,  // CLIP_REGION,
+        7,  // CLIP_RECT,
+        2,  // CONCAT,
+        0,  // DRAW_BITMAP,
+        0,  // DRAW_BITMAP_MATRIX,
+        0,  // DRAW_BITMAP_NINE,
+        0,  // DRAW_BITMAP_RECT,
+        0,  // DRAW_CLEAR,
+        0,  // DRAW_DATA,
+        0,  // DRAW_PAINT,
+        0,  // DRAW_PATH,
+        0,  // DRAW_PICTURE,
+        0,  // DRAW_POINTS,
+        0,  // DRAW_POS_TEXT,
+        0,  // DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
+        0,  // DRAW_POS_TEXT_H,
+        0,  // DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
+        0,  // DRAW_RECT,
+        0,  // DRAW_SPRITE,
+        0,  // DRAW_TEXT,
+        0,  // DRAW_TEXT_ON_PATH,
+        0,  // DRAW_TEXT_TOP_BOTTOM,   // fast variant of DRAW_TEXT
+        0,  // DRAW_VERTICES,
+        0,  // RESTORE,
+        2,  // ROTATE,
+        2,  // SAVE,
+        0,  // SAVE_LAYER,
+        3,  // SCALE,
+        2,  // SET_MATRIX,
+        3,  // SKEW,
+        3,  // TRANSLATE,
+    };
+
+    SkASSERT(sizeof(gSizes) == LAST_DRAWTYPE_ENUM + 1);
+    SkASSERT((unsigned)drawType <= (unsigned)LAST_DRAWTYPE_ENUM);
+    return gSizes[drawType] * sizeof(uint32_t);
+}
+
+#ifdef TRACK_COLLAPSE_STATS
+    static int gCollapseCount, gCollapseCalls;
+#endif
+
+/*
+ *  Restore has just been called (but not recoreded), so look back at the
+ *  matching save(), and see if we can eliminate the pair of them, due to no
+ *  intervening matrix/clip calls.
+ *
+ *  If so, update the writer and return true, in which case we won't even record
+ *  the restore() call. If we still need the restore(), return false.
+ */
+static bool collapseSaveClipRestore(SkWriter32* writer, int32_t offset) {
+#ifdef TRACK_COLLAPSE_STATS
+    gCollapseCalls += 1;
+#endif
+
+    int32_t restoreOffset = (int32_t)writer->size();
+
+    // back up to the save block
+    while (offset > 0) {
+        offset = *writer->peek32(offset);
+    }
+
+    // now offset points to a save
+    offset = -offset;
+    if (SAVE_LAYER == *writer->peek32(offset)) {
+        // not ready to cull these out yet (mrr)
+        return false;
+    }
+    SkASSERT(SAVE == *writer->peek32(offset));
+
+    // Walk forward until we get back to either a draw-verb (abort) or we hit
+    // our restore (success).
+    int32_t saveOffset = offset;
+
+    offset += getSkipableSize(SAVE);
+    while (offset < restoreOffset) {
+        uint32_t* block = writer->peek32(offset);
+        uint32_t op = *block;
+        uint32_t opSize = getSkipableSize(op);
+        if (0 == opSize) {
+            // drawing verb, abort
+            return false;
+        }
+        offset += opSize;
+    }
+    
+#ifdef TRACK_COLLAPSE_STATS
+    gCollapseCount += 1;
+    SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
+             (double)gCollapseCount / gCollapseCalls, "%");
+#endif
+
+    writer->rewindToOffset(saveOffset);
+    return true;
+}
+
 void SkPictureRecord::restore() {
     // FIXME: SkDeferredCanvas needs to be refactored to respect
     // save/restore balancing so that the following test can be
@@ -108,16 +214,17 @@
         return;
     }
 
-    fillRestoreOffsetPlaceholdersForCurrentStackLevel(
-        (uint32_t)fWriter.size());
-
     if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
         fFirstSavedLayerIndex = kNoSavedLayerIndex;
     }
 
+    if (!collapseSaveClipRestore(&fWriter, fRestoreOffsetStack.top())) {
+        fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
+        this->addDraw(RESTORE);
+    }
+
     fRestoreOffsetStack.pop();
 
-    addDraw(RESTORE);
     validate();
     return this->INHERITED::restore();
 }
@@ -187,12 +294,18 @@
 
 void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(
     uint32_t restoreOffset) {
-    uint32_t offset = fRestoreOffsetStack.top();
-    while (offset) {
+    int32_t offset = fRestoreOffsetStack.top();
+    while (offset > 0) {
         uint32_t* peek = fWriter.peek32(offset);
         offset = *peek;
         *peek = restoreOffset;
     }
+    
+#ifdef SK_DEBUG
+    // assert that the final offset value points to a save verb
+    uint32_t drawOp = *fWriter.peek32(-offset);
+    SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
+#endif
 }
 
 void SkPictureRecord::endRecording() {