blob: 32804bae13a45191e62f5ae3f2d7bf2e7b4a7d7e [file] [log] [blame]
bsalomonb3cb2142016-09-08 12:35:32 -07001/*
2 * Copyright 2016 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 "GrContext.h"
10#include "sk_tool_utils.h"
11#include "SkCanvas.h"
12#include "SkImage.h"
13#include "SkSurface.h"
14
15/** These benchmarks were designed to measure changes to GrResourceCache's replacement policy */
16
17//////////////////////////////////////////////////////////////////////////////
18
19// The width/height of the images to draw. The small size underestimates the value of a good
20// replacement strategy since the texture uploads are quite small. However, the effects are still
21// significant and this lets the benchmarks complete a lot faster, especially on mobile.
22static constexpr int kS = 25;
23
24static void make_images(sk_sp<SkImage> imgs[], int cnt) {
25 for (int i = 0; i < cnt; ++i) {
26 SkBitmap bmp = sk_tool_utils::create_checkerboard_bitmap(kS, kS, SK_ColorBLACK,
27 SK_ColorCYAN, 10);
28 imgs[i] = SkImage::MakeFromBitmap(bmp);
29 }
30}
31
32static void draw_image(SkCanvas* canvas, SkImage* img) {
33 // Make the paint transparent to avoid any issues of deferred tiler blending
34 // optmizations
35 SkPaint paint;
36 paint.setAlpha(0x10);
37 canvas->drawImage(img, 0, 0, &paint);
38}
39
40void set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) {
41 // This is inexact but we attempt to figure out a baseline number of resources GrContext needs
42 // to render an SkImage and add one additional resource for each image we'd like to fit.
43 GrContext* context = canvas->getGrContext();
44 SkASSERT(context);
45 context->flush();
46 context->purgeAllUnlockedResources();
47 sk_sp<SkImage> image;
48 make_images(&image, 1);
49 draw_image(canvas, image.get());
50 context->flush();
51 int baselineCount;
52 context->getResourceCacheUsage(&baselineCount, nullptr);
53 baselineCount -= 1; // for the image's textures.
54 context->setResourceCacheLimits(baselineCount + approxImagesInBudget, 1 << 30);
55 context->purgeAllUnlockedResources();
56}
57
58//////////////////////////////////////////////////////////////////////////////
59
60/**
61 * Tests repeatedly drawing the same set of images in each frame. Different instances of the bench
62 * run with different cache sizes and either repeat the image order each frame or use a random
63 * order. Every variation of this bench draws the same image set, only the budget and order of
64 * images differs. Since the total fill is the same they can be cross-compared.
65 */
66class ImageCacheBudgetBench : public Benchmark {
67public:
68 /** budgetSize is the number of images that can fit in the cache. 100 images will be drawn. */
69 ImageCacheBudgetBench(int budgetSize, bool shuffle)
70 : fBudgetSize(budgetSize)
71 , fShuffle(shuffle)
72 , fIndices(nullptr) {
73 float imagesOverBudget = float(kImagesToDraw) / budgetSize;
74 // Make the benchmark name contain the percentage of the budget that is used in each
75 // simulated frame.
76 fName.printf("image_cache_budget_%.0f%s", imagesOverBudget * 100,
77 (shuffle ? "_shuffle" : ""));
78 }
79
80 bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
81
82protected:
83 const char* onGetName() override {
84 return fName.c_str();
85 }
86
87 void onPerCanvasPreDraw(SkCanvas* canvas) override {
88 GrContext* context = canvas->getGrContext();
89 SkASSERT(context);
90 context->getResourceCacheLimits(&fOldCount, &fOldBytes);
91 set_cache_budget(canvas, fBudgetSize);
92 make_images(fImages, kImagesToDraw);
93 if (fShuffle) {
94 SkRandom random;
95 fIndices.reset(new int[kSimulatedFrames * kImagesToDraw]);
96 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
97 int* base = fIndices.get() + frame * kImagesToDraw;
98 for (int i = 0; i < kImagesToDraw; ++i) {
99 base[i] = i;
100 }
101 for (int i = 0; i < kImagesToDraw - 1; ++i) {
102 int other = random.nextULessThan(kImagesToDraw - i) + i;
103 SkTSwap(base[i], base[other]);
104 }
105 }
106 }
107 }
108
109 void onPerCanvasPostDraw(SkCanvas* canvas) override {
110 GrContext* context = canvas->getGrContext();
111 SkASSERT(context);
112 context->setResourceCacheLimits(fOldCount, fOldBytes);
113 for (int i = 0; i < kImagesToDraw; ++i) {
114 fImages[i].reset();
115 }
116 fIndices.reset(nullptr);
117 }
118
119 void onDraw(int loops, SkCanvas* canvas) override {
120 for (int i = 0; i < loops; ++i) {
121 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
122 for (int j = 0; j < kImagesToDraw; ++j) {
123 int idx;
124 if (fShuffle) {
125 idx = fIndices[frame * kImagesToDraw + j];
126 } else {
127 idx = j;
128 }
129 draw_image(canvas, fImages[idx].get());
130 }
131 // Simulate a frame boundary by flushing. This should notify GrResourceCache.
132 canvas->flush();
133 }
134 }
135 }
136
137private:
138 static constexpr int kImagesToDraw = 100;
139 static constexpr int kSimulatedFrames = 5;
140
141 int fBudgetSize;
142 bool fShuffle;
143 SkString fName;
144 sk_sp<SkImage> fImages[kImagesToDraw];
145 SkAutoTDeleteArray<int> fIndices;
146 size_t fOldBytes;
147 int fOldCount;
148
149 typedef Benchmark INHERITED;
150};
151
152DEF_BENCH( return new ImageCacheBudgetBench(105, false); )
153
154DEF_BENCH( return new ImageCacheBudgetBench(90, false); )
155
156DEF_BENCH( return new ImageCacheBudgetBench(80, false); )
157
158DEF_BENCH( return new ImageCacheBudgetBench(50, false); )
159
160DEF_BENCH( return new ImageCacheBudgetBench(105, true); )
161
162DEF_BENCH( return new ImageCacheBudgetBench(90, true); )
163
164DEF_BENCH( return new ImageCacheBudgetBench(80, true); )
165
166DEF_BENCH( return new ImageCacheBudgetBench(50, true); )
167
168//////////////////////////////////////////////////////////////////////////////
169
170/**
171 * Similar to above but changes between being over and under budget by varying the number of images
172 * rendered. This is not directly comparable to the non-dynamic benchmarks.
173 */
174class ImageCacheBudgetDynamicBench : public Benchmark {
175public:
176 enum class Mode {
177 // Increase from min to max images drawn gradually over simulated frames and then back.
178 kPingPong,
179 // Alternate between under and over budget every other simulated frame.
180 kFlipFlop
181 };
182
183 ImageCacheBudgetDynamicBench(Mode mode) : fMode(mode) {}
184
185 bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
186
187protected:
188 const char* onGetName() override {
189 switch (fMode) {
190 case Mode::kPingPong:
191 return "image_cache_budget_dynamic_ping_pong";
192 case Mode::kFlipFlop:
193 return "image_cache_budget_dynamic_flip_flop";
194 }
195 return "";
196 }
197
198 void onPerCanvasPreDraw(SkCanvas* canvas) override {
199 GrContext* context = canvas->getGrContext();
200 SkASSERT(context);
201 context->getResourceCacheLimits(&fOldCount, &fOldBytes);
202 make_images(fImages, kMaxImagesToDraw);
203 set_cache_budget(canvas, kImagesInBudget);
204 }
205
206 void onPerCanvasPostDraw(SkCanvas* canvas) override {
207 GrContext* context = canvas->getGrContext();
208 SkASSERT(context);
209 context->setResourceCacheLimits(fOldCount, fOldBytes);
210 for (int i = 0; i < kMaxImagesToDraw; ++i) {
211 fImages[i].reset();
212 }
213 }
214
215 void onDraw(int loops, SkCanvas* canvas) override {
216 int delta = 0;
217 switch (fMode) {
218 case Mode::kPingPong:
219 delta = 1;
220 break;
221 case Mode::kFlipFlop:
222 delta = kMaxImagesToDraw - kMinImagesToDraw;
223 break;
224 }
225 for (int i = 0; i < loops; ++i) {
226 int imgsToDraw = kMinImagesToDraw;
227 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
228 for (int j = 0; j < imgsToDraw; ++j) {
229 draw_image(canvas, fImages[j].get());
230 }
231 imgsToDraw += delta;
232 if (imgsToDraw > kMaxImagesToDraw || imgsToDraw < kMinImagesToDraw) {
233 delta = -delta;
234 imgsToDraw += 2 * delta;
235 }
236 // Simulate a frame boundary by flushing. This should notify GrResourceCache.
237 canvas->flush();
238 }
239 }
240 }
241
242private:
243 static constexpr int kImagesInBudget = 25;
244 static constexpr int kMinImagesToDraw = 15;
245 static constexpr int kMaxImagesToDraw = 35;
246 static constexpr int kSimulatedFrames = 80;
247
248 Mode fMode;
249 sk_sp<SkImage> fImages[kMaxImagesToDraw];
250 size_t fOldBytes;
251 int fOldCount;
252
253 typedef Benchmark INHERITED;
254};
255
256DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kPingPong); )
257DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kFlipFlop); )