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 #include "SkPictureRecorder.h"
     12 
     13 #define WARN(msg)                                           \
     14     SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
     15 
     16 // Do the commands in 'input' match the supplied pattern? Note: this is a pretty
     17 // heavy-weight operation since we are drawing the picture into a debug canvas
     18 // to extract the commands.
     19 static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
     20     SkDebugCanvas debugCanvas(input.width(), input.height());
     21     debugCanvas.setBounds(input.width(), input.height());
     22     input.draw(&debugCanvas);
     23 
     24     if (pattern.count() != debugCanvas.getSize()) {
     25         return false;
     26     }
     27 
     28     for (int i = 0; i < pattern.count(); ++i) {
     29         if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
     30             return false;
     31         }
     32     }
     33 
     34     return true;
     35 }
     36 
     37 // construct the pattern removed by the SkPictureRecord::remove_save_layer1
     38 // optimization, i.e.:
     39 //   SAVE_LAYER
     40 //       DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
     41 //   RESTORE
     42 //
     43 // saveLayerHasPaint - control if the saveLayer has a paint (the optimization
     44 //                     takes a different path if this is false)
     45 // dbmr2rHasPaint    - control if the dbmr2r has a paint (the optimization
     46 //                     takes a different path if this is false)
     47 // colorsMatch       - control if the saveLayer and dbmr2r paint colors
     48 //                     match (the optimization will fail if they do not)
     49 static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern,
     50                                           SkTDArray<DrawType>* postOptPattern,
     51                                           const SkBitmap& checkerBoard,
     52                                           bool saveLayerHasPaint,
     53                                           bool dbmr2rHasPaint,
     54                                           bool colorsMatch)  {
     55     // Create the pattern that should trigger the optimization
     56     preOptPattern->setCount(5);
     57     (*preOptPattern)[0] = SAVE;
     58     (*preOptPattern)[1] = SAVE_LAYER;
     59     (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
     60     (*preOptPattern)[3] = RESTORE;
     61     (*preOptPattern)[4] = RESTORE;
     62 
     63     if (colorsMatch) {
     64         // Create the pattern that should appear after the optimization
     65         postOptPattern->setCount(5);
     66         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
     67         (*postOptPattern)[1] = SAVE;
     68         (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
     69         (*postOptPattern)[3] = RESTORE;
     70         (*postOptPattern)[4] = RESTORE;
     71     } else {
     72         // Create the pattern that appears if the optimization doesn't fire
     73         postOptPattern->setCount(7);
     74         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
     75         (*postOptPattern)[1] = SAVE;
     76         (*postOptPattern)[2] = SAVE_LAYER;
     77         (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
     78         (*postOptPattern)[4] = RESTORE;
     79         (*postOptPattern)[5] = RESTORE;
     80         (*postOptPattern)[6] = RESTORE;
     81     }
     82 
     83     SkPictureRecorder recorder;
     84 
     85     SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0);
     86     // have to disable the optimizations while generating the picture
     87     recorder.internalOnly_EnableOpts(false);
     88 
     89     SkPaint saveLayerPaint;
     90     saveLayerPaint.setColor(0xCC000000);
     91 
     92     // saveLayer's 'bounds' parameter must be NULL for this optimization
     93     if (saveLayerHasPaint) {
     94         canvas->saveLayer(NULL, &saveLayerPaint);
     95     } else {
     96         canvas->saveLayer(NULL, NULL);
     97     }
     98 
     99     SkRect rect = { 10, 10, 90, 90 };
    100 
    101     // The dbmr2r's paint must be opaque
    102     SkPaint dbmr2rPaint;
    103     if (colorsMatch) {
    104         dbmr2rPaint.setColor(0xFF000000);
    105     } else {
    106         dbmr2rPaint.setColor(0xFFFF0000);
    107     }
    108 
    109     if (dbmr2rHasPaint) {
    110         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
    111     } else {
    112         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
    113     }
    114     canvas->restore();
    115 
    116     return recorder.endRecording();
    117 }
    118 
    119 // straight-ahead version that is seen in the skps
    120 static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern,
    121                                              SkTDArray<DrawType>* postOptPattern,
    122                                              const SkBitmap& checkerBoard) {
    123     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    124                                    true,   // saveLayer has a paint
    125                                    true,   // dbmr2r has a paint
    126                                    true);  // and the colors match
    127 }
    128 
    129 // alternate version that should still succeed
    130 static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern,
    131                                              SkTDArray<DrawType>* postOptPattern,
    132                                              const SkBitmap& checkerBoard) {
    133     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    134                                    false,  // saveLayer doesn't have a paint!
    135                                    true,   // dbmr2r has a paint
    136                                    true);  // color matching not really applicable
    137 }
    138 
    139 // alternate version that should still succeed
    140 static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern,
    141                                              SkTDArray<DrawType>* postOptPattern,
    142                                              const SkBitmap& checkerBoard) {
    143     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    144                                    true,   // saveLayer has a paint
    145                                    false,  // dbmr2r doesn't have a paint!
    146                                    true);  // color matching not really applicable
    147 }
    148 
    149 // version in which the optimization fails b.c. the colors don't match
    150 static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern,
    151                                              SkTDArray<DrawType>* postOptPattern,
    152                                              const SkBitmap& checkerBoard) {
    153     return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
    154                                    true,   // saveLayer has a paint
    155                                    true,   // dbmr2r has a paint
    156                                    false); // and the colors don't match!
    157 }
    158 
    159 // construct the pattern removed by the SkPictureRecord::remove_save_layer2
    160 // optimization, i.e.:
    161 //   SAVE_LAYER (with NULL == bounds)
    162 //      SAVE
    163 //         CLIP_RECT
    164 //         DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
    165 //      RESTORE
    166 //   RESTORE
    167 //
    168 // saveLayerHasPaint - control if the saveLayer has a paint (the optimization
    169 //                     takes a different path if this is false)
    170 // dbmr2rHasPaint    - control if the dbmr2r has a paint (the optimization
    171 //                     takes a different path if this is false)
    172 // colorsMatch       - control if the saveLayer and dbmr2r paint colors
    173 //                     match (the optimization will fail if they do not)
    174 static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern,
    175                                           SkTDArray<DrawType>* postOptPattern,
    176                                           const SkBitmap& checkerBoard,
    177                                           bool saveLayerHasPaint,
    178                                           bool dbmr2rHasPaint,
    179                                           bool colorsMatch)  {
    180     // Create the pattern that should trigger the optimization
    181     preOptPattern->setCount(8);
    182     (*preOptPattern)[0] = SAVE;
    183     (*preOptPattern)[1] = SAVE_LAYER;
    184     (*preOptPattern)[2] = SAVE;
    185     (*preOptPattern)[3] = CLIP_RECT;
    186     (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
    187     (*preOptPattern)[5] = RESTORE;
    188     (*preOptPattern)[6] = RESTORE;
    189     (*preOptPattern)[7] = RESTORE;
    190 
    191     if (colorsMatch) {
    192         // Create the pattern that should appear after the optimization
    193         postOptPattern->setCount(8);
    194         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
    195         (*postOptPattern)[1] = SAVE;
    196         (*postOptPattern)[2] = SAVE;
    197         (*postOptPattern)[3] = CLIP_RECT;
    198         (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
    199         (*postOptPattern)[5] = RESTORE;
    200         (*postOptPattern)[6] = RESTORE;
    201         (*postOptPattern)[7] = RESTORE;
    202     } else {
    203         // Create the pattern that appears if the optimization doesn't fire
    204         postOptPattern->setCount(10);
    205         (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
    206         (*postOptPattern)[1] = SAVE;
    207         (*postOptPattern)[2] = SAVE_LAYER;
    208         (*postOptPattern)[3] = SAVE;
    209         (*postOptPattern)[4] = CLIP_RECT;
    210         (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
    211         (*postOptPattern)[6] = RESTORE;
    212         (*postOptPattern)[7] = RESTORE;
    213         (*postOptPattern)[8] = RESTORE;
    214         (*postOptPattern)[9] = RESTORE;
    215     }
    216 
    217     SkPictureRecorder recorder;
    218 
    219     SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0);
    220     // have to disable the optimizations while generating the picture
    221     recorder.internalOnly_EnableOpts(false);
    222 
    223     SkPaint saveLayerPaint;
    224     saveLayerPaint.setColor(0xCC000000);
    225 
    226     // saveLayer's 'bounds' parameter must be NULL for this optimization
    227     if (saveLayerHasPaint) {
    228         canvas->saveLayer(NULL, &saveLayerPaint);
    229     } else {
    230         canvas->saveLayer(NULL, NULL);
    231     }
    232 
    233     canvas->save();
    234 
    235     SkRect rect = { 10, 10, 90, 90 };
    236     canvas->clipRect(rect);
    237 
    238     // The dbmr2r's paint must be opaque
    239     SkPaint dbmr2rPaint;
    240     if (colorsMatch) {
    241         dbmr2rPaint.setColor(0xFF000000);
    242     } else {
    243         dbmr2rPaint.setColor(0xFFFF0000);
    244     }
    245 
    246     if (dbmr2rHasPaint) {
    247         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
    248     } else {
    249         canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
    250     }
    251     canvas->restore();
    252     canvas->restore();
    253 
    254     return recorder.endRecording();
    255 }
    256 
    257 // straight-ahead version that is seen in the skps
    258 static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern,
    259                                              SkTDArray<DrawType>* postOptPattern,
    260                                              const SkBitmap& checkerBoard) {
    261     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    262                                    true,   // saveLayer has a paint
    263                                    true,   // dbmr2r has a paint
    264                                    true);  // and the colors match
    265 }
    266 
    267 // alternate version that should still succeed
    268 static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern,
    269                                              SkTDArray<DrawType>* postOptPattern,
    270                                              const SkBitmap& checkerBoard) {
    271     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    272                                    false,  // saveLayer doesn't have a paint!
    273                                    true,   // dbmr2r has a paint
    274                                    true);  // color matching not really applicable
    275 }
    276 
    277 // alternate version that should still succeed
    278 static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern,
    279                                              SkTDArray<DrawType>* postOptPattern,
    280                                              const SkBitmap& checkerBoard) {
    281     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    282                                    true,   // saveLayer has a paint
    283                                    false,  // dbmr2r doesn't have a paint!
    284                                    true);  // color matching not really applicable
    285 }
    286 
    287 // version in which the optimization fails b.c. the colors don't match
    288 static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern,
    289                                              SkTDArray<DrawType>* postOptPattern,
    290                                              const SkBitmap& checkerBoard) {
    291     return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
    292                                    true,   // saveLayer has a paint
    293                                    true,   // dbmr2r has a paint
    294                                    false); // and the colors don't match!
    295 }
    296 
    297 // As our .skp optimizations get folded into the captured skps our code will
    298 // no longer be locally exercised. This GM manually constructs the patterns
    299 // our optimizations will remove to test them. It acts as both a GM and a unit
    300 // test
    301 class OptimizationsGM : public skiagm::GM {
    302 public:
    303     OptimizationsGM() {
    304         this->makeCheckerboard();
    305     }
    306 
    307     static const int kWidth = 800;
    308     static const int kHeight = 800;
    309 
    310 protected:
    311     uint32_t onGetFlags() const SK_OVERRIDE {
    312         // One optimization changes the color drawn slightly in a 565 target.
    313         // We've decided it's innocuous, so we disable this GM when targeting 565.
    314         // Revisit this if we get finer-grained control: it'd be nice to keep drawing directly.
    315         // For more, see skia:1994.
    316         return skiagm::GM::kSkip565_Flag;
    317     }
    318 
    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             SkPictureRecorder recorder;
    361 
    362             SkCanvas* recordCanvas = recorder.beginRecording(pre->width(), pre->height(), NULL, 0);
    363 
    364             pre->draw(recordCanvas);
    365 
    366             SkAutoTUnref<SkPicture> post(recorder.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.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
    396         for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) {
    397             SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
    398             for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
    399                 *scanline++ = 0xFFFFFFFF;
    400                 *scanline++ = 0xFF000000;
    401             }
    402             scanline = fCheckerboard.getAddr32(0, y + 1);
    403             for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
    404                 *scanline++ = 0xFF000000;
    405                 *scanline++ = 0xFFFFFFFF;
    406             }
    407         }
    408     }
    409 
    410     SkBitmap fCheckerboard;
    411 
    412     typedef skiagm::GM INHERITED;
    413 };
    414 
    415 //////////////////////////////////////////////////////////////////////////////
    416 
    417 DEF_GM( return new OptimizationsGM; )
    418