diff options
-rw-r--r-- | include/utils/SkDeferredCanvas.h | 13 | ||||
-rw-r--r-- | src/utils/SkDeferredCanvas.cpp | 38 | ||||
-rw-r--r-- | tests/DeferredCanvasTest.cpp | 45 |
3 files changed, 87 insertions, 9 deletions
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h index 38ee678152..5f4e992622 100644 --- a/include/utils/SkDeferredCanvas.h +++ b/include/utils/SkDeferredCanvas.h @@ -85,6 +85,14 @@ public: */ void setDeferredDrawing(bool deferred); + /** + * 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); + // Overrides of the SkCanvas interface virtual int save(SaveFlags flags) SK_OVERRIDE; virtual int saveLayer(const SkRect* bounds, const SkPaint* paint, @@ -171,6 +179,7 @@ protected: void playback(); void reset(); bool hasRecorded() {return fAllocator.blockCount() != 0;} + size_t storageAllocatedForRecording() {return fAllocator.totalCapacity();} private: enum { kMinBlockSize = 4096 @@ -212,7 +221,7 @@ public: /** * Returns the recording canvas. */ - SkCanvas* recordingCanvas() const {return fRecordingCanvas;} + SkCanvas* recordingCanvas(); /** * Returns the immediate (non deferred) canvas. @@ -233,6 +242,7 @@ public: void flushPending(); void contentsCleared(); void flushIfNeeded(const SkBitmap& bitmap); + void setMaxRecordingStorage(size_t); virtual uint32_t getDeviceCapabilities() SK_OVERRIDE; virtual int width() const SK_OVERRIDE; @@ -330,6 +340,7 @@ public: SkCanvas* fRecordingCanvas; DeviceContext* fDeviceContext; bool fFreshFrame; + size_t fMaxRecordingStorageBytes; }; DeferredDevice* getDeferredDevice() const; diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp index 568f9a79a4..728e3d39d3 100644 --- a/src/utils/SkDeferredCanvas.cpp +++ b/src/utils/SkDeferredCanvas.cpp @@ -15,6 +15,11 @@ SK_DEFINE_INST_COUNT(SkDeferredCanvas::DeviceContext) +enum { + // Deferred canvas will auto-flush when recording reaches this limit + kDefaultMaxRecordingStorageBytes = 64*1024*1024, +}; + namespace { bool isPaintOpaque(const SkPaint* paint, @@ -100,6 +105,11 @@ void SkDeferredCanvas::init() { fDeferredDrawing = true; // On by default } +void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) { + validate(); + this->getDeferredDevice()->setMaxRecordingStorage(maxStorage); +} + void SkDeferredCanvas::validate() const { SkASSERT(getDevice()); } @@ -482,7 +492,7 @@ void SkDeferredCanvas::DeferredPipeController::playback() { fBlockList.reset(); if (fBlock) { - fReader.playback(fBlock,fBytesWritten); + fReader.playback(fBlock, fBytesWritten); fBlock = NULL; } @@ -507,6 +517,7 @@ SkDeferredCanvas::DeferredDevice::DeferredDevice( immediateDevice->height(), immediateDevice->isOpaque()) , fFreshFrame(true) { + fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; fDeviceContext = deviceContext; SkSafeRef(fDeviceContext); fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas @@ -522,6 +533,10 @@ SkDeferredCanvas::DeferredDevice::~DeferredDevice() { SkSafeUnref(fDeviceContext); } +void SkDeferredCanvas::DeferredDevice::setMaxRecordingStorage(size_t maxStorage) { + fMaxRecordingStorageBytes = maxStorage; + recordingCanvas(); // Accessing the recording canvas applies the new limit. +} void SkDeferredCanvas::DeferredDevice::endRecording() { #if SK_DEFERRED_CANVAS_USES_GPIPE @@ -613,10 +628,19 @@ void SkDeferredCanvas::DeferredDevice::flushPending() { } void SkDeferredCanvas::DeferredDevice::flush() { - flushPending(); + this->flushPending(); fImmediateCanvas->flush(); } +SkCanvas* SkDeferredCanvas::DeferredDevice::recordingCanvas() { +#if SK_DEFERRED_CANVAS_USES_GPIPE + if (fPipeController.storageAllocatedForRecording() > fMaxRecordingStorageBytes) { + this->flushPending(); + } +#endif + return fRecordingCanvas; +} + void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) { #if SK_DEFERRED_CANVAS_USES_GPIPE if (bitmap.isImmutable()) { @@ -632,7 +656,7 @@ void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) { // For now, drawing a writable bitmap triggers a flush // TODO: implement read-only semantics and auto buffer duplication on write // in SkBitmap/SkPixelRef, which will make deferral possible in this case. - flushPending(); + this->flushPending(); } uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() { @@ -648,7 +672,7 @@ int SkDeferredCanvas::DeferredDevice::height() const { } SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() { - flushPending(); + this->flushPending(); return fImmediateDevice->accessRenderTarget(); } @@ -664,7 +688,7 @@ void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap, SkCanvas::kNative_Premul_Config8888 != config8888 && kPMColorAlias != config8888) { //Special case config: no deferral - flushPending(); + this->flushPending(); fImmediateDevice->writePixels(bitmap, x, y, config8888); } @@ -675,7 +699,7 @@ void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap, } const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) { - flushPending(); + this->flushPending(); return fImmediateDevice->accessBitmap(false); } @@ -694,7 +718,7 @@ SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice( bool SkDeferredCanvas::DeferredDevice::onReadPixels( const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) { - flushPending(); + this->flushPending(); return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap), x, y, config8888); } diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp index 17adb52118..449f5e97bc 100644 --- a/tests/DeferredCanvasTest.cpp +++ b/tests/DeferredCanvasTest.cpp @@ -10,7 +10,6 @@ #include "SkDeferredCanvas.h" #include "SkShader.h" - static const int gWidth = 2; static const int gHeight = 2; @@ -177,10 +176,54 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { } } +class MockDevice : public SkDevice { +public: + MockDevice(const SkBitmap& bm) : SkDevice(bm) { + fDrawBitmapCallCount = 0; + } + virtual void drawBitmap(const SkDraw&, const SkBitmap&, + const SkIRect*, + const SkMatrix&, const SkPaint&) { + fDrawBitmapCallCount++; + } + + int fDrawBitmapCallCount; +}; + +// Verifies that the deferred canvas triggers a flush when its memory +// limit is exceeded +static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) { + SkBitmap store; + store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + store.allocPixels(); + MockDevice mockDevice(store); + SkDeferredCanvas canvas(&mockDevice); + canvas.setMaxRecordingStorage(160000); + + SkBitmap sourceImage; + // 100 by 100 image, takes 40,000 bytes in memory + sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + sourceImage.allocPixels(); + + for (int i = 0; i < 6; i++) { + sourceImage.notifyPixelsChanged(); // to force re-serialization + canvas.drawBitmap(sourceImage, 0, 0, NULL); + } + + // FIXME: Test temporarily disabled because the SkPicture path is not + // fixed and the SkGPipe path does not yet serialize images, but it + // will soon. +#if 0 + REPORTER_ASSERT(reporter, mockDevice.fDrawBitmapCallCount == 4); +#endif +} + + static void TestDeferredCanvas(skiatest::Reporter* reporter) { TestDeferredCanvasBitmapAccess(reporter); TestDeferredCanvasFlush(reporter); TestDeferredCanvasFreshFrame(reporter); + TestDeferredCanvasMemoryLimit(reporter); } #include "TestClassDef.h" |