diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h index 79fdc7a2a4..ae5865ab37 100644 --- a/include/config/SkUserConfig.h +++ b/include/config/SkUserConfig.h @@ -199,4 +199,12 @@ backend. Defaults to 1 (build the GPU code). */ //#define SK_SUPPORT_GPU 1 + +/* Defines the maximum size allowed for a given image to be rendered using the + deferred canvas. If the image is larger than this threshold, the image + is considered too large and the copy done by the deferred canvas too + expensive, so an image of that size would instead be drawn immediately. +*/ +//#define SK_DEFERRED_CANVAS_BITMAP_SIZE_THRESHOLD 1048576 + #endif diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h index 97848f1129..4686d000b9 100644 --- a/include/utils/SkDeferredCanvas.h +++ b/include/utils/SkDeferredCanvas.h @@ -115,6 +115,12 @@ public: */ 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); + /** * Executes all pending commands without drawing */ diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp index 207ec053a7..ffc935d5de 100644 --- a/src/utils/SkDeferredCanvas.cpp +++ b/src/utils/SkDeferredCanvas.cpp @@ -16,6 +16,10 @@ #include "SkPaint.h" #include "SkShader.h" +#ifndef SK_DEFERRED_CANVAS_BITMAP_SIZE_THRESHOLD +#define SK_DEFERRED_CANVAS_BITMAP_SIZE_THRESHOLD ~0 // Disables this feature +#endif + enum { // Deferred canvas will auto-flush when recording reaches this limit kDefaultMaxRecordingStorageBytes = 64*1024*1024, @@ -27,8 +31,10 @@ enum PlaybackMode { }; namespace { -bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint) { - if (bitmap && bitmap->getTexture() && !bitmap->isImmutable()) { +bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint, + size_t bitmapSizeThreshold) { + if (bitmap && ((bitmap->getTexture() && !bitmap->isImmutable()) || + (bitmap->getSize() > bitmapSizeThreshold))) { return true; } if (paint) { @@ -50,36 +56,6 @@ bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint) { } } -class AutoImmediateDrawIfNeeded { -public: - AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap* bitmap, - const SkPaint* paint) { - this->init(canvas, bitmap, paint); - } - - AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkPaint* paint) { - this->init(canvas, NULL, paint); - } - - ~AutoImmediateDrawIfNeeded() { - if (fCanvas) { - fCanvas->setDeferredDrawing(true); - } - } -private: - void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkPaint* paint) - { - if (canvas.isDeferredDrawing() && shouldDrawImmediately(bitmap, paint)) { - canvas.setDeferredDrawing(false); - fCanvas = &canvas; - } else { - fCanvas = NULL; - } - } - - SkDeferredCanvas* fCanvas; -}; - namespace { bool isPaintOpaque(const SkPaint* paint, @@ -244,6 +220,8 @@ public: bool hasPendingCommands(); size_t storageAllocatedForRecording() const; size_t freeMemoryIfPossible(size_t bytesToFree); + size_t getBitmapSizeThreshold() const; + void setBitmapSizeThreshold(size_t sizeThreshold); void flushPendingCommands(PlaybackMode); void skipPendingCommands(); void setMaxRecordingStorage(size_t); @@ -339,6 +317,7 @@ private: bool fFreshFrame; size_t fMaxRecordingStorageBytes; size_t fPreviousStorageAllocated; + size_t fBitmapSizeThreshold; }; DeferredDevice::DeferredDevice( @@ -347,7 +326,8 @@ DeferredDevice::DeferredDevice( immediateDevice->height(), immediateDevice->isOpaque()) , fRecordingCanvas(NULL) , fFreshFrame(true) - , fPreviousStorageAllocated(0){ + , fPreviousStorageAllocated(0) + , fBitmapSizeThreshold(SK_DEFERRED_CANVAS_BITMAP_SIZE_THRESHOLD){ fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; fNotificationClient = notificationClient; @@ -424,6 +404,14 @@ size_t DeferredDevice::freeMemoryIfPossible(size_t bytesToFree) { return val; } +size_t DeferredDevice::getBitmapSizeThreshold() const { + return fBitmapSizeThreshold; +} + +void DeferredDevice::setBitmapSizeThreshold(size_t sizeThreshold) { + fBitmapSizeThreshold = sizeThreshold; +} + size_t DeferredDevice::storageAllocatedForRecording() const { return (fPipeController.storageAllocatedForRecording() + fPipeWriter.storageAllocatedForRecording()); @@ -492,7 +480,7 @@ void DeferredDevice::writePixels(const SkBitmap& bitmap, SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); - if (shouldDrawImmediately(&bitmap, NULL)) { + if (shouldDrawImmediately(&bitmap, NULL, getBitmapSizeThreshold())) { this->flushPendingCommands(kNormal_PlaybackMode); fImmediateCanvas->drawSprite(bitmap, x, y, &paint); } else { @@ -527,6 +515,37 @@ bool DeferredDevice::onReadPixels( x, y, config8888); } +class AutoImmediateDrawIfNeeded { +public: + AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap* bitmap, + const SkPaint* paint) { + this->init(canvas, bitmap, paint); + } + + AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkPaint* paint) { + this->init(canvas, NULL, paint); + } + + ~AutoImmediateDrawIfNeeded() { + if (fCanvas) { + fCanvas->setDeferredDrawing(true); + } + } +private: + void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkPaint* paint) + { + DeferredDevice* device = static_cast(canvas.getDevice()); + if (canvas.isDeferredDrawing() && (NULL != device) && + shouldDrawImmediately(bitmap, paint, device->getBitmapSizeThreshold())) { + canvas.setDeferredDrawing(false); + fCanvas = &canvas; + } else { + fCanvas = NULL; + } + } + + SkDeferredCanvas* fCanvas; +}; SkDeferredCanvas::SkDeferredCanvas() { this->init(); @@ -554,6 +573,12 @@ size_t SkDeferredCanvas::freeMemoryIfPossible(size_t bytesToFree) { return this->getDeferredDevice()->freeMemoryIfPossible(bytesToFree); } +void SkDeferredCanvas::setBitmapSizeThreshold(size_t sizeThreshold) { + DeferredDevice* deferredDevice = this->getDeferredDevice(); + SkASSERT(deferredDevice); + deferredDevice->setBitmapSizeThreshold(sizeThreshold); +} + void SkDeferredCanvas::recordedDrawCommand() { if (fDeferredDrawing) { this->getDeferredDevice()->recordedDrawCommand(); diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp index 860edf9a2a..6bbcf8284f 100644 --- a/tests/DeferredCanvasTest.cpp +++ b/tests/DeferredCanvasTest.cpp @@ -391,6 +391,47 @@ static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, 0 == canvas.storageAllocatedForRecording()); } +static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) { + SkBitmap store; + store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + store.allocPixels(); + + SkBitmap sourceImage; + // 100 by 100 image, takes 40,000 bytes in memory + sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + sourceImage.allocPixels(); + + // 1 under : should not store the image + { + SkDevice device(store); + SkDeferredCanvas canvas(&device); + 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 + { + SkDevice device(store); + SkDeferredCanvas canvas(&device); + 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 + { + SkDevice device(store); + SkDeferredCanvas canvas(&device); + canvas.setBitmapSizeThreshold(40001); + canvas.drawBitmap(sourceImage, 0, 0, NULL); + size_t newBytesAllocated = canvas.storageAllocatedForRecording(); + REPORTER_ASSERT(reporter, newBytesAllocated > 0); + } +} + static void TestDeferredCanvas(skiatest::Reporter* reporter) { TestDeferredCanvasBitmapAccess(reporter); TestDeferredCanvasFlush(reporter); @@ -399,6 +440,7 @@ static void TestDeferredCanvas(skiatest::Reporter* reporter) { TestDeferredCanvasBitmapCaching(reporter); TestDeferredCanvasSkip(reporter); TestDeferredCanvasBitmapShaderNoLeak(reporter); + TestDeferredCanvasBitmapSizeThreshold(reporter); } #include "TestClassDef.h"