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:
parent
da04e0e80a
commit
451af5062e
@ -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); )
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
@ -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
|
||||
|
@ -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(¬ificationCounter);
|
||||
|
||||
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(¬ificationCounter);
|
||||
|
||||
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(¬ificationCounter);
|
||||
|
||||
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(¬ificationCounter);
|
||||
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(¬ificationCounter);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user