Augmenting SkDeferredCanvas notification interface to signal flushes and memory alloc...
authorjunov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 14 Aug 2012 13:36:26 +0000 (13:36 +0000)
committerjunov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 14 Aug 2012 13:36:26 +0000 (13:36 +0000)
Renamed SkDeferredCanvas::DeviceContext to SkDeferredCanvas::NotificationClient

BUG=http://code.google.com/p/chromium/issues/detail?id=136828
TEST=Added coverage for new API to DeferredCanvas unit test. Added DeferredCanvas bench test to track deferred canvas overhead cost.
Review URL: https://codereview.appspot.com/6442108

git-svn-id: http://skia.googlecode.com/svn/trunk@5078 2bbb7eff-a529-9590-31e7-b0007b416f81

bench/DeferredCanvasBench.cpp [new file with mode: 0644]
gyp/bench.gypi
include/utils/SkDeferredCanvas.h
src/utils/SkDeferredCanvas.cpp
tests/DeferredCanvasTest.cpp

diff --git a/bench/DeferredCanvasBench.cpp b/bench/DeferredCanvasBench.cpp
new file mode 100644 (file)
index 0000000..6087388
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkBenchmark.h"
+#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
+#include "SkString.h"
+
+class DeferredCanvasBench : public SkBenchmark {
+public:
+    DeferredCanvasBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("deferred_canvas_%s", name);
+    }
+
+    enum {
+        N = SkBENCHLOOP(25), // number of times to create the picture
+        CANVAS_WIDTH = 200,
+        CANVAS_HEIGHT = 200,
+    };
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkDeferredCanvas deferredCanvas(canvas->getDevice()->createCompatibleDevice(
+            SkBitmap::kARGB_8888_Config, CANVAS_WIDTH, CANVAS_HEIGHT, false));
+
+        initDeferredCanvas(deferredCanvas);
+
+        for (int i = 0; i < N; i++) {
+            drawInDeferredCanvas(deferredCanvas);
+        }
+
+        finalizeDeferredCanvas(deferredCanvas);
+        deferredCanvas.flush();
+    }
+
+    virtual void initDeferredCanvas(SkDeferredCanvas& canvas) = 0;
+    virtual void drawInDeferredCanvas(SkDeferredCanvas& canvas) = 0;
+    virtual void finalizeDeferredCanvas(SkDeferredCanvas& canvas) = 0;
+
+    SkString fName;
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class SimpleNotificationClient : public SkDeferredCanvas::NotificationClient {
+public:
+    SimpleNotificationClient() : fDummy(false) {}
+
+    //bogus virtual implementations that just do something small
+    virtual void prepareForDraw() SK_OVERRIDE {fDummy = true;}
+    virtual void storageAllocatedForRecordingChanged(size_t) SK_OVERRIDE {fDummy = false;}
+    virtual void flushedDrawCommands() SK_OVERRIDE {fDummy = !fDummy;}
+private:
+    bool fDummy;
+};
+
+// Test that records very simple draw operations.
+// This benchmark aims to capture performance fluctuations in the recording
+// overhead of SkDeferredCanvas
+class DeferredRecordBench : public DeferredCanvasBench {
+public:
+    DeferredRecordBench(void* param)
+        : INHERITED(param, "record") {
+    }
+
+    enum {
+        M = SkBENCHLOOP(700),   // number of individual draws in each loop
+    };
+protected:
+
+    virtual void initDeferredCanvas(SkDeferredCanvas& canvas) SK_OVERRIDE {
+        canvas.setNotificationClient(SkNEW(SimpleNotificationClient))->unref();
+    }
+
+    virtual void drawInDeferredCanvas(SkDeferredCanvas& canvas) SK_OVERRIDE {
+        SkRect rect;
+        rect.setXYWH(0, 0, 10, 10);
+        SkPaint paint;
+        for (int i = 0; i < M; i++) {
+            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            canvas.translate(SkIntToScalar(i * 27 % CANVAS_WIDTH), SkIntToScalar(i * 13 % CANVAS_HEIGHT));
+            canvas.drawRect(rect, paint);
+            canvas.restore();
+        }
+    }
+
+    virtual void finalizeDeferredCanvas(SkDeferredCanvas& canvas) SK_OVERRIDE {
+        canvas.clear(0x0);
+    }
+   
+private:
+    typedef DeferredCanvasBench INHERITED;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return new DeferredRecordBench(p); }
+
+static BenchRegistry gReg0(Fact0);
index e32db41..5d2730d 100644 (file)
@@ -13,6 +13,7 @@
     '../bench/ChromeBench.cpp',
     '../bench/DashBench.cpp',
     '../bench/DecodeBench.cpp',
