SkTileGrid: store insertion order, return results sorted by that.

This removes the need to assume the void* are SkPictureStateTree::Data*.

BUG=skia:
R=robertphillips@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/466503002
diff --git a/dm/DMCpuGMTask.cpp b/dm/DMCpuGMTask.cpp
index ceadc26..d39252a 100644
--- a/dm/DMCpuGMTask.cpp
+++ b/dm/DMCpuGMTask.cpp
@@ -46,9 +46,7 @@
     SPAWN(QuiltTask, fGMFactory(NULL), bm, QuiltTask::kNone_BBH,     QuiltTask::kSkRecord_Backend);
     SPAWN(QuiltTask, fGMFactory(NULL), bm, QuiltTask::kRTree_BBH,    QuiltTask::kSkRecord_Backend);
     SPAWN(QuiltTask, fGMFactory(NULL), bm, QuiltTask::kQuadTree_BBH, QuiltTask::kSkRecord_Backend);
-    /*  TODO: Failing, not sure why.  Enable these when passing.
     SPAWN(QuiltTask, fGMFactory(NULL), bm, QuiltTask::kTileGrid_BBH, QuiltTask::kSkRecord_Backend);
-    */
 
     SPAWN(SerializeTask, fGMFactory(NULL), bm, SerializeTask::kNormal_Mode);
     SPAWN(SerializeTask, fGMFactory(NULL), bm, SerializeTask::kSkRecord_Mode);
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index e075074..0e2a470 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -27,6 +27,7 @@
         SkASSERT(ops.count() == SkToInt(record.count()));
 
         // FIXME: QuadTree doesn't send these back in the order we inserted them.  :(
+        // Also remove the sort in SkPictureData::getActiveOps()?
         if (ops.count() > 0) {
             SkTQSort(ops.begin(), ops.end() - 1, SkTCompareLT<void*>());
         }
diff --git a/src/core/SkTileGrid.cpp b/src/core/SkTileGrid.cpp
index 21788c1..17102f5 100644
--- a/src/core/SkTileGrid.cpp
+++ b/src/core/SkTileGrid.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2012 Google Inc.
  *
@@ -7,65 +6,55 @@
  */
 
 #include "SkTileGrid.h"
-#include "SkPictureStateTree.h"
 
-SkTileGrid::SkTileGrid(int xTileCount, int yTileCount, const SkTileGridFactory::TileGridInfo& info) {
-    fXTileCount = xTileCount;
-    fYTileCount = yTileCount;
-    fInfo = info;
+SkTileGrid::SkTileGrid(int xTiles, int yTiles, const SkTileGridFactory::TileGridInfo& info)
+    : fXTiles(xTiles)
+    , fYTiles(yTiles)
+    , fInfo(info)
+    , fCount(0)
+    , fTiles(SkNEW_ARRAY(SkTDArray<Entry>, xTiles * yTiles)) {
     // Margin is offset by 1 as a provision for AA and
     // to cancel-out the outset applied by getClipDeviceBounds.
     fInfo.fMargin.fHeight++;
     fInfo.fMargin.fWidth++;
-    fTileCount = fXTileCount * fYTileCount;
-    fInsertionCount = 0;
-    fGridBounds = SkIRect::MakeXYWH(0, 0, fInfo.fTileInterval.width() * fXTileCount,
-        fInfo.fTileInterval.height() * fYTileCount);
-    fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount);
 }
 
 SkTileGrid::~SkTileGrid() {
-    SkDELETE_ARRAY(fTileData);
-}
-
-int SkTileGrid::tileCount(int x, int y) {
-    return this->tile(x, y).count();
-}
-
-const SkTDArray<void *>& SkTileGrid::tile(int x, int y) const {
-    return fTileData[y * fXTileCount + x];
-}
-
-SkTDArray<void *>& SkTileGrid::tile(int x, int y) {
-    return fTileData[y * fXTileCount + x];
+    SkDELETE_ARRAY(fTiles);
 }
 
 void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) {
     SkASSERT(!bounds.isEmpty());
     SkIRect dilatedBounds = bounds;
-    dilatedBounds.outset(fInfo.fMargin.width(), fInfo.fMargin.height());
-    dilatedBounds.offset(fInfo.fOffset);
-    if (!SkIRect::Intersects(dilatedBounds, fGridBounds)) {
+
+    // Dilating the largest SkIRect will overflow.  Other nearly-largest rects may overflow too,
+    // but we don't make active use of them like we do the largest.
+    if (!bounds.isLargest()) {
+        dilatedBounds.outset(fInfo.fMargin.width(), fInfo.fMargin.height());
+        dilatedBounds.offset(fInfo.fOffset);
+    }
+
+    const SkIRect gridBounds =
+        { 0, 0, fInfo.fTileInterval.width() * fXTiles, fInfo.fTileInterval.height() * fYTiles };
+    if (!SkIRect::Intersects(dilatedBounds, gridBounds)) {
         return;
     }
 
     // Note: SkIRects are non-inclusive of the right() column and bottom() row,
-    // hence the "-1"s in the computations of maxTileX and maxTileY.
-    int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fInfo.fTileInterval.width(),
-        fXTileCount - 1), 0);
-    int maxTileX = SkMax32(SkMin32((dilatedBounds.right() - 1) / fInfo.fTileInterval.width(),
-        fXTileCount - 1), 0);
-    int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fInfo.fTileInterval.height(),
-        fYTileCount -1), 0);
-    int maxTileY = SkMax32(SkMin32((dilatedBounds.bottom() -1) / fInfo.fTileInterval.height(),
-        fYTileCount -1), 0);
+    // hence the "-1"s in the computations of maxX and maxY.
+    int minX = SkMax32(0, SkMin32(dilatedBounds.left() / fInfo.fTileInterval.width(), fXTiles - 1));
+    int minY = SkMax32(0, SkMin32(dilatedBounds.top() / fInfo.fTileInterval.height(), fYTiles - 1));
+    int maxX = SkMax32(0, SkMin32((dilatedBounds.right()  - 1) / fInfo.fTileInterval.width(),
+                                  fXTiles - 1));
+    int maxY = SkMax32(0, SkMin32((dilatedBounds.bottom() - 1) / fInfo.fTileInterval.height(),
+                                  fYTiles - 1));
 
