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