+    '../bench/DeferredCanvasBench.cpp',
     '../bench/FontScalerBench.cpp',
     '../bench/GradientBench.cpp',
     '../bench/GrMemoryPoolBench.cpp',
index 67ef2ae..ed7ec00 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 Google Inc.
+ * Copyright 2012 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -23,7 +23,7 @@ class DeferredDevice;
 */
 class SK_API SkDeferredCanvas : public SkCanvas {
 public:
-    class DeviceContext;
+    class NotificationClient;
 
     SkDeferredCanvas();
 
@@ -35,17 +35,19 @@ public:
 
     /** Construct a canvas with the specified device to draw into, and
      *  a device context. Equivalent to calling default constructor, then
-     *  setDevice.
+     *  setDevice. The canvas takes reference on the device and notification
+     *  client.
      *  @param device Specifies a device for the canvas to draw into.
-     *  @param deviceContext interface for the device's the graphics context
+     *  @param client Interface for dispatching notifications
      */
-    explicit SkDeferredCanvas(SkDevice* device, DeviceContext* deviceContext);
+    explicit SkDeferredCanvas(SkDevice* device, NotificationClient* client);
 
     virtual ~SkDeferredCanvas();
 
     /**
      *  Specify a device to be used by this canvas. Calling setDevice will
-     *  release the previously set device, if any.
+     *  release the previously set device, if any. Takes a reference on the
+     *  device.
      *
      *  @param device The device that the canvas will raw into
      *  @return The device argument, for convenience.
@@ -53,18 +55,20 @@ public:
     virtual SkDevice* setDevice(SkDevice* device);
 
     /**
-     *  Specify a deviceContext to be used by this canvas. Calling
-     *  setDeviceContext will release the previously set deviceContext, if any.
-     *  A deviceContext must be specified if the device uses a graphics context
-     *  that requires some form of state initialization prior to drawing
-     *  and/or explicit flushing to synchronize the execution of rendering
-     *  operations.
+     *  Specify a NotificationClient to be used by this canvas. Calling
+     *  setNotificationClient will release the previously set 
+     *  NotificationClient, if any. Takes a reference on the notification
+     *  client.
      *  Note: Must be called after the device is set with setDevice.
      *
-     *  @deviceContext interface for the device's the graphics context
-     *  @return The deviceContext argument, for convenience.
+     *  @param notificationClient interface for dispatching notifications
+     *  @return The notificationClient argument, for convenience.
      */
-    DeviceContext* setDeviceContext(DeviceContext* deviceContext);
+    NotificationClient* setNotificationClient(NotificationClient* notificationClient);
+    // Temporarily bootstrapping the deprecated method name
+    NotificationClient* setDeviceContext(NotificationClient* notificationClient) {
+        return setNotificationClient(notificationClient);
+    }
 
     /**
      *  Enable or disable deferred drawing. When deferral is disabled,
@@ -176,21 +180,43 @@ public:
     virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter) SK_OVERRIDE;
 
 public:
-    class DeviceContext : public SkRefCnt {
+    class NotificationClient : public SkRefCnt {
     public:
-        SK_DECLARE_INST_COUNT(DeviceContext)
+        SK_DECLARE_INST_COUNT(NotificationClient)
 
+        /**
+         *  Called before executing one or several draw commands, which means
+         *  once per flush when deferred rendering is enabled.
+         */
         virtual void prepareForDraw() {}
+
+        /**
+         *  Called after a recording a draw command if additional memory
+         *  had to be allocated for recording.
+         *  @param newAllocatedStorage same value as would be returned by 
+         *      storageAllocatedForRecording(), for convenience.
+         */
+        virtual void storageAllocatedForRecordingChanged(
+            size_t newAllocatedStorage) {}
+
+        /**
+         *  Called after pending draw commands have been flushed
+         */
+        virtual void flushedDrawCommands() {}
         
     private:
         typedef SkRefCnt INHERITED;
     };
 