-    for (int x = minTileX; x <= maxTileX; x++) {
-        for (int y = minTileY; y <= maxTileY; y++) {
-            this->tile(x, y).push(data);
+    Entry entry = { fCount++, data };
+    for (int x = minX; x <= maxX; x++) {
+        for (int y = minY; y <= maxY; y++) {
+            fTiles[y * fXTiles + x].push(entry);
         }
     }
-    fInsertionCount++;
 }
 
 static int divide_ceil(int x, int y) {
@@ -96,35 +85,38 @@
     int endX = divide_ceil(adjusted.right(),  fInfo.fTileInterval.width()),
         endY = divide_ceil(adjusted.bottom(), fInfo.fTileInterval.height());
 
-    // Logically, we could pin endX to [startX, fXTileCount], but we force it
-    // up to (startX, fXTileCount] to make sure we hit at least one tile.
+    // Logically, we could pin endX to [startX, fXTiles], but we force it
+    // up to (startX, fXTiles] to make sure we hit at least one tile.
     // This snaps just-out-of-bounds queries to the neighboring border tile.
     // I don't know if this is an important feature outside of unit tests.
-    startX = SkPin32(startX, 0, fXTileCount - 1);
-    startY = SkPin32(startY, 0, fYTileCount - 1);
-    endX   = SkPin32(endX, startX + 1, fXTileCount);
-    endY   = SkPin32(endY, startY + 1, fYTileCount);
+    startX = SkPin32(startX, 0, fXTiles - 1);
+    startY = SkPin32(startY, 0, fYTiles - 1);
+    endX   = SkPin32(endX, startX + 1, fXTiles);
+    endY   = SkPin32(endY, startY + 1, fYTiles);
 
     const int tilesHit = (endX - startX) * (endY - startY);
     SkASSERT(tilesHit > 0);
 
     if (tilesHit == 1) {
         // A performance shortcut.  The merging code below would work fine here too.
-        *results = this->tile(startX, startY);
+        const SkTDArray<Entry>& tile = fTiles[startY * fXTiles + startX];
+        results->setCount(tile.count());
+        for (int i = 0; i < tile.count(); i++) {
+            (*results)[i] = tile[i].data;
+        }
         return;
     }
 
     // We've got to merge the data in many tiles into a single sorted and deduplicated stream.
-    // Each tile itself is already sorted (TODO: assert this while building) so we just need to do
-    // a simple k-way merge.
+    // We do a simple k-way merge based on the order the data was inserted.
 
     // Gather pointers to the starts and ends of the tiles to merge.
-    SkAutoSTArray<kStackAllocationTileCount, void**> tiles(tilesHit), ends(tilesHit);
+    SkAutoSTArray<kStackAllocationTileCount, const Entry*> starts(tilesHit), ends(tilesHit);
     int i = 0;
     for (int x = startX; x < endX; x++) {
         for (int y = startY; y < endY; y++) {
-            tiles[i] = fTileData[y * fXTileCount + x].begin();
-            ends[i]  = fTileData[y * fXTileCount + x].end();
+            starts[i] = fTiles[y * fXTiles + x].begin();
+            ends[i]  = fTiles[y * fXTiles + x].end();
             i++;
         }
     }
@@ -132,49 +124,43 @@
     // Merge tiles into results until they're fully consumed.
     results->reset();
     while (true) {
-        // The tiles themselves are already sorted, so the smallest datum is the front of some tile.
+        // The tiles themselves are already ordered, so the earliest is at the front of some tile.
         // It may be at the front of several, even all, tiles.
-        SkPictureStateTree::Draw* smallest = NULL;
-        for (int i = 0; i < tiles.count(); i++) {
-            if (tiles[i] < ends[i]) {
-                SkPictureStateTree::Draw* candidate =
-                    static_cast<SkPictureStateTree::Draw*>(*tiles[i]);
-                if (NULL == smallest || (*candidate) < (*smallest)) {
-                    smallest = candidate;
+        const Entry* earliest = NULL;
+        for (int i = 0; i < starts.count(); i++) {
+            if (starts[i] < ends[i]) {
+                if (NULL == earliest || starts[i]->order < earliest->order) {
+                    earliest = starts[i];
                 }
             }
         }
 
-        // If we didn't find a smallest datum, there's nothing left to merge.
-        if (NULL == smallest) {
+        // If we didn't find an earliest entry, there isn't anything left to merge.
+        if (NULL == earliest) {
             return;
         }
 
-        // We did find a smallest datum. Output it, and step forward in every tile that contains it.
-        results->push(smallest);
-        for (int i = 0; i < tiles.count(); i++) {
-            if (tiles[i] < ends[i] && *tiles[i] == smallest) {
-                tiles[i]++;
+        // We did find an earliest entry. Output it, and step forward every tile that contains it.
+        results->push(earliest->data);
+        for (int i = 0; i < starts.count(); i++) {
+            if (starts[i] < ends[i] && starts[i]->order == earliest->order) {
+                starts[i]++;
             }
         }
     }
 }
 
 void SkTileGrid::clear() {
-    for (int i = 0; i < fTileCount; i++) {
-        fTileData[i].reset();
+    for (int i = 0; i < fXTiles * fYTiles; i++) {
+        fTiles[i].reset();
     }
 }
 
-int SkTileGrid::getCount() const {
-    return fInsertionCount;
-}
-
 void SkTileGrid::rewindInserts() {
     SkASSERT(fClient);
-    for (int i = 0; i < fTileCount; ++i) {
-        while (!fTileData[i].isEmpty() && fClient->shouldRewind(fTileData[i].top())) {
-            fTileData[i].pop();
+    for (int i = 0; i < fXTiles * fYTiles; i++) {
+        while (!fTiles[i].isEmpty() && fClient->shouldRewind(fTiles[i].top().data)) {
+            fTiles[i].pop();
         }
     }
 }
diff --git a/src/core/SkTileGrid.h b/src/core/SkTileGrid.h
index c02235a..4146331 100644
--- a/src/core/SkTileGrid.h
+++ b/src/core/SkTileGrid.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2012 Google Inc.
  *
@@ -11,27 +10,21 @@
 
 #include "SkBBHFactory.h"
 #include "SkBBoxHierarchy.h"
-#include "SkPictureStateTree.h"
 
 /**
  * Subclass of SkBBoxHierarchy that stores elements in buckets that correspond
  * to tile regions, disposed in a regular grid.  This is useful when the tile
  * structure that will be use in search() calls is known prior to insertion.
- * Calls to search will return in constant time.
- *
- * Note: Current implementation of search() only supports looking-up regions
- * that are an exact match to a single tile.  Implementation could be augmented
- * to support arbitrary rectangles, but performance would be sub-optimal.
  */
 class SkTileGrid : public SkBBoxHierarchy {
 public:
-    SkTileGrid(int xTileCount, int yTileCount, const SkTileGridFactory::TileGridInfo& info);
+    SkTileGrid(int xTiles, int yTiles, const SkTileGridFactory::TileGridInfo& info);
 
     virtual ~SkTileGrid();
 
     /**
      * Insert a data pointer and corresponding bounding box
-     * @param data   The data pointer, may _NOT_ be NULL.
+     * @param data   An arbitrary data pointer, may be NULL.
      * @param bounds The bounding box, should not be empty.
      * @param defer  Ignored; SkTileGrid does not defer insertions.
      */
@@ -47,26 +40,27 @@
 
     virtual void clear() SK_OVERRIDE;
 
-    /**
-     * Gets the number of insertions
-     */
-    virtual int getCount() const SK_OVERRIDE;
+    virtual int getCount() const SK_OVERRIDE { return fCount; }
 
     virtual int getDepth() const SK_OVERRIDE { return -1; }
 
     virtual void rewindInserts() SK_OVERRIDE;
 
-    int tileCount(int x, int y);  // For testing only.
+    // For testing.
+    int tileCount(int x, int y) { return fTiles[y * fXTiles + x].count(); }
 
 private:
-    const SkTDArray<void*>& tile(int x, int y) const;
-    SkTDArray<void*>& tile(int x, int y);
+    struct Entry {
+        size_t order;  // Insertion order.  Used to preserve order when merging multiple tiles.
+        void*  data;
+    };
 
-    int fXTileCount, fYTileCount, fTileCount;
+    const int fXTiles, fYTiles;
     SkTileGridFactory::TileGridInfo fInfo;
-    SkTDArray<void*>* fTileData;
-    int fInsertionCount;
-    SkIRect fGridBounds;
+    size_t fCount;
+
+    // (fXTiles * fYTiles) SkTDArrays, each listing data overlapping that tile in insertion order.
+    SkTDArray<Entry>* fTiles;
 
     typedef SkBBoxHierarchy INHERITED;
 };