blob: bedc7b3ed25a8ea2aa55f913f3ddfc56e7d4b179 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "bench/Benchmark.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkImage.h"
11#include "include/core/SkSurface.h"
Robert Phillipsf0288102020-07-06 13:45:34 -040012#include "include/gpu/GrDirectContext.h"
Adlai Hollera0693042020-10-14 11:23:11 -040013#include "src/gpu/GrDirectContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "tools/ToolUtils.h"
bsalomonb3cb2142016-09-08 12:35:32 -070015
brianosman9f1f6e22016-09-15 08:33:02 -070016
Ben Wagnerf08d1d02018-06-18 15:11:00 -040017#include <utility>
18
bsalomonb3cb2142016-09-08 12:35:32 -070019/** These benchmarks were designed to measure changes to GrResourceCache's replacement policy */
20
21//////////////////////////////////////////////////////////////////////////////
22
23// The width/height of the images to draw. The small size underestimates the value of a good
24// replacement strategy since the texture uploads are quite small. However, the effects are still
25// significant and this lets the benchmarks complete a lot faster, especially on mobile.
26static constexpr int kS = 25;
27
28static void make_images(sk_sp<SkImage> imgs[], int cnt) {
29 for (int i = 0; i < cnt; ++i) {
Mike Reedac9f0c92020-12-23 10:11:33 -050030 imgs[i] = ToolUtils::create_checkerboard_image(kS, kS, SK_ColorBLACK, SK_ColorCYAN, 10);
bsalomonb3cb2142016-09-08 12:35:32 -070031 }
32}
33
34static void draw_image(SkCanvas* canvas, SkImage* img) {
35 // Make the paint transparent to avoid any issues of deferred tiler blending
36 // optmizations
37 SkPaint paint;
38 paint.setAlpha(0x10);
39 canvas->drawImage(img, 0, 0, &paint);
40}
41
42void set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) {
43 // This is inexact but we attempt to figure out a baseline number of resources GrContext needs
44 // to render an SkImage and add one additional resource for each image we'd like to fit.
Robert Phillipsf0288102020-07-06 13:45:34 -040045 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -070046 SkASSERT(context);
Greg Daniel0a2464f2020-05-14 15:45:44 -040047 context->flushAndSubmit();
Robert Phillipsdbaf3172019-02-06 15:12:53 -050048 context->priv().testingOnly_purgeAllUnlockedResources();
bsalomonb3cb2142016-09-08 12:35:32 -070049 sk_sp<SkImage> image;
50 make_images(&image, 1);
51 draw_image(canvas, image.get());
Greg Daniel0a2464f2020-05-14 15:45:44 -040052 context->flushAndSubmit();
bsalomonb3cb2142016-09-08 12:35:32 -070053 int baselineCount;
54 context->getResourceCacheUsage(&baselineCount, nullptr);
55 baselineCount -= 1; // for the image's textures.
56 context->setResourceCacheLimits(baselineCount + approxImagesInBudget, 1 << 30);
Robert Phillipsdbaf3172019-02-06 15:12:53 -050057 context->priv().testingOnly_purgeAllUnlockedResources();
bsalomonb3cb2142016-09-08 12:35:32 -070058}
59
60//////////////////////////////////////////////////////////////////////////////
61
62/**
63 * Tests repeatedly drawing the same set of images in each frame. Different instances of the bench
64 * run with different cache sizes and either repeat the image order each frame or use a random
65 * order. Every variation of this bench draws the same image set, only the budget and order of
66 * images differs. Since the total fill is the same they can be cross-compared.
67 */
68class ImageCacheBudgetBench : public Benchmark {
69public:
70 /** budgetSize is the number of images that can fit in the cache. 100 images will be drawn. */
71 ImageCacheBudgetBench(int budgetSize, bool shuffle)
72 : fBudgetSize(budgetSize)
73 , fShuffle(shuffle)
74 , fIndices(nullptr) {
75 float imagesOverBudget = float(kImagesToDraw) / budgetSize;
76 // Make the benchmark name contain the percentage of the budget that is used in each
77 // simulated frame.
78 fName.printf("image_cache_budget_%.0f%s", imagesOverBudget * 100,
79 (shuffle ? "_shuffle" : ""));
80 }
81
82 bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
83
84protected:
85 const char* onGetName() override {
86 return fName.c_str();
87 }
88
89 void onPerCanvasPreDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -040090 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -070091 SkASSERT(context);
Robert Phillipscf39f372019-09-03 10:29:20 -040092 fOldBytes = context->getResourceCacheLimit();
bsalomonb3cb2142016-09-08 12:35:32 -070093 set_cache_budget(canvas, fBudgetSize);
94 make_images(fImages, kImagesToDraw);
95 if (fShuffle) {
96 SkRandom random;
97 fIndices.reset(new int[kSimulatedFrames * kImagesToDraw]);
98 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
99 int* base = fIndices.get() + frame * kImagesToDraw;
100 for (int i = 0; i < kImagesToDraw; ++i) {
101 base[i] = i;
102 }
103 for (int i = 0; i < kImagesToDraw - 1; ++i) {
104 int other = random.nextULessThan(kImagesToDraw - i) + i;
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400105 using std::swap;
106 swap(base[i], base[other]);
bsalomonb3cb2142016-09-08 12:35:32 -0700107 }
108 }
109 }
110 }
111
112 void onPerCanvasPostDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -0400113 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -0700114 SkASSERT(context);
Robert Phillipscf39f372019-09-03 10:29:20 -0400115 context->setResourceCacheLimit(fOldBytes);
bsalomonb3cb2142016-09-08 12:35:32 -0700116 for (int i = 0; i < kImagesToDraw; ++i) {
117 fImages[i].reset();
118 }
119 fIndices.reset(nullptr);
120 }
121
122 void onDraw(int loops, SkCanvas* canvas) override {
Robert Phillips2c21a112020-11-20 13:49:37 -0500123 auto dContext = GrAsDirectContext(canvas->recordingContext());
124
bsalomonb3cb2142016-09-08 12:35:32 -0700125 for (int i = 0; i < loops; ++i) {
126 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
127 for (int j = 0; j < kImagesToDraw; ++j) {
128 int idx;
129 if (fShuffle) {
130 idx = fIndices[frame * kImagesToDraw + j];
131 } else {
132 idx = j;
133 }
134 draw_image(canvas, fImages[idx].get());
135 }
136 // Simulate a frame boundary by flushing. This should notify GrResourceCache.
Robert Phillips2c21a112020-11-20 13:49:37 -0500137 if (dContext) {
138 dContext->flush();
139 }
bsalomonb3cb2142016-09-08 12:35:32 -0700140 }
141 }
142 }
143
144private:
145 static constexpr int kImagesToDraw = 100;
146 static constexpr int kSimulatedFrames = 5;
147
148 int fBudgetSize;
149 bool fShuffle;
150 SkString fName;
151 sk_sp<SkImage> fImages[kImagesToDraw];
Ben Wagner7ecc5962016-11-02 17:07:33 -0400152 std::unique_ptr<int[]> fIndices;
bsalomonb3cb2142016-09-08 12:35:32 -0700153 size_t fOldBytes;
bsalomonb3cb2142016-09-08 12:35:32 -0700154
John Stiles7571f9e2020-09-02 22:42:33 -0400155 using INHERITED = Benchmark;
bsalomonb3cb2142016-09-08 12:35:32 -0700156};
157
158DEF_BENCH( return new ImageCacheBudgetBench(105, false); )
159
160DEF_BENCH( return new ImageCacheBudgetBench(90, false); )
161
162DEF_BENCH( return new ImageCacheBudgetBench(80, false); )
163
164DEF_BENCH( return new ImageCacheBudgetBench(50, false); )
165
166DEF_BENCH( return new ImageCacheBudgetBench(105, true); )
167
168DEF_BENCH( return new ImageCacheBudgetBench(90, true); )
169
170DEF_BENCH( return new ImageCacheBudgetBench(80, true); )
171
172DEF_BENCH( return new ImageCacheBudgetBench(50, true); )
173
174//////////////////////////////////////////////////////////////////////////////
175
176/**
177 * Similar to above but changes between being over and under budget by varying the number of images
178 * rendered. This is not directly comparable to the non-dynamic benchmarks.
179 */
180class ImageCacheBudgetDynamicBench : public Benchmark {
181public:
182 enum class Mode {
183 // Increase from min to max images drawn gradually over simulated frames and then back.
184 kPingPong,
185 // Alternate between under and over budget every other simulated frame.
186 kFlipFlop
187 };
188
189 ImageCacheBudgetDynamicBench(Mode mode) : fMode(mode) {}
190
191 bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
192
193protected:
194 const char* onGetName() override {
195 switch (fMode) {
196 case Mode::kPingPong:
197 return "image_cache_budget_dynamic_ping_pong";
198 case Mode::kFlipFlop:
199 return "image_cache_budget_dynamic_flip_flop";
200 }
201 return "";
202 }
203
204 void onPerCanvasPreDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -0400205 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -0700206 SkASSERT(context);
207 context->getResourceCacheLimits(&fOldCount, &fOldBytes);
208 make_images(fImages, kMaxImagesToDraw);
209 set_cache_budget(canvas, kImagesInBudget);
210 }
211
212 void onPerCanvasPostDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -0400213 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -0700214 SkASSERT(context);
215 context->setResourceCacheLimits(fOldCount, fOldBytes);
216 for (int i = 0; i < kMaxImagesToDraw; ++i) {
217 fImages[i].reset();
218 }
219 }
220
221 void onDraw(int loops, SkCanvas* canvas) override {
Robert Phillips2c21a112020-11-20 13:49:37 -0500222 auto dContext = GrAsDirectContext(canvas->recordingContext());
223
bsalomonb3cb2142016-09-08 12:35:32 -0700224 int delta = 0;
225 switch (fMode) {
226 case Mode::kPingPong:
227 delta = 1;
228 break;
229 case Mode::kFlipFlop:
230 delta = kMaxImagesToDraw - kMinImagesToDraw;
231 break;
232 }
233 for (int i = 0; i < loops; ++i) {
234 int imgsToDraw = kMinImagesToDraw;
235 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
236 for (int j = 0; j < imgsToDraw; ++j) {
237 draw_image(canvas, fImages[j].get());
238 }
239 imgsToDraw += delta;
240 if (imgsToDraw > kMaxImagesToDraw || imgsToDraw < kMinImagesToDraw) {
241 delta = -delta;
242 imgsToDraw += 2 * delta;
243 }
244 // Simulate a frame boundary by flushing. This should notify GrResourceCache.
Robert Phillips2c21a112020-11-20 13:49:37 -0500245 if (dContext) {
246 dContext->flush();
247 }
bsalomonb3cb2142016-09-08 12:35:32 -0700248 }
249 }
250 }
251
252private:
253 static constexpr int kImagesInBudget = 25;
254 static constexpr int kMinImagesToDraw = 15;
255 static constexpr int kMaxImagesToDraw = 35;
256 static constexpr int kSimulatedFrames = 80;
257
258 Mode fMode;
259 sk_sp<SkImage> fImages[kMaxImagesToDraw];
260 size_t fOldBytes;
261 int fOldCount;
262
John Stiles7571f9e2020-09-02 22:42:33 -0400263 using INHERITED = Benchmark;
bsalomonb3cb2142016-09-08 12:35:32 -0700264};
265
266DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kPingPong); )
267DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kFlipFlop); )