+    // Temporarily bootstrapping the deprecated name for a smooth chromium DEPS roll
+    typedef NotificationClient DeviceContext;
+
 protected:
     virtual SkCanvas* canvasForDrawIter();
     DeferredDevice* getDeferredDevice() const;
 
 private:
+    void recordedDrawCommand();
     SkCanvas* drawingCanvas() const;
     SkCanvas* immediateCanvas() const;
     bool isFullFrame(const SkRect*, const SkPaint*) const;
index 6259f94..bf3ce16 100644 (file)
@@ -16,7 +16,7 @@
 #include "SkPaint.h"
 #include "SkShader.h"
 
-SK_DEFINE_INST_COUNT(SkDeferredCanvas::DeviceContext)
+SK_DEFINE_INST_COUNT(SkDeferredCanvas::NotificationClient)
 
 enum {
     // Deferred canvas will auto-flush when recording reaches this limit
@@ -233,10 +233,10 @@ void DeferredPipeController::reset() {
 class DeferredDevice : public SkDevice {
 public:
     DeferredDevice(SkDevice* immediateDevice,
-        SkDeferredCanvas::DeviceContext* deviceContext = NULL);
+        SkDeferredCanvas::NotificationClient* notificationClient = NULL);
     ~DeferredDevice();
 
-    void setDeviceContext(SkDeferredCanvas::DeviceContext* deviceContext);
+    void setNotificationClient(SkDeferredCanvas::NotificationClient* notificationClient);
     SkCanvas* recordingCanvas();
     SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
     SkDevice* immediateDevice() const {return fImmediateDevice;}
@@ -246,6 +246,7 @@ public:
     void flushPending();
     void contentsCleared();
     void setMaxRecordingStorage(size_t);
+    void recordedDrawCommand();
 
     virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
     virtual int width() const SK_OVERRIDE;
@@ -337,20 +338,22 @@ private:
     SkDevice* fImmediateDevice;
     SkCanvas* fImmediateCanvas;
     SkCanvas* fRecordingCanvas;
-    SkDeferredCanvas::DeviceContext* fDeviceContext;
+    SkDeferredCanvas::NotificationClient* fNotificationClient;
     bool fFreshFrame;
     size_t fMaxRecordingStorageBytes;
+    size_t fPreviousStorageAllocated;
 };
 
 DeferredDevice::DeferredDevice(
-    SkDevice* immediateDevice, SkDeferredCanvas::DeviceContext* deviceContext) :
+    SkDevice* immediateDevice, SkDeferredCanvas::NotificationClient* notificationClient) :
     SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
              immediateDevice->height(), immediateDevice->isOpaque())
