remove SkDeferredCanvas

Waiting a day or so to see if the blink-removal of SkDeferredCanvas sticks

BUG=skia:

Review URL: https://codereview.chromium.org/1269093002
This commit is contained in:
reed 2015-08-19 08:18:04 -07:00 committed by Commit bot
parent da04e0e80a
commit 451af5062e
13 changed files with 2 additions and 2430 deletions

View File

@ -1,74 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Benchmark.h"
#include "SkDeferredCanvas.h"
#include "SkDevice.h"
#include "SkImage.h"
#include "SkSurface.h"
#if SK_SUPPORT_GPU
#include "GrRenderTarget.h"
#endif
class DeferredSurfaceCopyBench : public Benchmark {
enum {
kSurfaceWidth = 1000,
kSurfaceHeight = 1000,
};
public:
DeferredSurfaceCopyBench(bool discardableContents) {
fDiscardableContents = discardableContents;
}
protected:
const char* onGetName() override {
return fDiscardableContents ? "DeferredSurfaceCopy_discardable" :
"DeferredSurfaceCopy_nonDiscardable";
}
void onDraw(const int loops, SkCanvas* canvas) override {
// The canvas is not actually used for this test except to provide
// configuration information: gpu, multisampling, size, etc?
SkImageInfo info = SkImageInfo::MakeN32Premul(kSurfaceWidth, kSurfaceHeight);
const SkRect fullCanvasRect = SkRect::MakeWH(
SkIntToScalar(kSurfaceWidth), SkIntToScalar(kSurfaceHeight));
SkAutoTUnref<SkSurface> surface(canvas->newSurface(info));
// newSurface() can return NULL for several reasons, so we need to check
if (NULL == surface.get()) {
SkDebugf("DeferredSurfaceCopyBench newSurface failed, bench results are meaningless\n");
return; // should we signal the caller that we hit an error?
}
SkAutoTUnref<SkDeferredCanvas> drawingCanvas(SkDeferredCanvas::Create(surface));
for (int iteration = 0; iteration < loops; iteration++) {
drawingCanvas->clear(0);
SkAutoTUnref<SkImage> image(drawingCanvas->newImageSnapshot());
SkPaint paint;
if (!fDiscardableContents) {
// If paint is not opaque, prior canvas contents are
// not discardable because they are needed for compositing.
paint.setAlpha(127);
}
drawingCanvas->drawRect(fullCanvasRect, paint);
// Trigger copy on write, which should be faster in the discardable case.
drawingCanvas->flush();
}
}
private:
bool fDiscardableContents;
typedef Benchmark INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_BENCH( return new DeferredSurfaceCopyBench(false); )
DEF_BENCH( return new DeferredSurfaceCopyBench(true); )

View File

@ -418,7 +418,6 @@ static Sink* create_via(const char* tag, Sink* wrapped) {
VIA("twice", ViaTwice, wrapped);
VIA("pipe", ViaPipe, wrapped);
VIA("serialize", ViaSerialization, wrapped);
VIA("deferred", ViaDeferred, wrapped);
VIA("2ndpic", ViaSecondPicture, wrapped);
VIA("sp", ViaSingletonPictures, wrapped);
VIA("tiles", ViaTiles, 256, 256, NULL, wrapped);

View File

@ -10,7 +10,6 @@
#include "SkCodec.h"
#include "SkCommonFlags.h"
#include "SkData.h"
#include "SkDeferredCanvas.h"
#include "SkDocument.h"
#include "SkError.h"
#include "SkFunction.h"
@ -874,37 +873,13 @@ Error ViaPipe::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkStrin
return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) {
PipeController controller(canvas, &SkImageDecoder::DecodeMemory);
SkGPipeWriter pipe;
const uint32_t kFlags = 0; // We mirror SkDeferredCanvas, which doesn't use any flags.
const uint32_t kFlags = 0;
return src.draw(pipe.startRecording(&controller, kFlags, size.width(), size.height()));
});
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
Error ViaDeferred::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
// We draw via a deferred canvas into a surface that's compatible with the original canvas,
// then snap that surface as an image and draw it into the original canvas.
return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) -> Error {
SkAutoTUnref<SkSurface> surface(canvas->newSurface(canvas->imageInfo()));
if (!surface.get()) {
return "can't make surface for deferred canvas";
}
SkAutoTDelete<SkDeferredCanvas> defcan(SkDeferredCanvas::Create(surface));
Error err = src.draw(defcan);
if (!err.isEmpty()) {
return err;
}
SkAutoTUnref<SkImage> image(defcan->newImageSnapshot());
if (!image) {
return "failed to create deferred image snapshot";
}
canvas->drawImage(image, 0, 0, NULL);
return "";
});
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
Error ViaSerialization::draw(
const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
// Record our Src into a picture.

View File

@ -18,7 +18,6 @@
'<(skia_include_path)/utils/SkCubicInterval.h',
'<(skia_include_path)/utils/SkCullPoints.h',
'<(skia_include_path)/utils/SkDebugUtils.h',
'<(skia_include_path)/utils/SkDeferredCanvas.h',
'<(skia_include_path)/utils/SkDumpCanvas.h',
'<(skia_include_path)/utils/SkEventTracer.h',
'<(skia_include_path)/utils/SkInterpolator.h',
@ -54,7 +53,6 @@
'<(skia_src_path)/utils/SkCullPoints.cpp',
'<(skia_src_path)/utils/SkDashPath.cpp',
'<(skia_src_path)/utils/SkDashPathPriv.h',
'<(skia_src_path)/utils/SkDeferredCanvas.cpp',
'<(skia_src_path)/utils/SkDumpCanvas.cpp',
'<(skia_src_path)/utils/SkEventTracer.cpp',
'<(skia_src_path)/utils/SkFloatUtils.h',

View File

@ -1370,7 +1370,6 @@ private:
friend class AutoDrawLooper;
friend class SkLua; // needs top layer size and offset
friend class SkDebugCanvas; // needs experimental fAllowSimplifyClip
friend class SkDeferredDevice; // needs getTopDevice()
friend class SkSurface_Raster; // needs getDevice()
friend class SkRecorder; // InitFlags
friend class SkNoSaveLayerCanvas; // InitFlags

View File

@ -373,7 +373,6 @@ private:
friend class SkDrawIter;
friend class SkDeviceFilteredPaint;
friend class SkImageFilter::Proxy;
friend class SkDeferredDevice; // for newSurface
friend class SkNoPixelsBitmapDevice;
friend class SkSurface_Raster;

View File

@ -1,261 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkDeferredCanvas_DEFINED
#define SkDeferredCanvas_DEFINED
#include "SkCanvas.h"
#include "SkPixelRef.h"
class SkDeferredDevice;
class SkImage;
class SkSurface;
/** \class SkDeferredCanvas
Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred
drawing. The main difference between this class and SkPictureRecord (the
canvas provided by SkPicture) is that this is a full drop-in replacement
for SkCanvas, while SkPictureRecord only supports draw operations.
SkDeferredCanvas will transparently trigger the flushing of deferred
draw operations when an attempt is made to access the pixel data.
*/
class SK_API SkDeferredCanvas : public SkCanvas {
public:
class SK_API NotificationClient;
/** Construct a canvas with the specified surface to draw into.
This factory must be used for newImageSnapshot to work.
@param surface Specifies a surface for the canvas to draw into.
*/
static SkDeferredCanvas* Create(SkSurface* surface);
virtual ~SkDeferredCanvas();
/**
* Specify the surface to be used by this canvas. Calling setSurface will
* release the previously set surface or device. Takes a reference on the
* surface.
*
* @param surface The surface that the canvas will raw into
* @return The surface argument, for convenience.
*/
SkSurface* setSurface(SkSurface* surface);
/**
* Specify a NotificationClient to be used by this canvas. Calling
* setNotificationClient will release the previously set
* NotificationClient, if any. SkDeferredCanvas does not take ownership
* of the notification client. Therefore user code is resposible
* for its destruction. The notification client must be unregistered
* by calling setNotificationClient(NULL) if it is destroyed before
* this canvas.
* Note: Must be called after the device is set with setDevice.
*
* @param notificationClient interface for dispatching notifications
* @return The notificationClient argument, for convenience.
*/
NotificationClient* setNotificationClient(NotificationClient* notificationClient);
/**
* Enable or disable deferred drawing. When deferral is disabled,
* pending draw operations are immediately flushed and from then on,
* the SkDeferredCanvas behaves just like a regular SkCanvas.
* This method must not be called while the save/restore stack is in use.
* @param deferred true/false
*/
void setDeferredDrawing(bool deferred);
/**
* Returns true if deferred drawing is currenlty enabled.
*/
bool isDeferredDrawing() const;
/**
* Returns true if the canvas contains a fresh frame. A frame is
* considered fresh when its content do not depend on the contents
* of the previous frame. For example, if a canvas is cleared before
* drawing each frame, the frames will all be considered fresh.
* A frame is defined as the graphics image produced by as a result
* of all the canvas draws operation executed between two successive
* calls to isFreshFrame. The result of isFreshFrame is computed
* conservatively, so it may report false negatives.
*/
bool isFreshFrame() const;
/**
* Returns canvas's size.
*/
SkISize getCanvasSize() const;
/**
* Returns true if the canvas has recorded draw commands that have
* not yet been played back.
*/
bool hasPendingCommands() const;
/**
* Flushes pending draw commands, if any, and returns an image of the
* current state of the surface pixels up to this point. Subsequent
* changes to the surface (by drawing into its canvas) will not be
* reflected in this image. Will return NULL if the deferred canvas
* was not constructed from an SkSurface.
*/
SkImage* newImageSnapshot();
/**
* Specify the maximum number of bytes to be allocated for the purpose
* of recording draw commands to this canvas. The default limit, is
* 64MB.
* @param maxStorage The maximum number of bytes to be allocated.
*/
void setMaxRecordingStorage(size_t maxStorage);
/**
* Returns the number of bytes currently allocated for the purpose of
* recording draw commands.
*/
size_t storageAllocatedForRecording() const;
/**
* Attempt to reduce the storage allocated for recording by evicting
* cache resources.
* @param bytesToFree minimum number of bytes that should be attempted to
* be freed.
* @return number of bytes actually freed.
*/
size_t freeMemoryIfPossible(size_t bytesToFree);
/**
* Specifies the maximum size (in bytes) allowed for a given image to be
* rendered using the deferred canvas.
*/
void setBitmapSizeThreshold(size_t sizeThreshold);
size_t getBitmapSizeThreshold() const { return fBitmapSizeThreshold; }
/**
* Executes all pending commands without drawing
*/
void silentFlush();
SkDrawFilter* setDrawFilter(SkDrawFilter* filter) override;
protected:
void willSave() override;
SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
void willRestore() override;
void didConcat(const SkMatrix&) override;
void didSetMatrix(const SkMatrix&) override;
void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
const SkPaint&) override;
virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
const SkPaint&) override;
virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
SkScalar constY, const SkPaint&) override;
virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
const SkMatrix* matrix, const SkPaint&) override;
virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) override;
virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
const SkPoint texCoords[4], SkXfermode* xmode,
const SkPaint& paint) override;
void onDrawPaint(const SkPaint&) override;
void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
void onDrawRect(const SkRect&, const SkPaint&) override;
void onDrawOval(const SkRect&, const SkPaint&) override;
void onDrawRRect(const SkRRect&, const SkPaint&) override;
void onDrawPath(const SkPath&, const SkPaint&) override;
void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
SrcRectConstraint) override;
void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
const SkPaint*, SrcRectConstraint) override;
void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
const SkPaint*) override;
void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
const SkPaint*) override;
void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
void onDrawVertices(VertexMode vmode, int vertexCount,
const SkPoint vertices[], const SkPoint texs[],
const SkColor colors[], SkXfermode* xmode,
const uint16_t indices[], int indexCount,
const SkPaint&) override;
void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count,
SkXfermode::Mode, const SkRect* cullRect, const SkPaint*) override;
void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
void onClipRegion(const SkRegion&, SkRegion::Op) override;
void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
public:
class NotificationClient {
public:
virtual ~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() {}
/**
* Called after pending draw commands have been skipped, meaning
* that they were optimized-out because the canvas is cleared
* or completely overwritten by the command currently being recorded.
*/
virtual void skippedPendingDrawCommands() {}
};
protected:
SkCanvas* canvasForDrawIter() override;
SkDeferredDevice* getDeferredDevice() const;
private:
SkDeferredCanvas(SkDeferredDevice*);
void recordedDrawCommand();
SkCanvas* drawingCanvas() const;
SkCanvas* immediateCanvas() const;
bool isFullFrame(const SkRect*, const SkPaint*) const;
void validate() const;
void init();
int fSaveLevel;
int fFirstSaveLayerIndex;
size_t fBitmapSizeThreshold;
bool fDeferredDrawing;
mutable SkISize fCachedCanvasSize;
mutable bool fCachedCanvasSizeDirty;
friend class SkDeferredCanvasTester; // for unit testing
typedef SkCanvas INHERITED;
};
#endif

