Deferred canvas can now be flushed if an image is beyond a certain size to avoid...
authorsugoi@google.com <sugoi@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 21 Nov 2012 15:47:04 +0000 (15:47 +0000)
committersugoi@google.com <sugoi@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 21 Nov 2012 15:47:04 +0000 (15:47 +0000)
BUG=http://code.google.com/p/chromium/issues/detail?id=137924
TEST=TestDeferredCanvasBitmapSizeThreshold unit test
Review URL: https://codereview.appspot.com/6852071

git-svn-id: http://skia.googlecode.com/svn/trunk@6527 2bbb7eff-a529-9590-31e7-b0007b416f81

include/config/SkUserConfig.h
include/utils/SkDeferredCanvas.h
src/utils/SkDeferredCanvas.cpp
tests/DeferredCanvasTest.cpp

index 79fdc7a..ae5865a 100644 (file)
    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
index 97848f1..4686d00 100644 (file)
@@ -116,6 +116,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
      */
     void silentFlush();
index 207ec05..ffc935d 100644 (file)
 #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<DeferredDevice*>(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();
index 860edf9..6bbcf82 100644 (file)
@@ -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"