Home | History | Annotate | Download | only in bench
      1 /*
      2  * Copyright 2012 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 "Benchmark.h"
      9 #include "SkBitmap.h"
     10 #include "SkCanvas.h"
     11 #include "SkPaint.h"
     12 #include "SkRandom.h"
     13 #include "SkShader.h"
     14 #include "SkString.h"
     15 #include "SkVertices.h"
     16 
     17 // This bench simulates the calls Skia sees from various HTML5 canvas
     18 // game bench marks
     19 class GameBench : public Benchmark {
     20 public:
     21     enum Type {
     22         kScale_Type,
     23         kTranslate_Type,
     24         kRotate_Type
     25     };
     26 
     27     enum Clear {
     28         kFull_Clear,
     29         kPartial_Clear
     30     };
     31 
     32     GameBench(Type type, Clear clear,
     33               bool aligned = false, bool useAtlas = false,
     34               bool useDrawVertices = false)
     35         : fType(type)
     36         , fClear(clear)
     37         , fAligned(aligned)
     38         , fUseAtlas(useAtlas)
     39         , fUseDrawVertices(useDrawVertices)
     40         , fName("game")
     41         , fNumSaved(0)
     42         , fInitialized(false) {
     43 
     44         switch (fType) {
     45         case kScale_Type:
     46             fName.append("_scale");
     47             break;
     48         case kTranslate_Type:
     49             fName.append("_trans");
     50             break;
     51         case kRotate_Type:
     52             fName.append("_rot");
     53             break;
     54         }
     55 
     56         if (aligned) {
     57             fName.append("_aligned");
     58         }
     59 
     60         if (kPartial_Clear == clear) {
     61             fName.append("_partial");
     62         } else {
     63             fName.append("_full");
     64         }
     65 
     66         if (useAtlas) {
     67             fName.append("_atlas");
     68         }
     69 
     70         if (useDrawVertices) {
     71             fName.append("_drawVerts");
     72         }
     73 
     74         // It's HTML 5 canvas, so always AA
     75         fName.append("_aa");
     76     }
     77 
     78 protected:
     79     const char* onGetName() override {
     80         return fName.c_str();
     81     }
     82 
     83     void onDelayedSetup() override {
     84         if (!fInitialized) {
     85             this->makeCheckerboard();
     86             this->makeAtlas();
     87             fInitialized = true;
     88         }
     89     }
     90 
     91     void onDraw(int loops, SkCanvas* canvas) override {
     92         SkRandom scaleRand;
     93         SkRandom transRand;
     94         SkRandom rotRand;
     95 
     96         int width, height;
     97         if (fUseAtlas) {
     98             width = kAtlasCellWidth;
     99             height = kAtlasCellHeight;
    100         } else {
    101             width = kCheckerboardWidth;
    102             height = kCheckerboardHeight;
    103         }
    104 
    105         SkPaint clearPaint;
    106         clearPaint.setColor(0xFF000000);
    107         clearPaint.setAntiAlias(true);
    108 
    109         SkISize size = canvas->getBaseLayerSize();
    110 
    111         SkScalar maxTransX, maxTransY;
    112 
    113         if (kScale_Type == fType) {
    114             maxTransX = size.fWidth  - (1.5f * width);
    115             maxTransY = size.fHeight - (1.5f * height);
    116         } else if (kTranslate_Type == fType) {
    117             maxTransX = SkIntToScalar(size.fWidth  - width);
    118             maxTransY = SkIntToScalar(size.fHeight - height);
    119         } else {
    120             SkASSERT(kRotate_Type == fType);
    121             // Yes, some rotations will be off the top and left sides
    122             maxTransX = size.fWidth  - SK_ScalarSqrt2 * height;
    123             maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
    124         }
    125 
    126         SkMatrix mat;
    127         SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
    128         SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
    129         SkPoint verts[4] = { // for drawVertices path
    130             { 0, 0 },
    131             { 0, SkIntToScalar(height) },
    132             { SkIntToScalar(width), SkIntToScalar(height) },
    133             { SkIntToScalar(width), 0 }
    134         };
    135         uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
    136 
    137         SkPaint p;
    138         p.setColor(0xFF000000);
    139         p.setFilterQuality(kLow_SkFilterQuality);
    140 
    141         SkPaint p2;         // for drawVertices path
    142         p2.setColor(0xFF000000);
    143         p2.setFilterQuality(kLow_SkFilterQuality);
    144         p2.setShader(SkShader::MakeBitmapShader(fAtlas,
    145                                                 SkShader::kClamp_TileMode,
    146                                                 SkShader::kClamp_TileMode));
    147 
    148         for (int i = 0; i < loops; ++i, ++fNumSaved) {
    149             if (0 == i % kNumBeforeClear) {
    150                 if (kPartial_Clear == fClear) {
    151                     for (int j = 0; j < fNumSaved; ++j) {
    152                         canvas->setMatrix(SkMatrix::I());
    153                         mat.setTranslate(fSaved[j][0], fSaved[j][1]);
    154 
    155                         if (kScale_Type == fType) {
    156                             mat.preScale(fSaved[j][2], fSaved[j][2]);
    157                         } else if (kRotate_Type == fType) {
    158                             mat.preRotate(fSaved[j][2]);
    159                         }
    160 
    161                         canvas->concat(mat);
    162                         canvas->drawRect(clearRect, clearPaint);
    163                     }
    164                 } else {
    165                     canvas->clear(0xFF000000);
    166                 }
    167 
    168                 fNumSaved = 0;
    169             }
    170 
    171             SkASSERT(fNumSaved < kNumBeforeClear);
    172 
    173             canvas->setMatrix(SkMatrix::I());
    174 
    175             fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
    176             fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
    177             if (fAligned) {
    178                 // make the translations integer aligned
    179                 fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
    180                 fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
    181             }
    182 
    183             mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
    184 
    185             if (kScale_Type == fType) {
    186                 fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
    187                 mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
    188             } else if (kRotate_Type == fType) {
    189                 fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
    190                 mat.preRotate(fSaved[fNumSaved][2]);
    191             }
    192 
    193             canvas->concat(mat);
    194             if (fUseAtlas) {
    195                 const int curCell = i % (kNumAtlasedX * kNumAtlasedY);
    196                 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
    197 
    198                 if (fUseDrawVertices) {
    199                     SkPoint uvs[4] = {
    200                         { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fBottom) },
    201                         { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fTop) },
    202                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
    203                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
    204                     };
    205                     canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
    206                                                               4, verts, uvs, nullptr, 6, indices),
    207                                          SkBlendMode::kModulate, p2);
    208                 } else {
    209                     canvas->drawBitmapRect(fAtlas, src, dst, &p,
    210                                            SkCanvas::kFast_SrcRectConstraint);
    211                 }
    212             } else {
    213                 canvas->drawBitmapRect(fCheckerboard, dst, &p);
    214             }
    215         }
    216     }
    217 
    218 private:
    219     static const int kCheckerboardWidth = 64;
    220     static const int kCheckerboardHeight = 128;
    221 
    222     static const int kAtlasCellWidth = 48;
    223     static const int kAtlasCellHeight = 36;
    224     static const int kNumAtlasedX = 5;
    225     static const int kNumAtlasedY = 5;
    226     static const int kAtlasSpacer = 2;
    227     static const int kTotAtlasWidth  = kNumAtlasedX * kAtlasCellWidth +
    228                                        (kNumAtlasedX+1) * kAtlasSpacer;
    229     static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
    230                                        (kNumAtlasedY+1) * kAtlasSpacer;
    231     static const int kNumBeforeClear = 100;
    232 
    233     Type     fType;
    234     Clear    fClear;
    235     bool     fAligned;
    236     bool     fUseAtlas;
    237     bool     fUseDrawVertices;
    238     SkString fName;
    239     int      fNumSaved; // num draws stored in 'fSaved'
    240     bool     fInitialized;
    241 
    242     // 0 & 1 are always x & y translate. 2 is either scale or rotate.
    243     SkScalar fSaved[kNumBeforeClear][3];
    244 
    245     SkBitmap fCheckerboard;
    246     SkBitmap fAtlas;
    247     SkIRect  fAtlasRects[kNumAtlasedX][kNumAtlasedY];
    248 
    249     // Note: the resulting checker board has transparency
    250     void makeCheckerboard() {
    251         static int kCheckSize = 16;
    252 
    253         fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
    254         for (int y = 0; y < kCheckerboardHeight; ++y) {
    255             int even = (y / kCheckSize) % 2;
    256 
    257             SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
    258 
    259             for (int x = 0; x < kCheckerboardWidth; ++x) {
    260                 if (even == (x / kCheckSize) % 2) {
    261                     *scanline++ = 0xFFFF0000;
    262                 } else {
    263                     *scanline++ = 0x00000000;
    264                 }
    265             }
    266         }
    267     }
    268 
    269     // Note: the resulting atlas has transparency
    270     void makeAtlas() {
    271         SkRandom rand;
    272 
    273         SkColor colors[kNumAtlasedX][kNumAtlasedY];
    274 
    275         for (int y = 0; y < kNumAtlasedY; ++y) {
    276             for (int x = 0; x < kNumAtlasedX; ++x) {
    277                 colors[x][y] = rand.nextU() | 0xff000000;
    278                 fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer),
    279                                                       kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer),
    280                                                       kAtlasCellWidth,
    281                                                       kAtlasCellHeight);
    282             }
    283         }
    284 
    285         fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
    286 
    287         for (int y = 0; y < kTotAtlasHeight; ++y) {
    288             int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
    289             bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
    290 
    291             SkPMColor* scanline = fAtlas.getAddr32(0, y);
    292 
    293             for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) {
    294                 int colorX = x / (kAtlasCellWidth + kAtlasSpacer);
    295                 bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer;
    296 
    297                 if (inColorX && inColorY) {
    298                     SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY);
    299                     *scanline = colors[colorX][colorY];
    300                 } else {
    301                     *scanline = 0x00000000;
    302                 }
    303             }
    304         }
    305     }
    306 
    307     typedef Benchmark INHERITED;
    308 };
    309 
    310 // Partial clear
    311 DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kPartial_Clear);)
    312 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear);)
    313 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear, true);)
    314 DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kPartial_Clear);)
    315 
    316 // Full clear
    317 DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kFull_Clear);)
    318 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear);)
    319 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, true);)
    320 DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kFull_Clear);)
    321 
    322 // Atlased
    323 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true);)
    324 DEF_BENCH(return new GameBench(
    325                          GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true, true);)
    326