View File

@ -15,7 +15,6 @@
#include "SkCanvas.h"
#include "SkCommandLineFlags.h"
#include "SkData.h"
#include "SkDeferredCanvas.h"
#include "SkDevice.h"
#include "SkDocument.h"
#include "SkGPipe.h"
@ -188,7 +187,6 @@ public:
switch (win->getDeviceType()) {
case kRaster_DeviceType: // fallthrough
case kPicture_DeviceType: // fallthrough
case kDeferred_DeviceType: // fallthrough
case kGPU_DeviceType:
// all these guys use the native backend
fBackend = kNativeGL_BackEndType;
@ -216,7 +214,6 @@ public:
switch (win->getDeviceType()) {
case kRaster_DeviceType: // fallthrough
case kPicture_DeviceType: // fallthrough
case kDeferred_DeviceType: // fallthrough
case kGPU_DeviceType:
// all these guys use the native interface
glInterface.reset(GrGLCreateNativeInterface());
@ -674,7 +671,6 @@ static inline SampleWindow::DeviceType cycle_devicetype(SampleWindow::DeviceType
SampleWindow::kANGLE_DeviceType,
#endif // SK_ANGLE
#endif // SK_SUPPORT_GPU
SampleWindow::kDeferred_DeviceType,
SampleWindow::kRaster_DeviceType,
};
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gCT) == SampleWindow::kDeviceTypeCnt, array_size_mismatch);
@ -830,7 +826,7 @@ SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* dev
int itemID;
itemID =fAppMenu->appendList("Device Type", "Device Type", sinkID, 0,
"Raster", "Picture", "OpenGL", "Deferred",
"Raster", "Picture", "OpenGL",
#if SK_ANGLE
"ANGLE",
#endif
@ -1264,12 +1260,6 @@ SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
canvas = fPDFDocument->beginPage(this->width(), this->height());
} else if (kPicture_DeviceType == fDeviceType) {
canvas = fRecorder.beginRecording(9999, 9999, NULL, 0);
} else if (kDeferred_DeviceType == fDeviceType) {
fDeferredSurface.reset(canvas->newSurface(canvas->imageInfo()));
if (fDeferredSurface.get()) {
fDeferredCanvas.reset(SkDeferredCanvas::Create(fDeferredSurface));
canvas = fDeferredCanvas;
}
} else {
canvas = this->INHERITED::beforeChildren(canvas);
}
@ -1376,13 +1366,6 @@ void SampleWindow::afterChildren(SkCanvas* orig) {
} else {
picture->playback(orig);
}
} else if (kDeferred_DeviceType == fDeviceType) {
SkAutoTUnref<SkImage> image(fDeferredCanvas->newImageSnapshot());
if (image) {
orig->drawImage(image, 0, 0, NULL);
}
fDeferredCanvas.reset(NULL);
fDeferredSurface.reset(NULL);
}
// Do this after presentGL and other finishing, rather than in afterChild
@ -1949,7 +1932,6 @@ static const char* gDeviceTypePrefix[] = {
"angle: ",
#endif // SK_ANGLE
#endif // SK_SUPPORT_GPU
"deferred: ",
};
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypePrefix) == SampleWindow::kDeviceTypeCnt,
array_size_mismatch);

