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#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
18class PictureRecordBench : public Benchmark {
19public:
20    PictureRecordBench(const char name[])  {
21        fName.printf("picture_record_%s", name);
22    }
23
24    virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
25        return backend == kNonRendering_Backend;
26    }
27
28    enum {
29        PICTURE_WIDTH = 1000,
30        PICTURE_HEIGHT = 4000,
31    };
32protected:
33    virtual const char* onGetName() SK_OVERRIDE {
34        return fName.c_str();
35    }
36private:
37    SkString fName;
38    typedef Benchmark INHERITED;
39};
40
41
42static const int kMaxLoopsPerCanvas = 10000;
43
44/*
45 *  An SkPicture has internal dictionaries to store bitmaps, matrices, paints,
46 *  and regions.  This bench populates those dictionaries to test the speed of
47 *  reading and writing to those particular dictionary data structures.
48 */
49class DictionaryRecordBench : public PictureRecordBench {
50public:
51    DictionaryRecordBench() : INHERITED("dictionaries") {}
52
53protected:
54    virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
55        SkPictureRecorder recorder;
56        SkCanvas* canvas = NULL;
57
58        const SkPoint translateDelta = getTranslateDelta(loops);
59
60        for (int i = 0; i < loops; i++) {
61            if (0 == i % kMaxLoopsPerCanvas) {
62                SkAutoTUnref<SkPicture> picture(recorder.endRecording());
63                canvas = recorder.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT, NULL, 0);
64            }
65
66            SkColor color = SK_ColorYELLOW + (i % 255);
67            SkIRect rect = SkIRect::MakeWH(i % PICTURE_WIDTH, i % PICTURE_HEIGHT);
68
69            canvas->save();
70
71            // set the clip to the given region
72            SkRegion region;
73            region.setRect(rect);
74            canvas->clipRegion(region);
75
76            // fill the clip with a color
77            SkPaint paint;
78            paint.setColor(color);
79            canvas->drawPaint(paint);
80
81            // set a matrix on the canvas
82            SkMatrix matrix;
83            matrix.setRotate(SkIntToScalar(i % 360));
84            canvas->setMatrix(matrix);
85
86            // create a simple bitmap
87            SkBitmap bitmap;
88            bitmap.allocPixels(SkImageInfo::Make(10, 10,
89                                                 kRGB_565_SkColorType, kOpaque_SkAlphaType));
90
91            // draw a single color into the bitmap
92            SkCanvas bitmapCanvas(bitmap);
93            bitmapCanvas.drawColor(SkColorSetA(color, i % 255));
94
95            // draw the bitmap onto the canvas
96            canvas->drawBitmapMatrix(bitmap, matrix);
97
98            canvas->restore();
99            canvas->translate(translateDelta.fX, translateDelta.fY);
100        }
101    }
102
103    SkPoint getTranslateDelta(int M) {
104        SkIPoint canvasSize = onGetSize();
105        return SkPoint::Make(SkIntToScalar((PICTURE_WIDTH - canvasSize.fX)/M),
106                             SkIntToScalar((PICTURE_HEIGHT- canvasSize.fY)/M));
107    }
108private:
109    typedef PictureRecordBench INHERITED;
110};
111
112/*
113 *  Populates the SkPaint dictionary with a large number of unique paint
114 *  objects that differ only by color
115 */
116class UniquePaintDictionaryRecordBench : public PictureRecordBench {
117public:
118    UniquePaintDictionaryRecordBench() : INHERITED("unique_paint_dictionary") { }
119
120protected:
121    virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
122        SkRandom rand;
123        SkPaint paint;
124        SkPictureRecorder recorder;
125        SkCanvas* canvas = NULL;
126        for (int i = 0; i < loops; i++) {
127            if (0 == i % kMaxLoopsPerCanvas) {
128                SkAutoTUnref<SkPicture> picture(recorder.endRecording());
129                canvas = recorder.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT, NULL, 0);
130            }
131            paint.setColor(rand.nextU());
132            canvas->drawPaint(paint);
133        }
134    }
135
136private:
137    typedef PictureRecordBench INHERITED;
138};
139
140/*
141 *  Populates the SkPaint dictionary with a number of unique paint
142 *  objects that get reused repeatedly.
143 *
144 *  Re-creating the paint objects in the inner loop slows the benchmark down 10%.
145 *  Using setColor(i % objCount) instead of a random color creates a very high rate
146 *  of hash conflicts, slowing us down 12%.
147 */
148class RecurringPaintDictionaryRecordBench : public PictureRecordBench {
149public:
150    RecurringPaintDictionaryRecordBench() : INHERITED("recurring_paint_dictionary") {
151        SkRandom rand;
152        for (int i = 0; i < ObjCount; i++) {
153            fPaint[i].setColor(rand.nextU());
154        }
155    }
156
157    enum {
158        ObjCount = 100,  // number of unique paint objects
159    };
160protected:
161    virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
162        SkPictureRecorder recorder;
163        SkCanvas* canvas = recorder.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT, NULL, 0);
164        for (int i = 0; i < loops; i++) {
165            canvas->drawPaint(fPaint[i % ObjCount]);
166        }
167    }
168
169private:
170    SkPaint fPaint [ObjCount];
171    typedef PictureRecordBench INHERITED;
172};
173
174///////////////////////////////////////////////////////////////////////////////
175
176DEF_BENCH( return new DictionaryRecordBench(); )
177DEF_BENCH( return new UniquePaintDictionaryRecordBench(); )
178DEF_BENCH( return new RecurringPaintDictionaryRecordBench(); )
179