From 67d74228448cab0f1eca77fff444ddbe8c2eaef3 Mon Sep 17 00:00:00 2001 From: "junov@chromium.org" Date: Fri, 12 Apr 2013 13:33:01 +0000 Subject: [PATCH] Adding SkSurface support to SkDeferredCanvas Review URL: https://codereview.chromium.org/14178002 git-svn-id: http://skia.googlecode.com/svn/trunk@8648 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/utils/SkDeferredCanvas.h | 17 ++++++++ src/utils/SkDeferredCanvas.cpp | 74 +++++++++++++++++++++++++++-------- tests/DeferredCanvasTest.cpp | 83 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 156 insertions(+), 18 deletions(-) diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h index 53bea82..97b4a66 100644 --- a/include/utils/SkDeferredCanvas.h +++ b/include/utils/SkDeferredCanvas.h @@ -12,6 +12,8 @@ #include "SkPixelRef.h" class DeferredDevice; +class SkImage; +class SkSurface; /** \class SkDeferredCanvas Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred @@ -33,6 +35,12 @@ public: */ explicit SkDeferredCanvas(SkDevice* device); + /** Construct a canvas with the specified surface to draw into. + This constructor must be used for newImageSnapshot to work. + @param surface Specifies a surface for the canvas to draw into. + */ + explicit SkDeferredCanvas(SkSurface* surface); + virtual ~SkDeferredCanvas(); /** @@ -93,6 +101,15 @@ public: 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* newImageShapshot(); + + /** * Specify the maximum number of bytes to be allocated for the purpose * of recording draw commands to this canvas. The default limit, is * 64MB. diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp index 5119729..21f717c 100644 --- a/src/utils/SkDeferredCanvas.cpp +++ b/src/utils/SkDeferredCanvas.cpp @@ -17,6 +17,7 @@ #include "SkPaintPriv.h" #include "SkRRect.h" #include "SkShader.h" +#include "SkSurface.h" enum { // Deferred canvas will auto-flush when recording reaches this limit @@ -138,14 +139,15 @@ void DeferredPipeController::playback(bool silent) { //----------------------------------------------------------------------------- class DeferredDevice : public SkDevice { public: - DeferredDevice(SkDevice* immediateDevice, - SkDeferredCanvas::NotificationClient* notificationClient = NULL); + explicit DeferredDevice(SkDevice* immediateDevice); + explicit DeferredDevice(SkSurface* surface); ~DeferredDevice(); void setNotificationClient(SkDeferredCanvas::NotificationClient* notificationClient); SkCanvas* recordingCanvas(); SkCanvas* immediateCanvas() const {return fImmediateCanvas;} SkDevice* immediateDevice() const {return fImmediateDevice;} + SkImage* newImageShapshot(); bool isFreshFrame(); bool hasPendingCommands(); size_t storageAllocatedForRecording() const; @@ -237,12 +239,14 @@ private: virtual void flush(); void beginRecording(); + void init(); DeferredPipeController fPipeController; SkGPipeWriter fPipeWriter; SkDevice* fImmediateDevice; SkCanvas* fImmediateCanvas; SkCanvas* fRecordingCanvas; + SkSurface* fSurface; SkDeferredCanvas::NotificationClient* fNotificationClient; bool fFreshFrame; size_t fMaxRecordingStorageBytes; @@ -250,21 +254,40 @@ private: size_t fBitmapSizeThreshold; }; -DeferredDevice::DeferredDevice( - SkDevice* immediateDevice, SkDeferredCanvas::NotificationClient* notificationClient) : - SkDevice(SkBitmap::kNo_Config, - immediateDevice->width(), immediateDevice->height(), - immediateDevice->isOpaque(), - immediateDevice->getDeviceProperties()) - , fRecordingCanvas(NULL) - , fFreshFrame(true) - , fPreviousStorageAllocated(0) - , fBitmapSizeThreshold(kDeferredCanvasBitmapSizeThreshold){ +DeferredDevice::DeferredDevice(SkDevice* immediateDevice) + : SkDevice(SkBitmap::kNo_Config, + immediateDevice->width(), immediateDevice->height(), + immediateDevice->isOpaque(), + immediateDevice->getDeviceProperties()) { + fSurface = NULL; + fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas + fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice)); + this->init(); +} +DeferredDevice::DeferredDevice(SkSurface* surface) + : SkDevice(SkBitmap::kNo_Config, + surface->getCanvas()->getDevice()->width(), + surface->getCanvas()->getDevice()->height(), + surface->getCanvas()->getDevice()->isOpaque(), + surface->getCanvas()->getDevice()->getDeviceProperties()) { fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; - fNotificationClient = notificationClient; - fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas - fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice)); + fNotificationClient = NULL; + fImmediateCanvas = surface->getCanvas(); + SkSafeRef(fImmediateCanvas); + fSurface = surface; + SkSafeRef(fSurface); + fImmediateDevice = fImmediateCanvas->getDevice(); // ref counted via fImmediateCanvas + this->init(); +} + +void DeferredDevice::init() { + fRecordingCanvas = NULL; + fFreshFrame = true; + fPreviousStorageAllocated = 0; + fBitmapSizeThreshold = kDeferredCanvasBitmapSizeThreshold; + fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; + fNotificationClient = NULL; fPipeController.setPlaybackCanvas(fImmediateCanvas); this->beginRecording(); } @@ -272,6 +295,7 @@ DeferredDevice::DeferredDevice( DeferredDevice::~DeferredDevice() { this->flushPendingCommands(kSilent_PlaybackMode); SkSafeUnref(fImmediateCanvas); + SkSafeUnref(fSurface); } void DeferredDevice::setMaxRecordingStorage(size_t maxStorage) { @@ -376,6 +400,11 @@ SkCanvas* DeferredDevice::recordingCanvas() { return fRecordingCanvas; } +SkImage* DeferredDevice::newImageShapshot() { + this->flush(); + return fSurface ? fSurface->newImageShapshot() : NULL; +} + uint32_t DeferredDevice::getDeviceCapabilities() { return fImmediateDevice->getDeviceCapabilities(); } @@ -437,7 +466,9 @@ SkDevice* DeferredDevice::onCreateCompatibleDevice( SkAutoTUnref compatibleDevice (fImmediateDevice->createCompatibleDevice(config, width, height, isOpaque)); - return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fNotificationClient)); + DeferredDevice* device = SkNEW_ARGS(DeferredDevice, (compatibleDevice)); + device->setNotificationClient(fNotificationClient); + return device; } bool DeferredDevice::onReadPixels( @@ -488,6 +519,11 @@ SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) { this->setDevice(device); } +SkDeferredCanvas::SkDeferredCanvas(SkSurface* surface) { + this->init(); + this->INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (surface)))->unref(); +} + void SkDeferredCanvas::init() { fDeferredDrawing = true; // On by default } @@ -584,6 +620,12 @@ SkDeferredCanvas::NotificationClient* SkDeferredCanvas::setNotificationClient( return notificationClient; } +SkImage* SkDeferredCanvas::newImageShapshot() { + DeferredDevice* deferredDevice = this->getDeferredDevice(); + SkASSERT(deferredDevice); + return deferredDevice ? deferredDevice->newImageShapshot() : NULL; +} + bool SkDeferredCanvas::isFullFrame(const SkRect* rect, const SkPaint* paint) const { SkCanvas* canvas = this->drawingCanvas(); diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp index a95e09c..37e6721 100644 --- a/tests/DeferredCanvasTest.cpp +++ b/tests/DeferredCanvasTest.cpp @@ -12,6 +12,12 @@ #include "SkDevice.h" #include "SkGradientShader.h" #include "SkShader.h" +#include "SkSurface.h" +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" +#else +class GrContextFactory; +#endif static const int gWidth = 2; static const int gHeight = 2; @@ -465,7 +471,75 @@ static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) } } -static void TestDeferredCanvas(skiatest::Reporter* reporter) { + +typedef 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. +PixelPtr getSurfacePixelPtr(SkSurface* surface, bool useGpu) { + return useGpu ? surface->getCanvas()->getDevice()->accessBitmap(false).getTexture() : + surface->getCanvas()->getDevice()->accessBitmap(false).getPixels(); +} + +static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFactory* factory) { + SkImage::Info imageSpec = { + 10, // width + 10, // height + SkImage::kPMColor_ColorType, + SkImage::kPremul_AlphaType + }; + SkSurface* surface; + bool useGpu = NULL != factory; +#if SK_SUPPORT_GPU + if (useGpu) { + GrContext* context = factory->get(GrContextFactory::kNative_GLContextType); + surface = SkSurface::NewRenderTarget(context, imageSpec); + } else { + surface = SkSurface::NewRaster(imageSpec); + } +#else + SkASSERT(!useGpu); + surface = SkSurface::NewRaster(imageSpec); +#endif + SkASSERT(NULL != surface); + SkAutoTUnref aur(surface); + SkDeferredCanvas canvas(surface); + + SkImage* image1 = canvas.newImageShapshot(); + SkAutoTUnref aur_i1(image1); + PixelPtr pixels1 = getSurfacePixelPtr(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->newImageShapshot(); + SkAutoTUnref 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.newImageShapshot(); + SkAutoTUnref 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 = getSurfacePixelPtr(surface, useGpu); + REPORTER_ASSERT(reporter, pixels1 != pixels2); + canvas.clear(SK_ColorWHITE); + canvas.flush(); + PixelPtr pixels3 = getSurfacePixelPtr(surface, useGpu); + // 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_ColorBLACK); + canvas.flush(); + PixelPtr pixels4 = getSurfacePixelPtr(surface, useGpu); + REPORTER_ASSERT(reporter, pixels3 == pixels4); +} + +static void TestDeferredCanvas(skiatest::Reporter* reporter, GrContextFactory* factory) { TestDeferredCanvasBitmapAccess(reporter); TestDeferredCanvasFlush(reporter); TestDeferredCanvasFreshFrame(reporter); @@ -474,7 +548,12 @@ static void TestDeferredCanvas(skiatest::Reporter* reporter) { TestDeferredCanvasSkip(reporter); TestDeferredCanvasBitmapShaderNoLeak(reporter); TestDeferredCanvasBitmapSizeThreshold(reporter); + TestDeferredCanvasSurface(reporter, NULL); + if (NULL != factory) { + TestDeferredCanvasSurface(reporter, factory); + } } #include "TestClassDef.h" -DEFINE_TESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas) +DEFINE_GPUTESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas) + -- 2.7.4