-    , fFreshFrame(true) {
+    , fFreshFrame(true)
+    , fPreviousStorageAllocated(0){
 
     fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
-    fDeviceContext = deviceContext;
-    SkSafeRef(fDeviceContext);
+    fNotificationClient = notificationClient;
+    SkSafeRef(fNotificationClient);
     fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
     fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
     fPipeController.setPlaybackCanvas(fImmediateCanvas);
@@ -360,7 +363,7 @@ DeferredDevice::DeferredDevice(
 DeferredDevice::~DeferredDevice() {
     this->flushPending();
     SkSafeUnref(fImmediateCanvas);
-    SkSafeUnref(fDeviceContext);
+    SkSafeUnref(fNotificationClient);
 }
 
 void DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
@@ -378,9 +381,9 @@ void DeferredDevice::beginRecording() {
     fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0);
 }
     
-void DeferredDevice::setDeviceContext(
-    SkDeferredCanvas::DeviceContext* deviceContext) {
-    SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
+void DeferredDevice::setNotificationClient(
+    SkDeferredCanvas::NotificationClient* notificationClient) {
+    SkRefCnt_SafeAssign(fNotificationClient, notificationClient);
 }
 
 void DeferredDevice::contentsCleared() {
@@ -402,6 +405,7 @@ void DeferredDevice::contentsCleared() {
             // old one, hence purging deferred draw ops.
             this->endRecording();
             this->beginRecording();
+            fPreviousStorageAllocated = storageAllocatedForRecording();
 
             // Restore pre-purge state
             if (!clipRegion.isEmpty()) {
@@ -428,12 +432,15 @@ void DeferredDevice::flushPending() {
     if (!fPipeController.hasRecorded()) {
         return;
     }
-    if (fDeviceContext) {
-        fDeviceContext->prepareForDraw();
+    if (fNotificationClient) {
+        fNotificationClient->prepareForDraw();
     }
-
     fPipeWriter.flushRecording(true);
     fPipeController.playback();
+    if (fNotificationClient) {
+        fNotificationClient->flushedDrawCommands();
+    }
+    fPreviousStorageAllocated = storageAllocatedForRecording();
 }
 
 void DeferredDevice::flush() {
@@ -442,7 +449,9 @@ void DeferredDevice::flush() {
 }
 
 size_t DeferredDevice::freeMemoryIfPossible(size_t bytesToFree) {
-    return fPipeWriter.freeMemoryIfPossible(bytesToFree);
+    size_t val = fPipeWriter.freeMemoryIfPossible(bytesToFree);
+    fPreviousStorageAllocated = storageAllocatedForRecording();
+    return val;
 }
 
 size_t DeferredDevice::storageAllocatedForRecording() const {
@@ -450,8 +459,9 @@ size_t DeferredDevice::storageAllocatedForRecording() const {
             + fPipeWriter.storageAllocatedForRecording());
 }
 
-SkCanvas* DeferredDevice::recordingCanvas() {
+void DeferredDevice::recordedDrawCommand() {
     size_t storageAllocated = this->storageAllocatedForRecording();
+
     if (storageAllocated > fMaxRecordingStorageBytes) {
         // First, attempt to reduce cache without flushing
         size_t tryFree = storageAllocated - fMaxRecordingStorageBytes;
@@ -462,7 +472,17 @@ SkCanvas* DeferredDevice::recordingCanvas() {
             // which could cause a high flushing frequency.
             this->freeMemoryIfPossible(~0);
         }
+        storageAllocated = this->storageAllocatedForRecording();
     }
+
+    if (fNotificationClient && 
+        storageAllocated != fPreviousStorageAllocated) {
+        fPreviousStorageAllocated = storageAllocated;
+        fNotificationClient->storageAllocatedForRecordingChanged(storageAllocated);
+    }
+}
+
+SkCanvas* DeferredDevice::recordingCanvas() {
     return fRecordingCanvas;
 }
 
@@ -507,6 +527,8 @@ void DeferredDevice::writePixels(const SkBitmap& bitmap,
         fImmediateCanvas->drawSprite(bitmap, x, y, &paint);
     } else {
         this->recordingCanvas()->drawSprite(bitmap, x, y, &paint);
+        this->recordedDrawCommand();
+
     }
 }
 
@@ -525,7 +547,7 @@ SkDevice* DeferredDevice::onCreateCompatibleDevice(
     SkAutoTUnref<SkDevice> compatibleDevice
         (fImmediateDevice->createCompatibleDevice(config, width, height,
             isOpaque));
-    return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
+    return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fNotificationClient));
 }
 
 bool DeferredDevice::onReadPixels(
@@ -546,10 +568,10 @@ SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
 }
 
 SkDeferredCanvas::SkDeferredCanvas(SkDevice* device, 
-                                   DeviceContext* deviceContext) {
+                                   NotificationClient* notificationClient) {
     this->init();
     this->setDevice(device);
-    this->setDeviceContext(deviceContext);
+    this->setNotificationClient(notificationClient);
 }
 
 void SkDeferredCanvas::init() {
@@ -569,6 +591,12 @@ size_t SkDeferredCanvas::freeMemoryIfPossible(size_t bytesToFree) {
     return this->getDeferredDevice()->freeMemoryIfPossible(bytesToFree);
 }
 
+void SkDeferredCanvas::recordedDrawCommand() {
+    if (fDeferredDrawing) {
+        this->getDeferredDevice()->recordedDrawCommand();
+    }
+}
+
 void SkDeferredCanvas::validate() const {
     SkASSERT(this->getDevice());
 }
@@ -615,15 +643,15 @@ SkDevice* SkDeferredCanvas::setDevice(SkDevice* device) {
     return device;
 }
 
-SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
-    DeviceContext* deviceContext) {
+SkDeferredCanvas::NotificationClient* SkDeferredCanvas::setNotificationClient(
+    NotificationClient* notificationClient) {
 
     DeferredDevice* deferredDevice = this->getDeferredDevice();
     SkASSERT(deferredDevice);
     if (deferredDevice) {
-        deferredDevice->setDeviceContext(deviceContext);
+        deferredDevice->setNotificationClient(notificationClient);
     }
-    return deviceContext;
+    return notificationClient;
 }
 
 bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
@@ -683,7 +711,10 @@ bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
 
 int SkDeferredCanvas::save(SaveFlags flags) {
     this->drawingCanvas()->save(flags);
-    return this->INHERITED::save(flags);
+    int val = this->INHERITED::save(flags);
+    this->recordedDrawCommand();
+
+    return val;
 }
 
 int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
@@ -691,12 +722,15 @@ int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
     this->drawingCanvas()->saveLayer(bounds, paint, flags);
     int count = this->INHERITED::save(flags);
     this->clipRectBounds(bounds, flags, NULL);
+    this->recordedDrawCommand();
+
     return count;
 }
 
 void SkDeferredCanvas::restore() {
     this->drawingCanvas()->restore();
     this->INHERITED::restore();
+    this->recordedDrawCommand();
 }
 
 bool SkDeferredCanvas::isDrawingToLayer() const {
@@ -705,52 +739,69 @@ bool SkDeferredCanvas::isDrawingToLayer() const {
 
 bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy) {
     this->drawingCanvas()->translate(dx, dy);
-    return this->INHERITED::translate(dx, dy);
+    bool val = this->INHERITED::translate(dx, dy);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy) {
     this->drawingCanvas()->scale(sx, sy);
-    return this->INHERITED::scale(sx, sy);
+    bool val = this->INHERITED::scale(sx, sy);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::rotate(SkScalar degrees) {
     this->drawingCanvas()->rotate(degrees);
-    return this->INHERITED::rotate(degrees);
+    bool val = this->INHERITED::rotate(degrees);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy) {
     this->drawingCanvas()->skew(sx, sy);
-    return this->INHERITED::skew(sx, sy);
+    bool val = this->INHERITED::skew(sx, sy);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::concat(const SkMatrix& matrix) {
     this->drawingCanvas()->concat(matrix);
-    return this->INHERITED::concat(matrix);
+    bool val = this->INHERITED::concat(matrix);
+    this->recordedDrawCommand();
+    return val;
 }
 
 void SkDeferredCanvas::setMatrix(const SkMatrix& matrix) {
     this->drawingCanvas()->setMatrix(matrix);
     this->INHERITED::setMatrix(matrix);
+    this->recordedDrawCommand();
 }
 
 bool SkDeferredCanvas::clipRect(const SkRect& rect,
                                 SkRegion::Op op,
                                 bool doAntiAlias) {
     this->drawingCanvas()->clipRect(rect, op, doAntiAlias);
-    return this->INHERITED::clipRect(rect, op, doAntiAlias);
+    bool val = this->INHERITED::clipRect(rect, op, doAntiAlias);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::clipPath(const SkPath& path,
                                 SkRegion::Op op,
                                 bool doAntiAlias) {
     this->drawingCanvas()->clipPath(path, op, doAntiAlias);
-    return this->INHERITED::clipPath(path, op, doAntiAlias);
+    bool val = this->INHERITED::clipPath(path, op, doAntiAlias);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
                                   SkRegion::Op op) {
     this->drawingCanvas()->clipRegion(deviceRgn, op);
-    return this->INHERITED::clipRegion(deviceRgn, op);
+    bool val = this->INHERITED::clipRegion(deviceRgn, op);
+    this->recordedDrawCommand();
+    return val;
 }
 
 void SkDeferredCanvas::clear(SkColor color) {
@@ -760,6 +811,7 @@ void SkDeferredCanvas::clear(SkColor color) {
     }
 
     this->drawingCanvas()->clear(color);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
@@ -769,12 +821,14 @@ void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
     }
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawPaint(paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
                                   const SkPoint pts[], const SkPaint& paint) {
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawPoints(mode, count, pts, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
@@ -785,11 +839,13 @@ void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
 
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawRect(rect, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawPath(path, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
@@ -804,6 +860,7 @@ void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
 
     AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
     this->drawingCanvas()->drawBitmap(bitmap, left, top, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap, 
@@ -818,6 +875,7 @@ void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap,
 
     AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
     this->drawingCanvas()->drawBitmapRect(bitmap, src, dst, paint);
+    this->recordedDrawCommand();
 }
 
 
@@ -828,6 +886,7 @@ void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
     // covers canvas entirely and transformed bitmap covers canvas entirely
     AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
     this->drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
@@ -837,6 +896,7 @@ void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
     // covers canvas entirely and dst covers canvas entirely
     AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
     this->drawingCanvas()->drawBitmapNine(bitmap, center, dst, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
@@ -854,18 +914,21 @@ void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
 
     AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
     this->drawingCanvas()->drawSprite(bitmap, left, top, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
                                 SkScalar x, SkScalar y, const SkPaint& paint) {
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawText(text, byteLength, x, y, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
                                    const SkPoint pos[], const SkPaint& paint) {
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawPosText(text, byteLength, pos, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
@@ -873,6 +936,7 @@ void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
                                     const SkPaint& paint) {
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
@@ -881,10 +945,12 @@ void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
                                       const SkPaint& paint) {
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawTextOnPath(text, byteLength, path, matrix, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPicture(SkPicture& picture) {
     this->drawingCanvas()->drawPicture(picture);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
@@ -896,16 +962,21 @@ void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
     AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
     this->drawingCanvas()->drawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
                                         indices, indexCount, paint);
+    this->recordedDrawCommand();
 }
 
 SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) {
     this->drawingCanvas()->setBounder(bounder);
-    return this->INHERITED::setBounder(bounder);
+    this->INHERITED::setBounder(bounder);
+    this->recordedDrawCommand();
+    return bounder;
 }
 
 SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
     this->drawingCanvas()->setDrawFilter(filter); 
-    return this->INHERITED::setDrawFilter(filter);
+    this->INHERITED::setDrawFilter(filter);
+    this->recordedDrawCommand();
+    return filter;    
 }
 
 SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
index 69ca75a..d56bba6 100644 (file)
@@ -214,12 +214,34 @@ static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
     REPORTER_ASSERT(reporter, mockDevice.fDrawBitmapCallCount == 4);
 }
 
+class NotificationCounter : public SkDeferredCanvas::NotificationClient {
+public:
+    NotificationCounter() {
+        fPrepareForDrawCount = fStorageAllocatedChangedCount = fFlushedDrawCommandsCount = 0;
+    }
+
+    virtual void prepareForDraw() SK_OVERRIDE {
+        fPrepareForDrawCount++;
+    }
+    virtual void storageAllocatedForRecordingChanged(size_t size) SK_OVERRIDE {
+        fStorageAllocatedChangedCount++;
+    }
+    virtual void flushedDrawCommands() SK_OVERRIDE {
+        fFlushedDrawCommandsCount++;        
+    }
+
+    int fPrepareForDrawCount;
+    int fStorageAllocatedChangedCount;
+    int fFlushedDrawCommandsCount;
+};
+
 static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
     SkBitmap store;
     store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
     store.allocPixels();
     SkDevice device(store);
-    SkDeferredCanvas canvas(&device);
+    NotificationCounter notificationCounter;
+    SkDeferredCanvas canvas(&device, &notificationCounter);
 
     const int imageCount = 2;
     SkBitmap sourceImages[imageCount];
@@ -232,6 +254,7 @@ static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
     size_t bitmapSize = sourceImages[0].getSize();
 
     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fStorageAllocatedChangedCount);
     // stored bitmap + drawBitmap command
     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > bitmapSize);
     
@@ -239,7 +262,11 @@ static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
     REPORTER_ASSERT(reporter, 0 == canvas.freeMemoryIfPossible(~0));
 
     // verify that flush leaves image in cache
+    REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
+    REPORTER_ASSERT(reporter, 0 == notificationCounter.fPrepareForDrawCount);
     canvas.flush();
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fPrepareForDrawCount);
     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() >= bitmapSize);
 
     // verify that after a flush, cached image can be freed
@@ -247,20 +274,27 @@ static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
 
     // Verify that caching works for avoiding multiple copies of the same bitmap
     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < 2 * bitmapSize);
 
     // Verify partial eviction based on bytesToFree
     canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
     canvas.flush();
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2 * bitmapSize);
     size_t bytesFreed = canvas.freeMemoryIfPossible(1);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
     REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
     REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
 
     // Verifiy that partial purge works, image zero is in cache but not reffed by 
     // a pending draw, while image 1 is locked-in.
     canvas.freeMemoryIfPossible(~0);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
     canvas.flush();
     canvas.drawBitmap(sourceImages[1], 0, 0, NULL);