From: junov@chromium.org Date: Tue, 14 Aug 2012 13:36:26 +0000 (+0000) Subject: Augmenting SkDeferredCanvas notification interface to signal flushes and memory alloc... X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~15194 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9ed02b9da25a76ee4c73c1ab19c18b899a223a17;p=platform%2Fupstream%2FlibSkiaSharp.git Augmenting SkDeferredCanvas notification interface to signal flushes and memory allocations 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 --- diff --git a/bench/DeferredCanvasBench.cpp b/bench/DeferredCanvasBench.cpp new file mode 100644 index 0000000..6087388 --- /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 e32db41..5d2730d 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 67ef2ae..ed7ec00 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 6259f94..bf3ce16 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 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 69ca75a..d56bba6 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, ¬ificationCounter); 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);