Actually identify required saveLayers in SkGpuDevice::EXPERIMENTAL_drawPicture

NOTRY=true
R=bsalomon@google.com, rmistry@google.com

Author: robertphillips@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13993 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index e575aec..1ca9312 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2547,7 +2547,7 @@
     if (NULL != device) {
         // Canvas has to first give the device the opportunity to render
         // the picture itself.
-        if (device->EXPERIMENTAL_drawPicture(picture)) {
+        if (device->EXPERIMENTAL_drawPicture(&picture)) {
             return; // the device has rendered the entire picture
         }
     }
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 299d445..be295ce 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -175,7 +175,7 @@
     // The base class doesn't perform any analysis but derived classes may
 }
 
-bool SkBaseDevice::EXPERIMENTAL_drawPicture(const SkPicture& picture) {
+bool SkBaseDevice::EXPERIMENTAL_drawPicture(SkPicture* picture) {
     // The base class doesn't perform any accelerated picture rendering
     return false;
 }
diff --git a/src/gpu/GrClipData.cpp b/src/gpu/GrClipData.cpp
index 22b4371..40bdec8 100644
--- a/src/gpu/GrClipData.cpp
+++ b/src/gpu/GrClipData.cpp
@@ -18,15 +18,15 @@
  * the result of purely intersections of rects (with an initial replace)
  * isIntersectionOfRects will be set to true.
  */
-void GrClipData::getConservativeBounds(const GrSurface* surface,
+void GrClipData::getConservativeBounds(int width, int height,
                                        SkIRect* devResult,
                                        bool* isIntersectionOfRects) const {
     SkRect devBounds;
 
     fClipStack->getConservativeBounds(-fOrigin.fX,
                                       -fOrigin.fY,
-                                      surface->width(),
-                                      surface->height(),
+                                      width,
+                                      height,
                                       &devBounds,
                                       isIntersectionOfRects);
 
diff --git a/src/gpu/GrPictureUtils.cpp b/src/gpu/GrPictureUtils.cpp
new file mode 100644
index 0000000..20b8828
--- /dev/null
+++ b/src/gpu/GrPictureUtils.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrPictureUtils.h"
+#include "SkDevice.h"
+
+// The GrGather device performs GPU-backend-specific preprocessing on 
+// a picture. The results are stored in a GPUAccelData.
+//
+// Currently the only interesting work is done in drawDevice (i.e., when a
+// saveLayer is collapsed back into its parent) and, maybe, in onCreateDevice.
+// All the current work could be done much more efficiently by just traversing the
+// raw op codes in the SkPicture (although we would still need to replay all the
+// clip calls).
+class GrGatherDevice : public SkBaseDevice {
+public:
+    SK_DECLARE_INST_COUNT(GrGatherDevice)
+
+    GrGatherDevice(int width, int height, SkPicture* picture, GPUAccelData* accelData) {
+        fPicture = picture;
+        fInfo.fSize.set(width, height);
+        fInfo.fSaveLayerOpID = fPicture->EXPERIMENTAL_curOpID();
+        fInfo.fRestoreOpID = 0;
+        fInfo.fHasNestedLayers = false;
+
+        fEmptyBitmap.setConfig(SkImageInfo::Make(fInfo.fSize.fWidth, 
+                                                 fInfo.fSize.fHeight,
+                                                 kUnknown_SkColorType,
+                                                 kIgnore_SkAlphaType));
+        fAccelData = accelData;
+        fAlreadyDrawn = false;
+    }
+
+    virtual ~GrGatherDevice() { }
+
+    virtual int width() const SK_OVERRIDE { return fInfo.fSize.width(); }
+    virtual int height() const SK_OVERRIDE { return fInfo.fSize.height(); }
+    virtual bool isOpaque() const SK_OVERRIDE { return false; }
+    virtual SkBitmap::Config config() const SK_OVERRIDE {
+        return SkBitmap::kNo_Config;
+    }
+    virtual SkImageInfo imageInfo() const SK_OVERRIDE {
+        return fEmptyBitmap.info();
+    }
+
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+                             SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        NotSupported();
+    }
+#endif
+    virtual GrRenderTarget* accessRenderTarget() SK_OVERRIDE { return NULL; }
+
+protected:
+    virtual bool filterTextFlags(const SkPaint& paint, TextFlags*) SK_OVERRIDE {
+        return false;
+    }
+    virtual void clear(SkColor color) SK_OVERRIDE {
+        NothingToDo();
+    }
+    virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+                            const SkPoint points[], const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawRect(const SkDraw& draw, const SkRect& rect,
+                          const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawOval(const SkDraw& draw, const SkRect& rect,
+                          const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawRRect(const SkDraw& draw, const SkRRect& rrect,
+                           const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawPath(const SkDraw& draw, const SkPath& path,
+                          const SkPaint& paint, const SkMatrix* prePathMatrix,
+                          bool pathIsMutable) SK_OVERRIDE {
+    }
+    virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                            const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                            int x, int y, const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint& paint,
+                                SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE {
+    }
+    virtual void drawText(const SkDraw& draw, const void* text, size_t len,
+                          SkScalar x, SkScalar y,
+                          const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawPosText(const SkDraw& draw, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int scalarsPerPos, const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode, int vertexCount,
+                              const SkPoint verts[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) SK_OVERRIDE {
+    }
+    virtual void drawDevice(const SkDraw& draw, SkBaseDevice* deviceIn, int x, int y,
+                            const SkPaint&) SK_OVERRIDE {
+        GrGatherDevice* device = static_cast<GrGatherDevice*>(deviceIn);
+
+        if (device->fAlreadyDrawn) {
+            return;
+        }
+
+        device->fInfo.fRestoreOpID = fPicture->EXPERIMENTAL_curOpID();
+        fAccelData->addSaveLayerInfo(device->fInfo);
+        device->fAlreadyDrawn = true;
+    }
+    // TODO: allow this call to return failure, or move to SkBitmapDevice only.
+    virtual const SkBitmap& onAccessBitmap() SK_OVERRIDE {
+        return fEmptyBitmap;
+    }
+#ifdef SK_SUPPORT_LEGACY_READPIXELSCONFIG
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                              int x, int y,
+                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        NotSupported();
+        return false;
+    }
+#endif
+    virtual void lockPixels() SK_OVERRIDE { NothingToDo(); }
+    virtual void unlockPixels() SK_OVERRIDE { NothingToDo(); }
+    virtual bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE { return false; }
+    virtual bool canHandleImageFilter(const SkImageFilter*) SK_OVERRIDE { return false; }
+    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
+                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
+        return false;
+    }
+
+private:
+    // The picture being processed
+    SkPicture *fPicture;
+
+    SkBitmap fEmptyBitmap; // legacy -- need to remove
+
+    // All information gathered during the gather process is stored here
+    GPUAccelData* fAccelData;
+
+    // true if this device has already been drawn back to its parent(s) at least
+    // once.
+    bool   fAlreadyDrawn;
+
+    // The information regarding the saveLayer call this device represents.
+    GPUAccelData::SaveLayerInfo fInfo;
+
+    virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) SK_OVERRIDE {
+        NotSupported();
+    }
+
+    virtual SkBaseDevice* onCreateDevice(const SkImageInfo& info, Usage usage) SK_OVERRIDE {
+        // we expect to only get called via savelayer, in which case it is fine.
+        SkASSERT(kSaveLayer_Usage == usage);
+
+        fInfo.fHasNestedLayers = true;
+        return SkNEW_ARGS(GrGatherDevice, (info.width(), info.height(), fPicture, fAccelData));
+    }
+
+    virtual void flush() SK_OVERRIDE {}
+
+    static void NotSupported() {
+        SkDEBUGFAIL("this method should never be called");
+    }
+
+    static void NothingToDo() {}
+
+    typedef SkBaseDevice INHERITED;
+};
+
+// The GrGatherCanvas allows saveLayers but simplifies clipping. It is really
+// only intended to be used as:
+//
+//      GrGatherDevice dev(w, h, picture, accelData);
+//      GrGatherCanvas canvas(..., picture);
+//      canvas.gather();
+// 
+// which is all just to fill in 'accelData'     
+class SK_API GrGatherCanvas : public SkCanvas {
+public:
+    GrGatherCanvas(GrGatherDevice* device, SkPicture* pict) 
+        : INHERITED(device)
+        , fPicture(pict) {
+    }
+
+    void gather() {
+        if (NULL == fPicture || 0 == fPicture->width() || 0 == fPicture->height()) {
+            return;
+        }
+
+        this->clipRect(SkRect::MakeWH(SkIntToScalar(fPicture->width()),
+                                      SkIntToScalar(fPicture->height())),
+                       SkRegion::kIntersect_Op, false);
+        this->drawPicture(*fPicture);
+    }
+
+    virtual void drawPicture(SkPicture& picture) SK_OVERRIDE {
+        picture.draw(this);
+    }
+protected:
+    // disable aa for speed
+    virtual void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) SK_OVERRIDE {
+        this->INHERITED::onClipRect(rect, op, kHard_ClipEdgeStyle);
+    }
+
+    // for speed, just respect the bounds, and disable AA. May give us a few
+    // false positives and negatives.
+    virtual void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) SK_OVERRIDE {
+        this->updateClipConservativelyUsingBounds(path.getBounds(), op,
+                                                  path.isInverseFillType());
+    }
+    virtual void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle) SK_OVERRIDE {
+        this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
+    }
+
+private:
+    SkPicture* fPicture;
+
+    typedef SkCanvas INHERITED;
+};
+
+// GatherGPUInfo is only intended to be called within the context of SkGpuDevice's 
+// EXPERIMENTAL_optimize method. 
+void GatherGPUInfo(SkPicture* pict, GPUAccelData* accelData) {
+    if (0 == pict->width() || 0 == pict->height()) {
+        return ;
+    }
+
+    GrGatherDevice device(pict->width(), pict->height(), pict, accelData);
+    GrGatherCanvas canvas(&device, pict);
+
+    canvas.gather();
+}
+
diff --git a/src/gpu/GrPictureUtils.h b/src/gpu/GrPictureUtils.h
new file mode 100644
index 0000000..6b4d901
--- /dev/null
+++ b/src/gpu/GrPictureUtils.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrPictureUtils_DEFINED
+#define GrPictureUtils_DEFINED
+
+#include "SkPicture.h"
+#include "SkTDArray.h"
+
+// This class encapsulates the GPU-backend-specific acceleration data
+// for a single SkPicture
+class GPUAccelData : public SkPicture::AccelData {
+public:
+    // Information about a given saveLayer in an SkPicture
+    struct SaveLayerInfo {
+        // The size of the saveLayer
+        SkISize fSize;
+        // The ID of this saveLayer in the picture. 0 is an invalid ID.
+        size_t  fSaveLayerOpID;
+        // The ID of the matching restore in the picture. 0 is an invalid ID.
+        size_t  fRestoreOpID;
+        // True if this saveLayer has at least one other saveLayer nested within it.
+        // False otherwise.
+        bool    fHasNestedLayers;
+    };
+
+    GPUAccelData(Key key) : INHERITED(key) { }
+
+    void addSaveLayerInfo(const SaveLayerInfo& info) {
+        SkASSERT(info.fSaveLayerOpID < info.fRestoreOpID);
+        *fSaveLayerInfo.push() = info;
+    }
+
+    int numSaveLayers() const { return fSaveLayerInfo.count(); }
+
+    const SaveLayerInfo& saveLayerInfo(int index) const {
+        SkASSERT(index < fSaveLayerInfo.count());
+
+        return fSaveLayerInfo[index];
+    }
+
+protected:
+    SkTDArray<SaveLayerInfo> fSaveLayerInfo;
+
+private:
+    typedef SkPicture::AccelData INHERITED;
+};
+
+void GatherGPUInfo(SkPicture* pict, GPUAccelData* accelData);
+
+#endif // GrPictureUtils_DEFINED
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 1e52d23..1769f2b 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -14,6 +14,7 @@
 #include "GrContext.h"
 #include "GrBitmapTextContext.h"
 #include "GrDistanceFieldTextContext.h"
+#include "GrPictureUtils.h"
 
 #include "SkGrTexturePixelRef.h"
 
@@ -1944,16 +1945,6 @@
     return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples());
 }
 
