aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/DeferredCanvasBench.cpp107
-rw-r--r--gyp/bench.gypi1
-rw-r--r--include/utils/SkDeferredCanvas.h60
-rw-r--r--src/utils/SkDeferredCanvas.cpp141
-rw-r--r--tests/DeferredCanvasTest.cpp36
5 files changed, 292 insertions, 53 deletions
diff --git a/bench/DeferredCanvasBench.cpp b/bench/DeferredCanvasBench.cpp
new file mode 100644
index 0000000000..60873887a5
--- /dev/null
+++ b/bench/DeferredCanvasBench.cpp
@@ -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);
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index e32db41117..5d2730d296 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -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',
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
index 67ef2aecc6..ed7ec00adc 100644
--- a/include/utils/SkDeferredCanvas.h
+++ b/include/utils/SkDeferredCanvas.h
@@ -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;
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 6259f949ee..bf3ce168da 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -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() {
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
index 69ca75ad69..d56bba609f 100644
--- a/tests/DeferredCanvasTest.cpp
+++ b/tests/DeferredCanvasTest.cpp
@@ -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);