Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "gm.h"
      9 #include "SkDebugCanvas.h"
     10 #include "SkPictureFlat.h"
     11 
     12 #define WARN(msg)                                           \
     13     SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
     14 
     15 // Do the commands in 'input' match the supplied pattern? Note: this is a pretty
     16 // heavy-weight operation since we are drawing the picture into a debug canvas
     17 // to extract the commands.
     18 static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
     19     SkDebugCanvas debugCanvas(input.width(), input.height());
     20     debugCanvas.setBounds(input.width(), input.height());
     21     input.draw(&debugCanvas);
     22 
     23     if (pattern.count() != debugCanvas.getSize()) {
     24         return false;
     25     }
     26 
     27     for (int i = 0; i < pattern.count(); ++i) {
     28         if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
     29             return false;
     30         }
     31     }
     32 
     33     return true;
     34 }
     35 
     36 // construct the pattern removed by the SkPictureRecord::remove_save_layer1
     37 // optimization, i.e.:
     38 //   SAVE_LAYER
     39 //       DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
     40 //   RESTORE
     41 //
     42 // saveLayerHasPaint - control if the saveLayer has a paint (the optimization
     43 //                     takes a different path if this is false)
     44 // dbmr2rHasPaint    - control if the dbmr2r has a paint (the optimization
     45 //                     takes a different path if this is false)
     46 // colorsMatch       - control if the saveLayer and dbmr2r paint colors
     47 //                     match (the optimization will fail if they do not)
     48 static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern,
     49                                           SkTDArray<DrawType>* postOptPattern,
     50                                           const SkBitmap& checkerBoard,
     51                                           bool saveLayerHasPaint,
     52                                           bool dbmr2rHasPaint,
     53                                           bool colorsMatch)  {
     54     // Create the pattern that should trigger the optimization
     55     preOptPattern->setCount(5);
     56     (*preOptPattern)[0] = SAVE;
     57     (*preOptPattern)[1] = SAVE_LAYER;
     58     (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
     59     (*preOptPattern)[3] = RESTORE;
     60     (*preOptPattern)[4] = RESTORE;
     61 
     62     if (colorsMatch) {
     63         // Create the pattern that should appear after the optimization
     64         postOptPattern->setCount(5);
     65         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
     66         (*postOptPattern)[1] = SAVE;
     67         (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
     68         (*postOptPattern)[3] = RESTORE;
     69         (*postOptPattern)[4] = RESTORE;
     70     } else {
     71         // Create the pattern that appears if the optimization doesn't fire
     72         postOptPattern->setCount(7);
     73         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
     74         (*postOptPattern)[1] = SAVE;
     75         (*postOptPattern)[2] = SAVE_LAYER;
     76         (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
     77         (*postOptPattern)[4] = RESTORE;
     78         (*postOptPattern)[5] = RESTORE;
     79         (*postOptPattern)[6] = RESTORE;
     80     }
     81 
     82     SkPicture* result = new SkPicture;
     83 
     84     // have to disable the optimizations while generating the picture
     85     SkCanvas* canvas = result->beginRecording(100, 100,
     86                          SkPicture::kDisableRecordOptimizations_RecordingFlag);
     87 
     88     SkPaint saveLayerPaint;
     89     saveLayerPaint.setColor(0xCC000000);
     90 
     91     // saveLayer's 'bounds' parameter must be NULL for this optimization
     92     if (saveLayerHasPaint) {
     93         canvas->saveLayer(NULL, &saveLayerPaint);
     94     } else {
     95         canvas->saveLayer(NULL, NULL);
     96     }
     97 
     98     SkRect rect = { 10, 10, 90, 90 };
     99 
    100     // The dbmr2r's paint must be opaque
    101     SkPaint dbmr2rPaint;
    102     if (colorsMatch) {
    103         dbmr2rPaint.setColor(0xFF000000);
    104     } else {
    105         dbmr2rPaint.setColor(0xFFFF0000);
    106     }
    107 
    108     if (dbmr2rHasPaint) {
    109         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
    110     } else {
    111         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
    112     }
    113     canvas->restore();
    114 
    115     result->endRecording();
    116 
    117     return result;
    118 }
    119 
    120 // straight-ahead version that is seen in the skps
    121 static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern,
    122                                              SkTDArray<DrawType>* postOptPattern,
    123                                              const SkBitmap& checkerBoard) {
    124     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    125                                    true,   // saveLayer has a paint
    126                                    true,   // dbmr2r has a paint
    127                                    true);  // and the colors match
    128 }
    129 
    130 // alternate version that should still succeed
    131 static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern,
    132                                              SkTDArray<DrawType>* postOptPattern,
    133                                              const SkBitmap& checkerBoard) {
    134     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    135                                    false,  // saveLayer doesn't have a paint!
    136                                    true,   // dbmr2r has a paint
    137                                    true);  // color matching not really applicable
    138 }
    139 
    140 // alternate version that should still succeed
    141 static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern,
    142                                              SkTDArray<DrawType>* postOptPattern,
    143                                              const SkBitmap& checkerBoard) {
    144     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    145                                    true,   // saveLayer has a paint
    146                                    false,  // dbmr2r doesn't have a paint!
    147                                    true);  // color matching not really applicable
    148 }
    149 
    150 // version in which the optimization fails b.c. the colors don't match
    151 static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern,
    152                                              SkTDArray<DrawType>* postOptPattern,
    153                                              const SkBitmap& checkerBoard) {
    154     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    155                                    true,   // saveLayer has a paint
    156                                    true,   // dbmr2r has a paint
    157                                    false); // and the colors don't match!
    158 }
    159 
    160 // construct the pattern removed by the SkPictureRecord::remove_save_layer2
    161 // optimization, i.e.:
    162 //   SAVE_LAYER (with NULL == bounds)
    163 //      SAVE
    164 //         CLIP_RECT
    165 //         DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
    166 //      RESTORE
    167 //   RESTORE
    168 //
    169 // saveLayerHasPaint - control if the saveLayer has a paint (the optimization
    170 //                     takes a different path if this is false)
    171 // dbmr2rHasPaint    - control if the dbmr2r has a paint (the optimization
    172 //                     takes a different path if this is false)
    173 // colorsMatch       - control if the saveLayer and dbmr2r paint colors
    174 //                     match (the optimization will fail if they do not)
    175 static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern,
    176                                           SkTDArray<DrawType>* postOptPattern,
    177                                           const SkBitmap& checkerBoard,
    178                                           bool saveLayerHasPaint,
    179                                           bool dbmr2rHasPaint,
    180                                           bool colorsMatch)  {
    181     // Create the pattern that should trigger the optimization
    182     preOptPattern->setCount(8);
    183     (*preOptPattern)[0] = SAVE;
    184     (*preOptPattern)[1] = SAVE_LAYER;
    185     (*preOptPattern)[2] = SAVE;
    186     (*preOptPattern)[3] = CLIP_RECT;
    187     (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
    188     (*preOptPattern)[5] = RESTORE;
    189     (*preOptPattern)[6] = RESTORE;
    190     (*preOptPattern)[7] = RESTORE;
    191 
    192     if (colorsMatch) {
    193         // Create the pattern that should appear after the optimization
    194         postOptPattern->setCount(8);
    195         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
    196         (*postOptPattern)[1] = SAVE;
    197         (*postOptPattern)[2] = SAVE;
    198         (*postOptPattern)[3] = CLIP_RECT;
    199         (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
    200         (*postOptPattern)[5] = RESTORE;
    201         (*postOptPattern)[6] = RESTORE;
    202         (*postOptPattern)[7] = RESTORE;
    203     } else {
    204         // Create the pattern that appears if the optimization doesn't fire
    205         postOptPattern->setCount(10);
    206         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
    207         (*postOptPattern)[1] = SAVE;
    208         (*postOptPattern)[2] = SAVE_LAYER;
    209         (*postOptPattern)[3] = SAVE;
    210         (*postOptPattern)[4] = CLIP_RECT;
    211         (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
    212         (*postOptPattern)[6] = RESTORE;
    213         (*postOptPattern)[7] = RESTORE;
    214         (*postOptPattern)[8] = RESTORE;
    215         (*postOptPattern)[9] = RESTORE;
    216     }
    217 
    218     SkPicture* result = new SkPicture;
    219 
    220     // have to disable the optimizations while generating the picture
    221     SkCanvas* canvas = result->beginRecording(100, 100,
    222                          SkPicture::kDisableRecordOptimizations_RecordingFlag);
    223 
    224     SkPaint saveLayerPaint;
    225     saveLayerPaint.setColor(0xCC000000);
    226 
    227     // saveLayer's 'bounds' parameter must be NULL for this optimization
    228     if (saveLayerHasPaint) {
    229         canvas->saveLayer(NULL, &saveLayerPaint);
    230     } else {
    231         canvas->saveLayer(NULL, NULL);
    232     }
    233 
    234     canvas->save();
    235 
    236     SkRect rect = { 10, 10, 90, 90 };
    237     canvas->clipRect(rect);
    238 
    239     // The dbmr2r's paint must be opaque
    240     SkPaint dbmr2rPaint;
    241     if (colorsMatch) {
    242         dbmr2rPaint.setColor(0xFF000000);
    243     } else {
    244         dbmr2rPaint.setColor(0xFFFF0000);
    245     }
    246 
    247     if (dbmr2rHasPaint) {
    248         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
    249     } else {
    250         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
    251     }
    252     canvas->restore();
    253     canvas->restore();
    254 
    255     result->endRecording();
    256 
    257     return result;
    258 }
    259 
    260 // straight-ahead version that is seen in the skps
    261 static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern,
    262                                              SkTDArray<DrawType>* postOptPattern,
    263                                              const SkBitmap& checkerBoard) {
    264     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    265                                    true,   // saveLayer has a paint
    266                                    true,   // dbmr2r has a paint
    267                                    true);  // and the colors match
    268 }
    269 
    270 // alternate version that should still succeed
    271 static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern,
    272                                              SkTDArray<DrawType>* postOptPattern,
    273                                              const SkBitmap& checkerBoard) {
    274     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    275                                    false,  // saveLayer doesn't have a paint!
    276                                    true,   // dbmr2r has a paint
    277                                    true);  // color matching not really applicable
    278 }
    279 
    280 // alternate version that should still succeed
    281 static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern,
    282                                              SkTDArray<DrawType>* postOptPattern,
    283                                              const SkBitmap& checkerBoard) {
    284     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    285                                    true,   // saveLayer has a paint
    286                                    false,  // dbmr2r doesn't have a paint!
    287                                    true);  // color matching not really applicable
    288 }
    289 
    290 // version in which the optimization fails b.c. the colors don't match
    291 static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern,
    292                                              SkTDArray<DrawType>* postOptPattern,
    293                                              const SkBitmap& checkerBoard) {
    294     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    295                                    true,   // saveLayer has a paint
    296                                    true,   // dbmr2r has a paint
    297                                    false); // and the colors don't match!
    298 }
    299 
    300 // As our .skp optimizations get folded into the captured skps our code will
    301 // no longer be locally exercised. This GM manually constructs the patterns
    302 // our optimizations will remove to test them. It acts as both a GM and a unit
    303 // test
    304 class OptimizationsGM : public skiagm::GM {
    305 public:
    306     OptimizationsGM() {
    307         this->makeCheckerboard();
    308     }
    309 
    310     static const int kWidth = 800;
    311     static const int kHeight = 800;
    312 
    313 protected:
    314     SkString onShortName() {
    315         return SkString("optimizations");
    316     }
    317 
    318     SkISize onISize() { return SkISize::Make(kWidth, kHeight); }
    319 
    320     typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern,
    321                                       SkTDArray<DrawType> *postOptPattern,
    322                                       const SkBitmap& checkerBoard);
    323 
    324     virtual void onDraw(SkCanvas* canvas) {
    325 
    326         PFCreateOpt gOpts[] = {
    327             create_save_layer_opt_1_v1,
    328             create_save_layer_opt_1_v2,
    329             create_save_layer_opt_1_v3,
    330             create_save_layer_opt_1_v4,
    331             create_save_layer_opt_2_v1,
    332             create_save_layer_opt_2_v2,
    333             create_save_layer_opt_2_v3,
    334             create_save_layer_opt_2_v4,
    335         };
    336 
    337         SkTDArray<DrawType> prePattern, postPattern;
    338         int xPos = 0, yPos = 0;
    339 
    340         for (size_t i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) {
    341             SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard));
    342 
    343             if (!(check_pattern(*pre, prePattern))) {
    344                 WARN("Pre optimization pattern mismatch");
    345                 SkASSERT(0);
    346             }
    347 
    348             canvas->save();
    349                 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
    350                 pre->draw(canvas);
    351                 xPos += pre->width();
    352             canvas->restore();
    353 
    354             // re-render the 'pre' picture and thus 'apply' the optimization
    355             SkAutoTUnref<SkPicture> post(new SkPicture);
    356 
    357             SkCanvas* recordCanvas = post->beginRecording(pre->width(), pre->height());
    358 
    359             pre->draw(recordCanvas);
    360 
    361             post->endRecording();
    362 
    363             if (!(check_pattern(*post, postPattern))) {
    364                 WARN("Post optimization pattern mismatch");
    365                 SkASSERT(0);
    366             }
    367 
    368             canvas->save();
    369                 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
    370                 post->draw(canvas);
    371                 xPos += post->width();
    372             canvas->restore();
    373 
    374             if (xPos >= kWidth) {
    375                 // start a new line
    376                 xPos = 0;
    377                 yPos += post->height();
    378             }
    379 
    380             // TODO: we could also render the pre and post pictures to bitmaps
    381             // and manually compare them in this method
    382         }
    383     }
    384 
    385 private:
    386     void makeCheckerboard() {
    387         static const unsigned int kCheckerboardWidth = 16;
    388         static const unsigned int kCheckerboardHeight = 16;
    389 
    390         fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config,
    391                                 kCheckerboardWidth, kCheckerboardHeight);
    392         fCheckerboard.allocPixels();
    393         SkAutoLockPixels lock(fCheckerboard);
    394         for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) {
    395             SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
    396             for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
    397                 *scanline++ = 0xFFFFFFFF;
    398                 *scanline++ = 0xFF000000;
    399             }
    400             scanline = fCheckerboard.getAddr32(0, y + 1);
    401             for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
    402                 *scanline++ = 0xFF000000;
    403                 *scanline++ = 0xFFFFFFFF;
    404             }
    405         }
    406     }
    407 
    408     SkBitmap fCheckerboard;
    409 
    410     typedef skiagm::GM INHERITED;
    411 };
    412 
    413 //////////////////////////////////////////////////////////////////////////////
    414 
    415 DEF_GM( return new OptimizationsGM; )
    416