View File

@ -23,7 +23,6 @@ class GrRenderTarget;
class SkCanvas;
class SkData;
class SkDeferredCanvas;
class SkDocument;
class SkEvent;
class SkTypeface;
@ -41,7 +40,6 @@ public:
kANGLE_DeviceType,
#endif // SK_ANGLE
#endif // SK_SUPPORT_GPU
kDeferred_DeviceType,
kDeviceTypeCnt
};
@ -162,8 +160,6 @@ private:
int fCurrIndex;
SkPictureRecorder fRecorder;
SkAutoTDelete<SkSurface> fDeferredSurface;
SkAutoTDelete<SkDeferredCanvas> fDeferredCanvas;
SkAutoTDelete<SkCanvas> fFlagsFilterCanvas;
SkPath fClipPath;

View File

@ -209,9 +209,6 @@ static DrawType peek_op_and_size(SkWriter32* writer, size_t offset, uint32_t* si
#endif//SK_DEBUG
void SkPictureRecord::willRestore() {
// FIXME: SkDeferredCanvas needs to be refactored to respect
// save/restore balancing so that the following test can be
// turned on permanently.
#if 0
SkASSERT(fRestoreOffsetStack.count() > 1);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,6 @@
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkClipStack.h"
#include "SkDeferredCanvas.h"
#include "SkDevice.h"
#include "SkDocument.h"
#include "SkMatrix.h"
@ -72,12 +71,6 @@ static void createBitmap(SkBitmap* bm, SkColor color) {
bm->eraseColor(color);
}
static SkSurface* createSurface(SkColor color) {
SkSurface* surface = SkSurface::NewRasterN32Premul(kWidth, kHeight);
surface->getCanvas()->clear(color);
return surface;
}
///////////////////////////////////////////////////////////////////////////////
// Constants used by test steps
const SkPoint kTestPoints[] = {
@ -239,16 +232,8 @@ static void test_clipstack(skiatest::Reporter* reporter) {
static const char* const kDefaultAssertMessageFormat = "%s";
static const char* const kCanvasDrawAssertMessageFormat =
"Drawing test step %s with SkCanvas";
static const char* const kDeferredDrawAssertMessageFormat =
"Drawing test step %s with SkDeferredCanvas";
static const char* const kNWayDrawAssertMessageFormat =
"Drawing test step %s with SkNWayCanvas";
static const char* const kDeferredPreFlushAssertMessageFormat =
"test step %s, SkDeferredCanvas state consistency before flush";
static const char* const kDeferredPostFlushPlaybackAssertMessageFormat =
"test step %s, SkDeferredCanvas playback canvas state consistency after flush";
static const char* const kDeferredPostSilentFlushPlaybackAssertMessageFormat =
"test step %s, SkDeferredCanvas playback canvas state consistency after silent flush";
static const char* const kNWayStateAssertMessageFormat =
"test step %s, SkNWayCanvas state consistency";
static const char* const kNWayIndirect1StateAssertMessageFormat =
@ -586,49 +571,6 @@ static void TestPdfDevice(skiatest::Reporter* reporter,
REPORTER_ASSERT(reporter, doc->close());
}
// The following class groups static functions that need to access
// the privates members of SkDeferredCanvas
class SkDeferredCanvasTester {
public:
static void TestDeferredCanvasStateConsistency(
skiatest::Reporter* reporter,
const TestData& d,
CanvasTestStep* testStep,
const SkCanvas& referenceCanvas, bool silent) {
SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
SkAutoTUnref<SkDeferredCanvas> deferredCanvas(SkDeferredCanvas::Create(surface.get()));
testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat);
testStep->draw(deferredCanvas, d, reporter);
testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat);
AssertCanvasStatesEqual(reporter, d, deferredCanvas, &referenceCanvas, testStep);
if (silent) {
deferredCanvas->silentFlush();
} else {
deferredCanvas->flush();
}
testStep->setAssertMessageFormat(
silent ? kDeferredPostSilentFlushPlaybackAssertMessageFormat :
kDeferredPostFlushPlaybackAssertMessageFormat);
AssertCanvasStatesEqual(reporter, d, deferredCanvas->immediateCanvas(),
&referenceCanvas, testStep);
// Verified that deferred canvas state is not affected by flushing
// pending draw operations
// The following test code is commented out because it currently fails.
// Issue: http://code.google.com/p/skia/issues/detail?id=496
/*
testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat);
AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
testStep);
*/
}
};
// unused
static void TestNWayCanvasStateConsistency(
skiatest::Reporter* reporter,
@ -675,10 +617,6 @@ static void TestOverrideStateConsistency(skiatest::Reporter* reporter, const Tes
testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
testStep->draw(&referenceCanvas, d, reporter);
SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, d, testStep, referenceCanvas, false);
SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, d, testStep, referenceCanvas, true);
// The following test code is disabled because SkNWayCanvas does not
// report correct clipping and device bounds information
// Issue: http://code.google.com/p/skia/issues/detail?id=501

View File

@ -1,949 +0,0 @@
/*
* 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 "../src/image/SkImagePriv.h"
#include "../src/image/SkSurface_Base.h"
#include "SkBitmap.h"
#include "SkBitmapProcShader.h"
#include "SkDeferredCanvas.h"
#include "SkGradientShader.h"
#include "SkPath.h"
#include "SkShader.h"
#include "SkSurface.h"
#include "Test.h"
#include "sk_tool_utils.h"
#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
#else
class GrContextFactory;
#endif
static const int gWidth = 2;
static const int gHeight = 2;
static void create(SkBitmap* bm, SkColor color) {
bm->allocN32Pixels(gWidth, gHeight);
bm->eraseColor(color);
}
static SkSurface* createSurface(SkColor color) {
SkSurface* surface = SkSurface::NewRasterN32Premul(gWidth, gHeight);
surface->getCanvas()->clear(color);
return surface;
}
static SkPMColor read_pixel(SkSurface* surface, int x, int y) {
SkPMColor pixel = 0;
SkBitmap bitmap;
bitmap.installPixels(SkImageInfo::MakeN32Premul(1, 1), &pixel, 4);
SkCanvas canvas(bitmap);
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
surface->draw(&canvas, -SkIntToScalar(x), -SkIntToScalar(y), &paint);
return pixel;
}
class MockSurface : public SkSurface_Base {
public:
MockSurface(int width, int height) : SkSurface_Base(width, height, NULL) {
clearCounts();
fBitmap.allocN32Pixels(width, height);
}
SkCanvas* onNewCanvas() override {
return SkNEW_ARGS(SkCanvas, (fBitmap));
}
SkSurface* onNewSurface(const SkImageInfo&) override {
return NULL;
}
SkImage* onNewImageSnapshot(Budgeted) override {
return SkNewImageFromRasterBitmap(fBitmap, &this->props());
}
void onCopyOnWrite(ContentChangeMode mode) override {
if (mode == SkSurface::kDiscard_ContentChangeMode) {
fCOWDiscardCount++;
} else {
fCOWRetainCount++;
}
}
void onDiscard() override {
fDiscardCount++;
}
void clearCounts() {
fCOWDiscardCount = 0;
fCOWRetainCount = 0;
fDiscardCount = 0;
}
int fCOWDiscardCount;
int fCOWRetainCount;
int fDiscardCount;
SkBitmap fBitmap;
};
static void TestDeferredCanvasWritePixelsToSurface(skiatest::Reporter* reporter) {
SkAutoTUnref<MockSurface> surface(SkNEW_ARGS(MockSurface, (10, 10)));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
SkBitmap srcBitmap;
srcBitmap.allocPixels(SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
srcBitmap.eraseColor(SK_ColorGREEN);
// Tests below depend on this bitmap being recognized as opaque
// Preliminary sanity check: no copy on write if no active snapshot
// Discard notification happens on SkSurface::onDiscard, since no
// active snapshot.
surface->clearCounts();
canvas->clear(SK_ColorWHITE);
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->flush();
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
// Case 1: Discard notification happens upon flushing
// with an Image attached.
surface->clearCounts();
SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->clear(SK_ColorWHITE);
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->flush();
REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
// Case 2: Opaque writePixels
surface->clearCounts();
SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
// Case 3: writePixels that partially covers the canvas
surface->clearCounts();
SkAutoTUnref<SkImage> image3(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
// Case 4: unpremultiplied opaque writePixels that entirely
// covers the canvas
surface->clearCounts();
SkAutoTUnref<SkImage> image4(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->writePixels(srcBitmap, 0, 0);
REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->flush();
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
// Case 5: unpremultiplied opaque writePixels that partially
// covers the canvas
surface->clearCounts();
SkAutoTUnref<SkImage> image5(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->writePixels(srcBitmap, 5, 0);
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 1 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->flush();
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
// Case 6: unpremultiplied opaque writePixels that entirely
// covers the canvas, preceded by clear
surface->clearCounts();
SkAutoTUnref<SkImage> image6(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->clear(SK_ColorWHITE);
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->writePixels(srcBitmap, 0, 0);
REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->flush();
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
// Case 7: unpremultiplied opaque writePixels that partially
// covers the canvas, preceeded by a clear
surface->clearCounts();
SkAutoTUnref<SkImage> image7(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->clear(SK_ColorWHITE);
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->writePixels(srcBitmap, 5, 0);
REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount); // because of the clear
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->flush();
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
// Case 8: unpremultiplied opaque writePixels that partially
// covers the canvas, preceeded by a drawREct that partially
// covers the canvas
surface->clearCounts();
SkAutoTUnref<SkImage> image8(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
SkPaint paint;
canvas->drawRect(SkRect::MakeLTRB(0, 0, 5, 5), paint);
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->writePixels(srcBitmap, 5, 0);
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 1 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
surface->clearCounts();
canvas->flush();
REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
}
static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
canvas->clear(0x00000000);
// verify that clear was deferred
REPORTER_ASSERT(reporter, 0xFFFFFFFF == read_pixel(surface, 0, 0));
canvas->flush();
// verify that clear was executed
REPORTER_ASSERT(reporter, 0 == read_pixel(surface, 0, 0));
}
static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
SkRect fullRect;
fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth),
SkIntToScalar(gHeight));
SkRect partialRect;
partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0),
SkIntToScalar(1), SkIntToScalar(1));
SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
// verify that frame is intially fresh
REPORTER_ASSERT(reporter, canvas->isFreshFrame());
// no clearing op since last call to isFreshFrame -> not fresh
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
// Verify that clear triggers a fresh frame
canvas->clear(0x00000000);
REPORTER_ASSERT(reporter, canvas->isFreshFrame());
// Verify that clear with saved state triggers a fresh frame
canvas->save();
canvas->clear(0x00000000);
canvas->restore();
REPORTER_ASSERT(reporter, canvas->isFreshFrame());
// Verify that clear within a layer does NOT trigger a fresh frame
canvas->saveLayer(NULL, NULL);
canvas->clear(0x00000000);
canvas->restore();
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
// Verify that full frame rects with different forms of opaque paint
// trigger frames to be marked as fresh
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(255);
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, canvas->isFreshFrame());
}
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(255);
paint.setXfermodeMode(SkXfermode::kSrcIn_Mode);
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
SkBitmap bmp;
create(&bmp, 0xFFFFFFFF);
bmp.setAlphaType(kOpaque_SkAlphaType);
SkShader* shader = SkShader::CreateBitmapShader(bmp,
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
paint.setShader(shader)->unref();
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, canvas->isFreshFrame());
}
// Verify that full frame rects with different forms of non-opaque paint
// do not trigger frames to be marked as fresh
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(254);
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
// Defining a cone that partially overlaps the canvas
const SkPoint pt1 = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(0));
const SkScalar r1 = SkIntToScalar(1);
const SkPoint pt2 = SkPoint::Make(SkIntToScalar(10), SkIntToScalar(0));
const SkScalar r2 = SkIntToScalar(5);
const SkColor colors[2] = {SK_ColorWHITE, SK_ColorWHITE};
const SkScalar pos[2] = {0, SK_Scalar1};
SkShader* shader = SkGradientShader::CreateTwoPointConical(
pt1, r1, pt2, r2, colors, pos, 2, SkShader::kClamp_TileMode);
paint.setShader(shader)->unref();
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
SkBitmap bmp;
create(&bmp, 0xFFFFFFFF);
bmp.setAlphaType(kPremul_SkAlphaType);
SkShader* shader = SkShader::CreateBitmapShader(bmp,
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
paint.setShader(shader)->unref();
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
// Verify that incomplete coverage does not trigger a fresh frame
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(255);
canvas->drawRect(partialRect, paint);
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
// Verify that incomplete coverage due to clipping does not trigger a fresh
// frame
{
canvas->save();
canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false);
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(255);
canvas->drawRect(fullRect, paint);
canvas->restore();
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
{
canvas->save();
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(255);
SkPath path;
path.addCircle(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2));
canvas->clipPath(path, SkRegion::kIntersect_Op, false);
canvas->drawRect(fullRect, paint);
canvas->restore();
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
// Verify that stroked rect does not trigger a fresh frame
{
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setAlpha(255);
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
}
// Verify kSrcMode triggers a fresh frame even with transparent color
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(100);
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
canvas->drawRect(fullRect, paint);
REPORTER_ASSERT(reporter, canvas->isFreshFrame());
}
}
class NotificationCounter : public SkDeferredCanvas::NotificationClient {
public:
NotificationCounter() {
fPrepareForDrawCount = fStorageAllocatedChangedCount =
fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0;
}
void prepareForDraw() override {
fPrepareForDrawCount++;
}
void storageAllocatedForRecordingChanged(size_t) override {
fStorageAllocatedChangedCount++;
}
void flushedDrawCommands() override {
fFlushedDrawCommandsCount++;
}
void skippedPendingDrawCommands() override {
fSkippedPendingDrawCommandsCount++;
}
int fPrepareForDrawCount;
int fStorageAllocatedChangedCount;
int fFlushedDrawCommandsCount;
int fSkippedPendingDrawCommandsCount;
private:
typedef SkDeferredCanvas::NotificationClient INHERITED;
};
// Verifies that the deferred canvas triggers a flush when its memory
// limit is exceeded
static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
NotificationCounter notificationCounter;
canvas->setNotificationClient(&notificationCounter);
canvas->setMaxRecordingStorage(160000);
SkBitmap sourceImage;
// 100 by 100 image, takes 40,000 bytes in memory
sourceImage.allocN32Pixels(100, 100);
sourceImage.eraseColor(SK_ColorGREEN);
for (int i = 0; i < 5; i++) {
sourceImage.notifyPixelsChanged(); // to force re-serialization
canvas->drawBitmap(sourceImage, 0, 0, NULL);
}
REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
}
static void TestDeferredCanvasSilentFlush(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(createSurface(0));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
NotificationCounter notificationCounter;
canvas->setNotificationClient(&notificationCounter);
canvas->silentFlush(); // will skip the initial clear that was recorded in createSurface
REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
}
static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
NotificationCounter notificationCounter;
canvas->setNotificationClient(&notificationCounter);
const int imageCount = 2;
SkBitmap sourceImages[imageCount];
for (int i = 0; i < imageCount; i++) {
sourceImages[i].allocN32Pixels(100, 100);
sourceImages[i].eraseColor(SK_ColorGREEN);
}
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);
// verify that nothing can be freed at this point
REPORTER_ASSERT(reporter, 0 == canvas->freeMemoryIfPossible(~0U));
// 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
REPORTER_ASSERT(reporter, canvas->freeMemoryIfPossible(~0U) >= bitmapSize);
// 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(~0U);
REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
canvas->flush();
canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
bytesFreed = canvas->freeMemoryIfPossible(~0U);
// only one bitmap should have been freed.
REPORTER_ASSERT(reporter, bytesFreed >= bitmapSize);
REPORTER_ASSERT(reporter, bytesFreed < 2*bitmapSize);
// Clear for next test
canvas->flush();
canvas->freeMemoryIfPossible(~0U);
REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < bitmapSize);
// Verify the image cache is sensitive to genID bumps
canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
sourceImages[1].notifyPixelsChanged();
canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2*bitmapSize);
// Verify that nothing in this test caused commands to be skipped
REPORTER_ASSERT(reporter, 0 == notificationCounter.fSkippedPendingDrawCommandsCount);
}
static void TestDeferredCanvasSkip(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
NotificationCounter notificationCounter;
canvas->setNotificationClient(&notificationCounter);
canvas->clear(0x0);
REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
canvas->flush();
REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
}
static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) {
// This is a regression test for crbug.com/155875
// This test covers a code path that inserts bitmaps into the bitmap heap through the
// flattening of SkBitmapProcShaders. The refcount in the bitmap heap is maintained through
// the flattening and unflattening of the shader.
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
// test will fail if nbIterations is not in sync with
// BITMAPS_TO_KEEP in SkGPipeWrite.cpp
const int nbIterations = 5;
size_t bytesAllocated = 0;
for(int pass = 0; pass < 2; ++pass) {
for(int i = 0; i < nbIterations; ++i) {
SkPaint paint;
SkBitmap paintPattern;
paintPattern.allocN32Pixels(10, 10);
paintPattern.eraseColor(SK_ColorGREEN);
paint.setShader(SkNEW_ARGS(SkBitmapProcShader,
(paintPattern, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)))->unref();
canvas->drawPaint(paint);
canvas->flush();
// In the first pass, memory allocation should be monotonically increasing as
// the bitmap heap slots fill up. In the second pass memory allocation should be
// stable as bitmap heap slots get recycled.
size_t newBytesAllocated = canvas->storageAllocatedForRecording();
if (pass == 0) {
REPORTER_ASSERT(reporter, newBytesAllocated > bytesAllocated);
bytesAllocated = newBytesAllocated;
} else {
REPORTER_ASSERT(reporter, newBytesAllocated == bytesAllocated);
}
}
}
// All cached resources should be evictable since last canvas call was flush()
canvas->freeMemoryIfPossible(~0U);
REPORTER_ASSERT(reporter, 0 == canvas->storageAllocatedForRecording());
}
static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
SkBitmap sourceImage;
// 100 by 100 image, takes 40,000 bytes in memory
sourceImage.allocN32Pixels(100, 100);
sourceImage.eraseColor(SK_ColorGREEN);
// 1 under : should not store the image
{
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
canvas->setBitmapSizeThreshold(39999);
canvas->drawBitmap(sourceImage, 0, 0, NULL);
size_t newBytesAllocated = canvas->storageAllocatedForRecording();
REPORTER_ASSERT(reporter, newBytesAllocated == 0);
}
// exact value : should store the image
{
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
canvas->setBitmapSizeThreshold(40000);
canvas->drawBitmap(sourceImage, 0, 0, NULL);
size_t newBytesAllocated = canvas->storageAllocatedForRecording();
REPORTER_ASSERT(reporter, newBytesAllocated > 0);
}
// 1 over : should still store the image
{
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
canvas->setBitmapSizeThreshold(40001);
canvas->drawBitmap(sourceImage, 0, 0, NULL);
size_t newBytesAllocated = canvas->storageAllocatedForRecording();
REPORTER_ASSERT(reporter, newBytesAllocated > 0);
}
}
static void TestDeferredCanvasImageFreeAfterFlush(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRasterN32Premul(100, 100));
SkAutoTUnref<SkImage> sourceImage(sourceSurface->newImageSnapshot());
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
canvas->drawImage(sourceImage, 0, 0, NULL);
size_t newBytesAllocated = canvas->storageAllocatedForRecording();
REPORTER_ASSERT(reporter, newBytesAllocated > 0);
canvas->flush();
newBytesAllocated = canvas->storageAllocatedForRecording();
REPORTER_ASSERT(reporter, newBytesAllocated == 0);
}
typedef const void* PixelPtr;
// Returns an opaque pointer which, either points to a GrTexture or RAM pixel
// buffer. Used to test pointer equality do determine whether a surface points
// to the same pixel data storage as before.
static PixelPtr get_surface_ptr(SkSurface* surface, bool useGpu) {
#if SK_SUPPORT_GPU
if (useGpu) {
return surface->getCanvas()->internal_private_accessTopLayerRenderTarget()->asTexture();
} else
#endif
{
return surface->peekPixels(NULL, NULL);
}
}
static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFactory* factory) {
SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10);
bool useGpu = SkToBool(factory);
int cnt;
#if SK_SUPPORT_GPU
if (useGpu) {
cnt = GrContextFactory::kGLContextTypeCnt;
} else {
cnt = 1;
}
#else
SkASSERT(!useGpu);
cnt = 1;
#endif
for (int i = 0; i < cnt; ++i) {
SkSurface* surface;
#if SK_SUPPORT_GPU
if (useGpu) {
GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
continue;
}
GrContext* context = factory->get(glCtxType);
if (NULL == context) {
return;
}
surface =
SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL);
} else
#endif
{
surface = SkSurface::NewRaster(imageSpec);
}
SkASSERT(surface);
SkAutoTUnref<SkSurface> aur(surface);
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface));
SkImage* image1 = canvas->newImageSnapshot();
SkAutoTUnref<SkImage> aur_i1(image1);
PixelPtr pixels1 = get_surface_ptr(surface, useGpu);
// The following clear would normally trigger a copy on write, but
// it won't because rendering is deferred.
canvas->clear(SK_ColorBLACK);
// Obtaining a snapshot directly from the surface (as opposed to the
// SkDeferredCanvas) will not trigger a flush of deferred draw operations
// and will therefore return the same image as the previous snapshot.
SkImage* image2 = surface->newImageSnapshot();
SkAutoTUnref<SkImage> aur_i2(image2);
// Images identical because of deferral
REPORTER_ASSERT(reporter, image1->uniqueID() == image2->uniqueID());
// Now we obtain a snpshot via the deferred canvas, which triggers a flush.
// Because there is a pending clear, this will generate a different image.
SkImage* image3 = canvas->newImageSnapshot();
SkAutoTUnref<SkImage> aur_i3(image3);
REPORTER_ASSERT(reporter, image1->uniqueID() != image3->uniqueID());
// Verify that backing store is now a different buffer because of copy on
// write
PixelPtr pixels2 = get_surface_ptr(surface, useGpu);
REPORTER_ASSERT(reporter, pixels1 != pixels2);
// Verify copy-on write with a draw operation that gets deferred by
// the in order draw buffer.
SkPaint paint;
canvas->drawPaint(paint);
SkImage* image4 = canvas->newImageSnapshot(); // implicit flush
SkAutoTUnref<SkImage> aur_i4(image4);
REPORTER_ASSERT(reporter, image4->uniqueID() != image3->uniqueID());
PixelPtr pixels3 = get_surface_ptr(surface, useGpu);
REPORTER_ASSERT(reporter, pixels2 != pixels3);
// Verify that a direct canvas flush with a pending draw does not trigger
// a copy on write when the surface is not sharing its buffer with an
// SkImage.
canvas->clear(SK_ColorWHITE);
canvas->flush();
PixelPtr pixels4 = get_surface_ptr(surface, useGpu);
canvas->drawPaint(paint);
canvas->flush();
PixelPtr pixels5 = get_surface_ptr(surface, useGpu);
REPORTER_ASSERT(reporter, pixels4 == pixels5);
}
}
static void TestDeferredCanvasSetSurface(skiatest::Reporter* reporter, GrContextFactory* factory) {
SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10);
SkSurface* surface;
SkSurface* alternateSurface;
bool useGpu = SkToBool(factory);
int cnt;
#if SK_SUPPORT_GPU
if (useGpu) {
cnt = GrContextFactory::kGLContextTypeCnt;
} else {
cnt = 1;
}
#else
SkASSERT(!useGpu);
cnt = 1;
#endif
for (int i = 0; i < cnt; ++i) {
#if SK_SUPPORT_GPU
if (useGpu) {
GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
continue;
}
GrContext* context = factory->get(glCtxType);
if (NULL == context) {
continue;
}
surface =
SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL);
alternateSurface =
SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL);
} else
#endif
{
surface = SkSurface::NewRaster(imageSpec);
alternateSurface = SkSurface::NewRaster(imageSpec);
}
SkASSERT(surface);
SkASSERT(alternateSurface);
SkAutoTUnref<SkSurface> aur1(surface);
SkAutoTUnref<SkSurface> aur2(alternateSurface);
PixelPtr pixels1 = get_surface_ptr(surface, useGpu);
PixelPtr pixels2 = get_surface_ptr(alternateSurface, useGpu);
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface));
SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot());
canvas->setSurface(alternateSurface);
SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot());
REPORTER_ASSERT(reporter, image1->uniqueID() != image2->uniqueID());
// Verify that none of the above operations triggered a surface copy on write.
REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1);
REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) == pixels2);
// Verify that a flushed draw command will trigger a copy on write on alternateSurface.
canvas->clear(SK_ColorWHITE);
canvas->flush();
REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1);
REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) != pixels2);
}
}
static void TestDeferredCanvasCreateCompatibleDevice(skiatest::Reporter* reporter) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
NotificationCounter notificationCounter;
canvas->setNotificationClient(&notificationCounter);
SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
SkAutoTUnref<SkSurface> secondarySurface(canvas->newSurface(info));
SkRect rect = SkRect::MakeWH(5, 5);
SkPaint paint;
// After spawning a compatible canvas:
// 1) Verify that secondary canvas is usable and does not report to the notification client.
surface->getCanvas()->drawRect(rect, paint);
REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 0);
// 2) Verify that original canvas is usable and still reports to the notification client.
canvas->drawRect(rect, paint);
REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 1);
}
static void TestDeferredCanvasGetCanvasSize(skiatest::Reporter* reporter) {
SkRect rect;
rect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), SkIntToScalar(gHeight));
SkRect clip;
clip.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(1), SkIntToScalar(1));
SkPaint paint;
SkISize size = SkISize::Make(gWidth, gHeight);
SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
SkSurface* newSurface = SkSurface::NewRasterN32Premul(4, 4);
SkAutoTUnref<SkSurface> aur(newSurface);
for (int i = 0; i < 2; ++i) {
if (i == 1) {
canvas->setSurface(newSurface);
size = SkISize::Make(4, 4);
}
// verify that canvas size is correctly initialized or set
REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
// Verify that clear, clip and draw the canvas will not change its size
canvas->clear(0x00000000);
canvas->clipRect(clip, SkRegion::kIntersect_Op, false);
canvas->drawRect(rect, paint);
REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
// Verify that flush the canvas will not change its size
canvas->flush();
REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
// Verify that clear canvas with saved state will not change its size
canvas->save();
canvas->clear(0xFFFFFFFF);
REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
// Verify that restore canvas state will not change its size
canvas->restore();
REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
// Verify that clear within a layer will not change canvas size
canvas->saveLayer(&clip, &paint);
canvas->clear(0x00000000);
REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
// Verify that restore from a layer will not change canvas size
canvas->restore();
REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
}
}
DEF_TEST(DeferredCanvas_CPU, reporter) {
TestDeferredCanvasFlush(reporter);
TestDeferredCanvasSilentFlush(reporter);
TestDeferredCanvasFreshFrame(reporter);
TestDeferredCanvasMemoryLimit(reporter);
TestDeferredCanvasBitmapCaching(reporter);
TestDeferredCanvasSkip(reporter);
TestDeferredCanvasBitmapShaderNoLeak(reporter);
TestDeferredCanvasBitmapSizeThreshold(reporter);
TestDeferredCanvasImageFreeAfterFlush(reporter);
TestDeferredCanvasCreateCompatibleDevice(reporter);
TestDeferredCanvasWritePixelsToSurface(reporter);
TestDeferredCanvasGetCanvasSize(reporter);
TestDeferredCanvasSurface(reporter, NULL);
TestDeferredCanvasSetSurface(reporter, NULL);
}
DEF_GPUTEST(DeferredCanvas_GPU, reporter, factory) {
if (factory != NULL) {
TestDeferredCanvasSurface(reporter, factory);
TestDeferredCanvasSetSurface(reporter, factory);
}
}