Add testing of optimizations to GM
authorrobertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 12 Apr 2013 14:53:29 +0000 (14:53 +0000)
committerrobertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 12 Apr 2013 14:53:29 +0000 (14:53 +0000)
https://codereview.chromium.org/12843028/

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

gm/optimizations.cpp [new file with mode: 0644]
gyp/SampleApp.gyp
gyp/gm.gyp
gyp/gmslides.gypi
include/core/SkPicture.h
src/core/SkPictureRecord.cpp

diff --git a/gm/optimizations.cpp b/gm/optimizations.cpp
new file mode 100644 (file)
index 0000000..7a9f1f6
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * 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 "gm.h"
+#include "..\debugger\SkDebugCanvas.h"
+#include "SkPictureFlat.h"
+
+#define WARN(msg)                                           \
+    SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
+
+namespace {
+
+// Do the commands in 'input' match the supplied pattern? Note: this is a pretty
+// heavy-weight operation since we are drawing the picture into a debug canvas 
+// to extract the commands.
+bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
+    SkDebugCanvas debugCanvas(input.width(), input.height());
+    debugCanvas.setBounds(input.width(), input.height());
+    input.draw(&debugCanvas);
+
+    if (pattern.count() != debugCanvas.getSize()) {
+        return false;
+    }
+
+    for (int i = 0; i < pattern.count(); ++i) {
+        if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// construct the pattern removed by the SkPictureRecord::remove_save_layer1
+// optimization, i.e.:
+//   SAVE_LAYER
+//       DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
+//   RESTORE
+//
+// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
+//                     takes a different path if this is false)
+// dbmr2rHasPaint    - control if the dbmr2r has a paint (the optimization
+//                     takes a different path if this is false)
+// colorsMatch       - control if the saveLayer and dbmr2r paint colors
+//                     match (the optimization will fail if they do not)
+SkPicture* create_save_layer_opt_1(SkTDArray<DrawType> *preOptPattern,
+                                   SkTDArray<DrawType> *postOptPattern,
+                                   const SkBitmap& checkerBoard,
+                                   bool saveLayerHasPaint,
+                                   bool dbmr2rHasPaint,
+                                   bool colorsMatch)  {
+    // Create the pattern that should trigger the optimization
+    preOptPattern->setCount(5);
+    (*preOptPattern)[0] = SAVE;
+    (*preOptPattern)[1] = SAVE_LAYER;
+    (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
+    (*preOptPattern)[3] = RESTORE;
+    (*preOptPattern)[4] = RESTORE;
+
+    if (colorsMatch) {
+        // Create the pattern that should appear after the optimization
+        postOptPattern->setCount(5);
+        (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+        (*postOptPattern)[1] = SAVE;
+        (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
+        (*postOptPattern)[3] = RESTORE;
+        (*postOptPattern)[4] = RESTORE;
+    } else {
+        // Create the pattern that appears if the optimization doesn't fire
+        postOptPattern->setCount(7);
+        (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+        (*postOptPattern)[1] = SAVE;
+        (*postOptPattern)[2] = SAVE_LAYER;
+        (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
+        (*postOptPattern)[4] = RESTORE;
+        (*postOptPattern)[5] = RESTORE;
+        (*postOptPattern)[6] = RESTORE;
+    }
+
+    SkPicture* result = new SkPicture;
+
+    // have to disable the optimizations while generating the picture
+    SkCanvas* canvas = result->beginRecording(100, 100,
+                         SkPicture::kDisableRecordOptimizations_RecordingFlag);
+
+    SkPaint saveLayerPaint;
+    saveLayerPaint.setColor(0xCC000000);
+
+    // saveLayer's 'bounds' parameter must be NULL for this optimization
+    if (saveLayerHasPaint) {
+        canvas->saveLayer(NULL, &saveLayerPaint);
+    } else {
+        canvas->saveLayer(NULL, NULL);
+    }
+
+    SkRect rect = { 10, 10, 90, 90 };
+
+    // The dbmr2r's paint must be opaque
+    SkPaint dbmr2rPaint;
+    if (colorsMatch) {
+        dbmr2rPaint.setColor(0xFF000000);
+    } else {
+        dbmr2rPaint.setColor(0xFFFF0000);
+    }
+
+    if (dbmr2rHasPaint) {
+        canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
+    } else {
+        canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
+    }
+    canvas->restore();
+
+    result->endRecording();
+
+    return result;
+}
+
+// straight-ahead version that is seen in the skps
+SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+                                   true,   // saveLayer has a paint
+                                   true,   // dbmr2r has a paint
+                                   true);  // and the colors match
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+                                   false,  // saveLayer doesn't have a paint!
+                                   true,   // dbmr2r has a paint
+                                   true);  // color matching not really applicable
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+                                   true,   // saveLayer has a paint
+                                   false,  // dbmr2r doesn't have a paint!
+                                   true);  // color matching not really applicable
+}
+
+// version in which the optimization fails b.c. the colors don't match
+SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+                                   true,   // saveLayer has a paint
+                                   true,   // dbmr2r has a paint
+                                   false); // and the colors don't match!
+}
+
+// construct the pattern removed by the SkPictureRecord::remove_save_layer2
+// optimization, i.e.:
+//   SAVE_LAYER (with NULL == bounds)
+//      SAVE
+//         CLIP_RECT
+//         DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
+//      RESTORE
+//   RESTORE
+//
+// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
+//                     takes a different path if this is false)
+// dbmr2rHasPaint    - control if the dbmr2r has a paint (the optimization
+//                     takes a different path if this is false)
+// colorsMatch       - control if the saveLayer and dbmr2r paint colors
+//                     match (the optimization will fail if they do not)
+SkPicture* create_save_layer_opt_2(SkTDArray<DrawType> *preOptPattern,
+                                   SkTDArray<DrawType> *postOptPattern,
+                                   const SkBitmap& checkerBoard,
+                                   bool saveLayerHasPaint,
+                                   bool dbmr2rHasPaint,
+                                   bool colorsMatch)  {
+    // Create the pattern that should trigger the optimization
+    preOptPattern->setCount(8);
+    (*preOptPattern)[0] = SAVE;
+    (*preOptPattern)[1] = SAVE_LAYER;
+    (*preOptPattern)[2] = SAVE;
+    (*preOptPattern)[3] = CLIP_RECT;
+    (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
+    (*preOptPattern)[5] = RESTORE;
+    (*preOptPattern)[6] = RESTORE;
+    (*preOptPattern)[7] = RESTORE;
+
+    if (colorsMatch) {
+        // Create the pattern that should appear after the optimization
+        postOptPattern->setCount(8);
+        (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+        (*postOptPattern)[1] = SAVE;
+        (*postOptPattern)[2] = SAVE;
+        (*postOptPattern)[3] = CLIP_RECT;
+        (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
+        (*postOptPattern)[5] = RESTORE;
+        (*postOptPattern)[6] = RESTORE;
+        (*postOptPattern)[7] = RESTORE;
+    } else {
+        // Create the pattern that appears if the optimization doesn't fire
+        postOptPattern->setCount(10);
+        (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+        (*postOptPattern)[1] = SAVE;
+        (*postOptPattern)[2] = SAVE_LAYER;
+        (*postOptPattern)[3] = SAVE;
+        (*postOptPattern)[4] = CLIP_RECT;
+        (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
+        (*postOptPattern)[6] = RESTORE;
+        (*postOptPattern)[7] = RESTORE;
+        (*postOptPattern)[8] = RESTORE;
+        (*postOptPattern)[9] = RESTORE;
+    }
+
+    SkPicture* result = new SkPicture;
+
+    // have to disable the optimizations while generating the picture
+    SkCanvas* canvas = result->beginRecording(100, 100,
+                         SkPicture::kDisableRecordOptimizations_RecordingFlag);
+
+    SkPaint saveLayerPaint;
+    saveLayerPaint.setColor(0xCC000000);
+
+    // saveLayer's 'bounds' parameter must be NULL for this optimization
+    if (saveLayerHasPaint) {
+        canvas->saveLayer(NULL, &saveLayerPaint);
+    } else {
+        canvas->saveLayer(NULL, NULL);
+    }
+
+    canvas->save();
+
+    SkRect rect = { 10, 10, 90, 90 };
+    canvas->clipRect(rect);
+
+    // The dbmr2r's paint must be opaque
+    SkPaint dbmr2rPaint;
+    if (colorsMatch) {
+        dbmr2rPaint.setColor(0xFF000000);
+    } else {
+        dbmr2rPaint.setColor(0xFFFF0000);
+    }
+
+    if (dbmr2rHasPaint) {
+        canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
+    } else {
+        canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
+    }
+    canvas->restore();
+    canvas->restore();
+
+    result->endRecording();
+
+    return result;
+}
+
+// straight-ahead version that is seen in the skps
+SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+                                   true,   // saveLayer has a paint
+                                   true,   // dbmr2r has a paint
+                                   true);  // and the colors match
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+                                   false,  // saveLayer doesn't have a paint!
+                                   true,   // dbmr2r has a paint
+                                   true);  // color matching not really applicable
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+                                   true,   // saveLayer has a paint
+                                   false,  // dbmr2r doesn't have a paint!
+                                   true);  // color matching not really applicable
+}
+
+// version in which the optimization fails b.c. the colors don't match
+SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard) {
+    return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+                                   true,   // saveLayer has a paint
+                                   true,   // dbmr2r has a paint
+                                   false); // and the colors don't match!
+}
+
+};
+
+
+// As our .skp optimizations get folded into the captured skps our code will
+// no longer be locally exercised. This GM manually constructs the patterns
+// our optimizations will remove to test them. It acts as both a GM and a unit
+// test
+class OptimizationsGM : public skiagm::GM {
+public:
+    OptimizationsGM() {
+        this->makeCheckerboard();
+    }
+
+    static const int kWidth = 800;
+    static const int kHeight = 800;
+
+protected:
+    SkString onShortName() {
+        return SkString("optimizations");
+    }
+
+    SkISize onISize() { return SkISize::Make(kWidth, kHeight); }
+
+    typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern,
+                                      SkTDArray<DrawType> *postOptPattern,
+                                      const SkBitmap& checkerBoard);
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        PFCreateOpt gOpts[] = {
+            create_save_layer_opt_1_v1,
+            create_save_layer_opt_1_v2,
+            create_save_layer_opt_1_v3,
+            create_save_layer_opt_1_v4,
+            create_save_layer_opt_2_v1,
+            create_save_layer_opt_2_v2,
+            create_save_layer_opt_2_v3,
+            create_save_layer_opt_2_v4,
+        };
+
+        SkTDArray<DrawType> prePattern, postPattern;
+        int xPos = 0, yPos = 0;
+
+        for (int i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) {
+            SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard));
+
+            if (!(check_pattern(*pre, prePattern))) {
+                WARN("Pre optimization pattern mismatch");
+                SkASSERT(0);
+            }
+
+            canvas->save();
+                canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
+                pre->draw(canvas);
+                xPos += pre->width();
+            canvas->restore();
+
+            // re-render the 'pre' picture and thus 'apply' the optimization
+            SkAutoTUnref<SkPicture> post(new SkPicture);
+
+            SkCanvas* recordCanvas = post->beginRecording(pre->width(), pre->height());
+
+            pre->draw(recordCanvas);
+
+            post->endRecording();
+
+            if (!(check_pattern(*post, postPattern))) {
+                WARN("Post optimization pattern mismatch");
+                SkASSERT(0);
+            }
+
+            canvas->save();
+                canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
+                post->draw(canvas);
+                xPos += post->width();
+            canvas->restore();
+
+            if (xPos >= kWidth) {
+                // start a new line
+                xPos = 0;
+                yPos += post->height();
+            }
+
+            // TODO: we could also render the pre and post pictures to bitmaps
+            // and manually compare them in this method
+        }
+    }
+
+private:
+    void makeCheckerboard() {
+        static const unsigned int kCheckerboardWidth = 16;
+        static const unsigned int kCheckerboardHeight = 16;
+
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 
+                                kCheckerboardWidth, kCheckerboardHeight);
+        fCheckerboard.allocPixels();
+        SkAutoLockPixels lock(fCheckerboard);
+        for (int y = 0; y < kCheckerboardHeight; y += 2) {
+            SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
+            for (int x = 0; x < kCheckerboardWidth; x += 2) {
+                *scanline++ = 0xFFFFFFFF;
+                *scanline++ = 0xFF000000;
+            }
+            scanline = fCheckerboard.getAddr32(0, y + 1);
+            for (int x = 0; x < kCheckerboardWidth; x += 2) {
+                *scanline++ = 0xFF000000;
+                *scanline++ = 0xFFFFFFFF;
+            }
+        }
+    }
+
+    SkBitmap fCheckerboard;
+
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new OptimizationsGM; )
index b4104ed..9c2b643 100644 (file)
@@ -5,6 +5,7 @@
       'type': 'executable',
       'mac_bundle' : 1,
       'include_dirs' : [
+        '../debugger',
         '../src/core',
         '../src/effects', #needed for BlurMask.h
         '../gm',       # needed to pull gm.h
         'gmslides.gypi',
       ],
       'sources': [
+        '../debugger/SkDrawCommand.h',
+        '../debugger/SkDrawCommand.cpp',
+        '../debugger/SkDebugCanvas.h',
+        '../debugger/SkDebugCanvas.cpp',
+        '../debugger/SkObjectParser.h',
+        '../debugger/SkObjectParser.cpp',
+        
         '../gm/gm.cpp',
         '../gm/gm.h',
 
index 940ab84..3458107 100644 (file)
@@ -8,6 +8,7 @@
       'target_name': 'gm',
       'type': 'executable',
       'include_dirs' : [
+        '../debugger',
         '../src/core',
         '../src/effects',
         '../src/pipe/utils/',
         'gmslides.gypi',
       ],
       'sources': [
+        '../debugger/SkDrawCommand.h',
+        '../debugger/SkDrawCommand.cpp',
+        '../debugger/SkDebugCanvas.h',
+        '../debugger/SkDebugCanvas.cpp',
+        '../debugger/SkObjectParser.h',
+        '../debugger/SkObjectParser.cpp',
+
         '../gm/gm.cpp',
         '../gm/gmmain.cpp',
         '../gm/system_preferences_default.cpp',
+
         '../src/pipe/utils/SamplePipeControllers.h',
         '../src/pipe/utils/SamplePipeControllers.cpp',
       ],
index f04098c..6bd07bd 100644 (file)
@@ -60,6 +60,7 @@
     '../gm/morphology.cpp',
     '../gm/ninepatchstretch.cpp',
     '../gm/nocolorbleed.cpp',
+    '../gm/optimizations.cpp',
     '../gm/patheffects.cpp',
     '../gm/pathfill.cpp',
     '../gm/pathinterior.cpp',
index 7929557..1b5783d 100644 (file)
@@ -115,7 +115,16 @@ public:
             Note: Currently this is not serializable, the bounding data will be
             discarded if you serialize into a stream and then deserialize.
         */
-        kOptimizeForClippedPlayback_RecordingFlag = 0x02
+        kOptimizeForClippedPlayback_RecordingFlag = 0x02,
+        /*
+            This flag disables all the picture recording optimizations (i.e.,
+            those in SkPictureRecord). It is mainly intended for testing the 
+            existing optimizations (i.e., to actually have the pattern
+            appear in an .skp we have to disable the optimization). This
+            option doesn't affect the optimizations controlled by
+            'kOptimizeForClippedPlayback_RecordingFlag'.
+         */
+        kDisableRecordOptimizations_RecordingFlag = 0x04
     };
 
     /** Returns the canvas that records the drawing commands.
index 97ceeb1..7fd8a1f 100644 (file)
@@ -412,7 +412,7 @@ static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
 /*
  * Restore has just been called (but not recorded), look back at the
  * matching save* and see if we are in the configuration:
- *   SAVE_LAYER
+ *   SAVE_LAYER (with NULL == bounds)
  *      SAVE
  *         CLIP_RECT
  *         DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
@@ -565,18 +565,21 @@ void SkPictureRecord::restore() {
 
     uint32_t initialOffset, size;
     size_t opt;
-    for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
-        if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
-            // Some optimization fired so don't add the RESTORE
-            size = 0;
-            initialOffset = fWriter.size();
-            apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
-                                      fStateTree, fBoundingHierarchy);
-            break;
+    if (!(fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag)) {
+        for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
+            if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
+                // Some optimization fired so don't add the RESTORE
+                size = 0;
+                initialOffset = fWriter.size();
+                apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
+                                          fStateTree, fBoundingHierarchy);
+                break;
+            }
         }
-    }
+    } 
 
-    if (SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
+    if ((fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag) ||
+        SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
         // No optimization fired so add the RESTORE
         fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
         size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code