Culling API

*** SKP format breaking change ***

Adding a couple of culling primitives: pushCull(SkRect) & popCull().

These are currently only plumbed for SKP playback quickreject.

At record time, we perform a couple of optimizations to trim down the
number of redundant culls:

  * collapse empty pushCull/popCull pairs
  * skip pushCull/popCull pairs nested within an identical cull rect

Things still missing/to consider:

  * use an inlineable, simplified quickreject (Mike's old prototype)
  * debugger visualization for cull boxes
  * BBH integration: the initial prototype had some minimal BBH support,
    but since the optimizations required expensive rewinds and culling
    is expected to be a BBH alternative, it got dropped.

R=bsalomon@google.com, reed@google.com, robertphillips@google.com, caryclark@google.com, tomhudson@google.com, iancottrell@google.com

Author: fmalita@chromium.org

Review URL: https://codereview.chromium.org/138013009

git-svn-id: http://skia.googlecode.com/svn/trunk@13611 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index be86951..978e2b3 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -112,6 +112,8 @@
         0,  // COMMENT - no paint
         0,  // END_GROUP - no paint
         1,  // DRAWDRRECT - right after op code
+        0,  // PUSH_CULL - no paint
+        0,  // POP_CULL - no paint
     };
 
     SK_COMPILE_ASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1,
@@ -220,6 +222,15 @@
 }
 
 /*
+ * Read the op code from 'offset' in 'writer'.
+ */
+#ifdef SK_DEBUG
+static DrawType peek_op(SkWriter32* writer, int32_t offset) {
+    return (DrawType)(writer->readTAt<uint32_t>(offset) >> 24);
+}
+#endif
+
+/*
  * Read the op code from 'offset' in 'writer' and extract the size too.
  */
 static DrawType peek_op_and_size(SkWriter32* writer, int32_t offset, uint32_t* size) {
@@ -1548,6 +1559,61 @@
     this->validate(initialOffset, size);
 }
 
+// [op/size] [rect] [skip offset]
+static const uint32_t kPushCullOpSize = 2 * kUInt32Size + sizeof(SkRect);
+void SkPictureRecord::onPushCull(const SkRect& cullRect) {
+    // Skip identical cull rects.
+    if (!fCullOffsetStack.isEmpty()) {
+        const SkRect& prevCull = fWriter.readTAt<SkRect>(fCullOffsetStack.top() - sizeof(SkRect));
+        if (prevCull == cullRect) {
+            // Skipped culls are tracked on the stack, but they point to the previous offset.
+            fCullOffsetStack.push(fCullOffsetStack.top());
+            return;
+        }
+
+        SkASSERT(prevCull.contains(cullRect));
+    }
+
+    uint32_t size = kPushCullOpSize;
+    size_t initialOffset = this->addDraw(PUSH_CULL, &size);
+    // PUSH_CULL's size should stay constant (used to rewind).
+    SkASSERT(size == kPushCullOpSize);
+
+    this->addRect(cullRect);
+    fCullOffsetStack.push(fWriter.bytesWritten());
+    this->addInt(0);
+    this->validate(initialOffset, size);
+}
+
+void SkPictureRecord::onPopCull() {
+    SkASSERT(!fCullOffsetStack.isEmpty());
+
+    uint32_t cullSkipOffset = fCullOffsetStack.top();
+    fCullOffsetStack.pop();
+
+    // Skipped push, do the same for pop.
+    if (!fCullOffsetStack.isEmpty() && cullSkipOffset == fCullOffsetStack.top()) {
+        return;
+    }
+
+    // Collapse empty push/pop pairs.
+    if ((size_t)(cullSkipOffset + kUInt32Size) == fWriter.bytesWritten()) {
+        SkASSERT(fWriter.bytesWritten() >= kPushCullOpSize);
+        SkASSERT(PUSH_CULL == peek_op(&fWriter, fWriter.bytesWritten() - kPushCullOpSize));
+        fWriter.rewindToOffset(fWriter.bytesWritten() - kPushCullOpSize);
+        return;
+    }
+
+    // op only
+    uint32_t size = kUInt32Size;
+    size_t initialOffset = this->addDraw(POP_CULL, &size);
+
+    // update the cull skip offset to point past this op.
+    fWriter.overwriteTAt<uint32_t>(cullSkipOffset, fWriter.bytesWritten());
+
+    this->validate(initialOffset, size);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkSurface* SkPictureRecord::onNewSurface(const SkImageInfo& info) {