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