-class GPUAccelData : public SkPicture::AccelData {
-public:
-    GPUAccelData(Key key) : INHERITED(key) { }
-
-protected:
-
-private:
-    typedef SkPicture::AccelData INHERITED;
-};
-
 // In the future this may not be a static method if we need to incorporate the
 // clip and matrix state into the key
 SkPicture::AccelData::Key SkGpuDevice::ComputeAccelDataKey() {
@@ -1968,18 +1959,84 @@
     GPUAccelData* data = SkNEW_ARGS(GPUAccelData, (key));
 
     picture->EXPERIMENTAL_addAccelData(data);
+
+    GatherGPUInfo(picture, data);
 }
 
-bool SkGpuDevice::EXPERIMENTAL_drawPicture(const SkPicture& picture) {
+bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkPicture* picture) {
+
     SkPicture::AccelData::Key key = ComputeAccelDataKey();
 
-    const SkPicture::AccelData* data = picture.EXPERIMENTAL_getAccelData(key);
+    const SkPicture::AccelData* data = picture->EXPERIMENTAL_getAccelData(key);
     if (NULL == data) {
         return false;
     }
 
-#if 0
     const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data);
+
+//#define SK_PRINT_PULL_FORWARD_INFO 1
+
+#ifdef SK_PRINT_PULL_FORWARD_INFO
+    static bool gPrintedAccelData = false;
+
+    if (!gPrintedAccelData) {
+        for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
+            const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i);
+
+            SkDebugf("%d: Width: %d Height: %d SL: %d R: %d hasNestedLayers: %s\n", 
+                                            i,
+                                            info.fSize.fWidth, 
+                                            info.fSize.fHeight, 
+                                            info.fSaveLayerOpID, 
+                                            info.fRestoreOpID, 
+                                            info.fHasNestedLayers ? "T" : "F");
+        }
+        gPrintedAccelData = true;
+    }
+#endif
+
+    SkAutoTArray<bool> pullForward(gpuData->numSaveLayers());
+    for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
+        pullForward[i] = false;
+    }
+
+    SkIRect clip;
+
+    fClipData.getConservativeBounds(this->width(), this->height(), &clip, NULL);
+
+    SkMatrix inv;
+    if (!fContext->getMatrix().invert(&inv)) {
+        return false;
+    }
+
+    SkRect r = SkRect::Make(clip);
+    inv.mapRect(&r);
+    r.roundOut(&clip);
+
+    const SkPicture::OperationList& ops = picture->EXPERIMENTAL_getActiveOps(clip);
+
+#ifdef SK_PRINT_PULL_FORWARD_INFO
+    SkDebugf("rect: %d %d %d %d\n", clip.fLeft, clip.fTop, clip.fRight, clip.fBottom);
+#endif
+
+    for (int i = 0; i < ops.numOps(); ++i) {
+        for (int j = 0; j < gpuData->numSaveLayers(); ++j) {
+            const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j);
+
+            if (ops.offset(i) > info.fSaveLayerOpID && ops.offset(i) < info.fRestoreOpID) {
+                pullForward[j] = true;
+            }
+        }
+    }
+
+#ifdef SK_PRINT_PULL_FORWARD_INFO
+    SkDebugf("Need SaveLayers: ");
+    for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
+        if (pullForward[i]) {
+            SkDebugf("%d, ", i);
+        }
+    }
+    SkDebugf("\n");
 #endif
 
     return false;