1/*
2 * Copyright 2011 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#include "Benchmark.h"
8#include "SkCanvas.h"
9#include "SkColor.h"
10#include "SkPaint.h"
11#include "SkPicture.h"
12#include "SkPictureRecorder.h"
13#include "SkPoint.h"
14#include "SkRandom.h"
15#include "SkRect.h"
16#include "SkString.h"
17
18// This is designed to emulate about 4 screens of textual content
19
20
21class PicturePlaybackBench : public Benchmark {
22public:
23    PicturePlaybackBench(const char name[])  {
24        fName.printf("picture_playback_%s", name);
25        fPictureWidth = SkIntToScalar(PICTURE_WIDTH);
26        fPictureHeight = SkIntToScalar(PICTURE_HEIGHT);
27        fTextSize = SkIntToScalar(TEXT_SIZE);
28    }
29
30    enum {
31        PICTURE_WIDTH = 1000,
32        PICTURE_HEIGHT = 4000,
33        TEXT_SIZE = 10
34    };
35protected:
36    virtual const char* onGetName() {
37        return fName.c_str();
38    }
39
40    virtual void onDraw(int loops, SkCanvas* canvas) {
41
42        SkPictureRecorder recorder;
43        SkCanvas* pCanvas = recorder.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT, nullptr, 0);
44        this->recordCanvas(pCanvas);
45        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
46
47        const SkPoint translateDelta = getTranslateDelta(loops);
48
49        for (int i = 0; i < loops; i++) {
50            picture->playback(canvas);
51            canvas->translate(translateDelta.fX, translateDelta.fY);
52        }
53    }
54
55    virtual void recordCanvas(SkCanvas* canvas) = 0;
56    virtual SkPoint getTranslateDelta(int N) {
57        SkIPoint canvasSize = onGetSize();
58        return SkPoint::Make(SkIntToScalar((PICTURE_WIDTH - canvasSize.fX)/N),
59                             SkIntToScalar((PICTURE_HEIGHT- canvasSize.fY)/N));
60    }
61
62    SkString fName;
63    SkScalar fPictureWidth;
64    SkScalar fPictureHeight;
65    SkScalar fTextSize;
66private:
67    typedef Benchmark INHERITED;
68};
69
70
71class TextPlaybackBench : public PicturePlaybackBench {
72public:
73    TextPlaybackBench() : INHERITED("drawText") { }
74protected:
75    void recordCanvas(SkCanvas* canvas) override {
76        SkPaint paint;
77        paint.setTextSize(fTextSize);
78        paint.setColor(SK_ColorBLACK);
79
80        const char* text = "Hamburgefons";
81        size_t len = strlen(text);
82        const SkScalar textWidth = paint.measureText(text, len);
83
84        for (SkScalar x = 0; x < fPictureWidth; x += textWidth) {
85            for (SkScalar y = 0; y < fPictureHeight; y += fTextSize) {
86                canvas->drawText(text, len, x, y, paint);
87            }
88        }
89    }
90private:
91    typedef PicturePlaybackBench INHERITED;
92};
93
94class PosTextPlaybackBench : public PicturePlaybackBench {
95public:
96    PosTextPlaybackBench(bool drawPosH)
97        : INHERITED(drawPosH ? "drawPosTextH" : "drawPosText")
98        , fDrawPosH(drawPosH) { }
99protected:
100    void recordCanvas(SkCanvas* canvas) override {
101        SkPaint paint;
102        paint.setTextSize(fTextSize);
103        paint.setColor(SK_ColorBLACK);
104
105        const char* text = "Hamburgefons";
106        size_t len = strlen(text);
107        const SkScalar textWidth = paint.measureText(text, len);
108
109        SkScalar* adv = new SkScalar[len];
110        paint.getTextWidths(text, len, adv);
111
112        for (SkScalar x = 0; x < fPictureWidth; x += textWidth) {
113            for (SkScalar y = 0; y < fPictureHeight; y += fTextSize) {
114
115                SkPoint* pos = new SkPoint[len];
116                SkScalar advX = 0;
117
118                for (size_t i = 0; i < len; i++) {
119                    if (fDrawPosH)
120                        pos[i].set(x + advX, y);
121                    else
122                        pos[i].set(x + advX, y + i);
123                    advX += adv[i];
124                }
125
126                canvas->drawPosText(text, len, pos, paint);
127                delete[] pos;
128            }
129        }
130        delete[] adv;
131    }
132private:
133    bool fDrawPosH;
134    typedef PicturePlaybackBench INHERITED;
135};
136
137
138///////////////////////////////////////////////////////////////////////////////
139
140DEF_BENCH( return new TextPlaybackBench(); )
141DEF_BENCH( return new PosTextPlaybackBench(true); )
142DEF_BENCH( return new PosTextPlaybackBench(false); )
143
144// Chrome draws into small tiles with impl-side painting.
145// This benchmark measures the relative performance of our bounding-box hierarchies,
146// both when querying tiles perfectly and when not.
147enum BBH  { kNone, kRTree };
148enum Mode { kTiled, kRandom };
149class TiledPlaybackBench : public Benchmark {
150public:
151    TiledPlaybackBench(BBH bbh, Mode mode) : fBBH(bbh), fMode(mode), fName("tiled_playback") {
152        switch (fBBH) {
153            case kNone:     fName.append("_none"    ); break;
154            case kRTree:    fName.append("_rtree"   ); break;
155        }
156        switch (fMode) {
157            case kTiled:  fName.append("_tiled" ); break;
158            case kRandom: fName.append("_random"); break;
159        }
160    }
161
162    const char* onGetName() override { return fName.c_str(); }
163    SkIPoint onGetSize() override { return SkIPoint::Make(1024,1024); }
164
165    void onDelayedSetup() override {
166        std::unique_ptr<SkBBHFactory> factory;
167        switch (fBBH) {
168            case kNone:                                                 break;
169            case kRTree:    factory.reset(new SkRTreeFactory);          break;
170        }
171
172        SkPictureRecorder recorder;
173        SkCanvas* canvas = recorder.beginRecording(1024, 1024, factory.get());
174            SkRandom rand;
175            for (int i = 0; i < 10000; i++) {
176                SkScalar x = rand.nextRangeScalar(0, 1024),
177                         y = rand.nextRangeScalar(0, 1024),
178                         w = rand.nextRangeScalar(0, 128),
179                         h = rand.nextRangeScalar(0, 128);
180                SkPaint paint;
181                paint.setColor(rand.nextU());
182                paint.setAlpha(0xFF);
183                canvas->drawRect(SkRect::MakeXYWH(x,y,w,h), paint);
184            }
185        fPic = recorder.finishRecordingAsPicture();
186    }
187
188    void onDraw(int loops, SkCanvas* canvas) override {
189        for (int i = 0; i < loops; i++) {
190            // This inner loop guarantees we make the same choices for all bench variants.
191            SkRandom rand;
192            for (int j = 0; j < 10; j++) {
193                SkScalar x = 0, y = 0;
194                switch (fMode) {
195                    case kTiled:  x = SkScalar(256 * rand.nextULessThan(4));
196                                  y = SkScalar(256 * rand.nextULessThan(4));
197                                  break;
198                    case kRandom: x = rand.nextRangeScalar(0, 768);
199                                  y = rand.nextRangeScalar(0, 768);
200                                  break;
201                }
202                SkAutoCanvasRestore ar(canvas, true/*save now*/);
203                canvas->clipRect(SkRect::MakeXYWH(x,y,256,256));
204                fPic->playback(canvas);
205            }
206        }
207    }
208
209private:
210    BBH                 fBBH;
211    Mode                fMode;
212    SkString            fName;
213    sk_sp<SkPicture>    fPic;
214};
215
216DEF_BENCH( return new TiledPlaybackBench(kNone,     kRandom); )
217DEF_BENCH( return new TiledPlaybackBench(kNone,     kTiled ); )
218DEF_BENCH( return new TiledPlaybackBench(kRTree,    kRandom); )
219DEF_BENCH( return new TiledPlaybackBench(kRTree,    kTiled ); )
220