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 
     15 // This bench simulates the calls Skia sees from various HTML5 canvas
     16 // game bench marks
     17 class GameBench : public Benchmark {
     18 public:
     19     enum Type {
     20         kScale_Type,
     21         kTranslate_Type,
     22         kRotate_Type
     23     };
     24 
     25     enum Clear {
     26         kFull_Clear,
     27         kPartial_Clear
     28     };
     29 
     30     GameBench(Type type, Clear clear,
     31               bool aligned = false, bool useAtlas = false,
     32               bool useDrawVertices = false)
     33         : fType(type)
     34         , fClear(clear)
     35         , fAligned(aligned)
     36         , fUseAtlas(useAtlas)
     37         , fUseDrawVertices(useDrawVertices)
     38         , fName("game")
     39         , fNumSaved(0)
     40         , fInitialized(false) {
     41 
     42         switch (fType) {
     43         case kScale_Type:
     44             fName.append("_scale");
     45             break;
     46         case kTranslate_Type:
     47             fName.append("_trans");
     48             break;
     49         case kRotate_Type:
     50             fName.append("_rot");
     51             break;
     52         };
     53 
     54         if (aligned) {
     55             fName.append("_aligned");
     56         }
     57 
     58         if (kPartial_Clear == clear) {
     59             fName.append("_partial");
     60         } else {
     61             fName.append("_full");
     62         }
     63 
     64         if (useAtlas) {
     65             fName.append("_atlas");
     66         }
     67 
     68         if (useDrawVertices) {
     69             fName.append("_drawVerts");
     70         }
     71 
     72         // It's HTML 5 canvas, so always AA
     73         fName.append("_aa");
     74     }
     75 
     76 protected:
     77     virtual const char* onGetName() SK_OVERRIDE {
     78         return fName.c_str();
     79     }
     80 
     81     virtual void onPreDraw() SK_OVERRIDE {
     82         if (!fInitialized) {
     83             this->makeCheckerboard();
     84             this->makeAtlas();
     85             fInitialized = true;
     86         }
     87     }
     88 
     89     virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
     90         SkRandom scaleRand;
     91         SkRandom transRand;
     92         SkRandom rotRand;
     93 
     94         int width, height;
     95         if (fUseAtlas) {
     96             width = kAtlasCellWidth;
     97             height = kAtlasCellHeight;
     98         } else {
     99             width = kCheckerboardWidth;
    100             height = kCheckerboardHeight;
    101         }
    102 
    103         SkPaint clearPaint;
    104         clearPaint.setColor(0xFF000000);
    105         clearPaint.setAntiAlias(true);
    106 
    107         SkISize size = canvas->getDeviceSize();
    108 
    109         SkScalar maxTransX, maxTransY;
    110 
    111         if (kScale_Type == fType) {
    112             maxTransX = size.fWidth  - (1.5f * width);
    113             maxTransY = size.fHeight - (1.5f * height);
    114         } else if (kTranslate_Type == fType) {
    115             maxTransX = SkIntToScalar(size.fWidth  - width);
    116             maxTransY = SkIntToScalar(size.fHeight - height);
    117         } else {
    118             SkASSERT(kRotate_Type == fType);
    119             // Yes, some rotations will be off the top and left sides
    120             maxTransX = size.fWidth  - SK_ScalarSqrt2 * height;
    121             maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
    122         }
    123 
    124         SkMatrix mat;
    125         SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
    126         SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
    127         SkPoint verts[4] = { // for drawVertices path
    128             { 0, 0 },
    129             { 0, SkIntToScalar(height) },
    130             { SkIntToScalar(width), SkIntToScalar(height) },
    131             { SkIntToScalar(width), 0 }
    132         };
    133         uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
    134 
    135         SkPaint p;
    136         p.setColor(0xFF000000);
    137         p.setFilterLevel(SkPaint::kLow_FilterLevel);
    138 
    139         SkPaint p2;         // for drawVertices path
    140         p2.setColor(0xFF000000);
    141         p2.setFilterLevel(SkPaint::kLow_FilterLevel);
    142         p2.setShader(SkShader::CreateBitmapShader(fAtlas,
    143                                                   SkShader::kClamp_TileMode,
    144                                                   SkShader::kClamp_TileMode))->unref();
    145 
    146         for (int i = 0; i < loops; ++i, ++fNumSaved) {
    147             if (0 == i % kNumBeforeClear) {
    148                 if (kPartial_Clear == fClear) {
    149                     for (int j = 0; j < fNumSaved; ++j) {
    150                         canvas->setMatrix(SkMatrix::I());
    151                         mat.setTranslate(fSaved[j][0], fSaved[j][1]);
    152 
    153                         if (kScale_Type == fType) {
    154                             mat.preScale(fSaved[j][2], fSaved[j][2]);
    155                         } else if (kRotate_Type == fType) {
    156                             mat.preRotate(fSaved[j][2]);
    157                         }
    158 
    159                         canvas->concat(mat);
    160                         canvas->drawRect(clearRect, clearPaint);
    161                     }
    162                 } else {
    163                     canvas->clear(0xFF000000);
    164                 }
    165 
    166                 fNumSaved = 0;
    167             }
    168 
    169             SkASSERT(fNumSaved < kNumBeforeClear);
    170 
    171             canvas->setMatrix(SkMatrix::I());
    172 
    173             fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
    174             fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
    175             if (fAligned) {
    176                 // make the translations integer aligned
    177                 fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
    178                 fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
    179             }
    180 
    181             mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
    182 
    183             if (kScale_Type == fType) {
    184                 fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
    185                 mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
    186             } else if (kRotate_Type == fType) {
    187                 fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
    188                 mat.preRotate(fSaved[fNumSaved][2]);
    189             }
    190 
    191             canvas->concat(mat);
    192             if (fUseAtlas) {
    193                 const int curCell = i % (kNumAtlasedX * kNumAtlasedY);
    194                 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
    195 
    196                 if (fUseDrawVertices) {
    197                     SkPoint uvs[4] = {
    198                         { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fBottom) },
    199                         { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fTop) },
    200                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
    201                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
    202                     };
    203                     canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
    204                                          4, verts, uvs, NULL, NULL,
    205                                          indices, 6, p2);
    206                 } else {
    207                     canvas->drawBitmapRect(fAtlas, &src, dst, &p,
    208                                            SkCanvas::kBleed_DrawBitmapRectFlag);
    209                 }
    210             } else {
    211                 canvas->drawBitmapRect(fCheckerboard, NULL, dst, &p);
    212             }
    213         }
    214     }
    215 
    216 private:
    217     static const int kCheckerboardWidth = 64;
    218     static const int kCheckerboardHeight = 128;
    219 
    220     static const int kAtlasCellWidth = 48;
    221     static const int kAtlasCellHeight = 36;
    222     static const int kNumAtlasedX = 5;
    223     static const int kNumAtlasedY = 5;
    224     static const int kAtlasSpacer = 2;
    225     static const int kTotAtlasWidth  = kNumAtlasedX * kAtlasCellWidth +
    226                                        (kNumAtlasedX+1) * kAtlasSpacer;
    227     static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
    228                                        (kNumAtlasedY+1) * kAtlasSpacer;
    229     static const int kNumBeforeClear = 100;
    230 
    231     Type     fType;
    232     Clear    fClear;
    233     bool     fAligned;
    234     bool     fUseAtlas;
    235     bool     fUseDrawVertices;
    236     SkString fName;
    237     int      fNumSaved; // num draws stored in 'fSaved'
    238     bool     fInitialized;
    239 
    240     // 0 & 1 are always x & y translate. 2 is either scale or rotate.
    241     SkScalar fSaved[kNumBeforeClear][3];
    242 
    243     SkBitmap fCheckerboard;
    244     SkBitmap fAtlas;
    245     SkIRect  fAtlasRects[kNumAtlasedX][kNumAtlasedY];
    246 
    247     // Note: the resulting checker board has transparency
    248     void makeCheckerboard() {
    249         static int kCheckSize = 16;
    250 
    251         fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
    252         SkAutoLockPixels lock(fCheckerboard);
    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         SkAutoLockPixels lock(fAtlas);
    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 SkNEW_ARGS(GameBench, (GameBench::kScale_Type,
    312                                             GameBench::kPartial_Clear)); )
    313 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
    314                                             GameBench::kPartial_Clear)); )
    315 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
    316                                             GameBench::kPartial_Clear, true)); )
    317 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type,
    318                                             GameBench::kPartial_Clear)); )
    319 
    320 // Full clear
    321 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kScale_Type,
    322                                             GameBench::kFull_Clear)); )
    323 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
    324                                             GameBench::kFull_Clear)); )
    325 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
    326                                             GameBench::kFull_Clear, true)); )
    327 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type,
    328                                             GameBench::kFull_Clear)); )
    329 
    330 // Atlased
    331 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
    332                                             GameBench::kFull_Clear, false, true)); )
    333 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
    334                                             GameBench::kFull_Clear, false, true, true)); )
    335