blob: 5aa1621ec074a0a535e57a2e3500235586622181 [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"
Michael Ludwig9d1cc052021-06-09 20:49:48 -040014#include "src/gpu/GrResourceCache.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "tools/ToolUtils.h"
bsalomonb3cb2142016-09-08 12:35:32 -070016
brianosman9f1f6e22016-09-15 08:33:02 -070017
Ben Wagnerf08d1d02018-06-18 15:11:00 -040018#include <utility>
19
bsalomonb3cb2142016-09-08 12:35:32 -070020/** These benchmarks were designed to measure changes to GrResourceCache's replacement policy */
21
22//////////////////////////////////////////////////////////////////////////////
23
24// The width/height of the images to draw. The small size underestimates the value of a good
25// replacement strategy since the texture uploads are quite small. However, the effects are still
26// significant and this lets the benchmarks complete a lot faster, especially on mobile.
27static constexpr int kS = 25;
28
29static void make_images(sk_sp<SkImage> imgs[], int cnt) {
30 for (int i = 0; i < cnt; ++i) {
Mike Reedac9f0c92020-12-23 10:11:33 -050031 imgs[i] = ToolUtils::create_checkerboard_image(kS, kS, SK_ColorBLACK, SK_ColorCYAN, 10);
bsalomonb3cb2142016-09-08 12:35:32 -070032 }
33}
34
35static void draw_image(SkCanvas* canvas, SkImage* img) {
36 // Make the paint transparent to avoid any issues of deferred tiler blending
37 // optmizations
38 SkPaint paint;
39 paint.setAlpha(0x10);
Mike Reed039f1362021-01-27 21:21:08 -050040 canvas->drawImage(img, 0, 0, SkSamplingOptions(), &paint);
bsalomonb3cb2142016-09-08 12:35:32 -070041}
42
43void set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) {
44 // This is inexact but we attempt to figure out a baseline number of resources GrContext needs
45 // to render an SkImage and add one additional resource for each image we'd like to fit.
Robert Phillipsf0288102020-07-06 13:45:34 -040046 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -070047 SkASSERT(context);
Greg Daniel0a2464f2020-05-14 15:45:44 -040048 context->flushAndSubmit();
Michael Ludwig9d1cc052021-06-09 20:49:48 -040049 context->priv().getResourceCache()->purgeUnlockedResources();
bsalomonb3cb2142016-09-08 12:35:32 -070050 sk_sp<SkImage> image;
51 make_images(&image, 1);
52 draw_image(canvas, image.get());
Greg Daniel0a2464f2020-05-14 15:45:44 -040053 context->flushAndSubmit();
bsalomonb3cb2142016-09-08 12:35:32 -070054 int baselineCount;
55 context->getResourceCacheUsage(&baselineCount, nullptr);
56 baselineCount -= 1; // for the image's textures.
57 context->setResourceCacheLimits(baselineCount + approxImagesInBudget, 1 << 30);
Michael Ludwig9d1cc052021-06-09 20:49:48 -040058 context->priv().getResourceCache()->purgeUnlockedResources();
bsalomonb3cb2142016-09-08 12:35:32 -070059}
60
61//////////////////////////////////////////////////////////////////////////////
62
63/**
64 * Tests repeatedly drawing the same set of images in each frame. Different instances of the bench
65 * run with different cache sizes and either repeat the image order each frame or use a random
66 * order. Every variation of this bench draws the same image set, only the budget and order of
67 * images differs. Since the total fill is the same they can be cross-compared.
68 */
69class ImageCacheBudgetBench : public Benchmark {
70public:
71 /** budgetSize is the number of images that can fit in the cache. 100 images will be drawn. */
72 ImageCacheBudgetBench(int budgetSize, bool shuffle)
73 : fBudgetSize(budgetSize)
74 , fShuffle(shuffle)
75 , fIndices(nullptr) {
76 float imagesOverBudget = float(kImagesToDraw) / budgetSize;
77 // Make the benchmark name contain the percentage of the budget that is used in each
78 // simulated frame.
79 fName.printf("image_cache_budget_%.0f%s", imagesOverBudget * 100,
80 (shuffle ? "_shuffle" : ""));
81 }
82
83 bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
84
85protected:
86 const char* onGetName() override {
87 return fName.c_str();
88 }
89
90 void onPerCanvasPreDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -040091 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -070092 SkASSERT(context);
Robert Phillipscf39f372019-09-03 10:29:20 -040093 fOldBytes = context->getResourceCacheLimit();
bsalomonb3cb2142016-09-08 12:35:32 -070094 set_cache_budget(canvas, fBudgetSize);
95 make_images(fImages, kImagesToDraw);
96 if (fShuffle) {
97 SkRandom random;
98 fIndices.reset(new int[kSimulatedFrames * kImagesToDraw]);
99 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
100 int* base = fIndices.get() + frame * kImagesToDraw;
101 for (int i = 0; i < kImagesToDraw; ++i) {
102 base[i] = i;
103 }
104 for (int i = 0; i < kImagesToDraw - 1; ++i) {
105 int other = random.nextULessThan(kImagesToDraw - i) + i;
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400106 using std::swap;
107 swap(base[i], base[other]);
bsalomonb3cb2142016-09-08 12:35:32 -0700108 }
109 }
110 }
111 }
112
113 void onPerCanvasPostDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -0400114 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -0700115 SkASSERT(context);
Robert Phillipscf39f372019-09-03 10:29:20 -0400116 context->setResourceCacheLimit(fOldBytes);
bsalomonb3cb2142016-09-08 12:35:32 -0700117 for (int i = 0; i < kImagesToDraw; ++i) {
118 fImages[i].reset();
119 }
120 fIndices.reset(nullptr);
121 }
122
123 void onDraw(int loops, SkCanvas* canvas) override {
Robert Phillips2c21a112020-11-20 13:49:37 -0500124 auto dContext = GrAsDirectContext(canvas->recordingContext());
125
bsalomonb3cb2142016-09-08 12:35:32 -0700126 for (int i = 0; i < loops; ++i) {
127 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
128 for (int j = 0; j < kImagesToDraw; ++j) {
129 int idx;
130 if (fShuffle) {
131 idx = fIndices[frame * kImagesToDraw + j];
132 } else {
133 idx = j;
134 }
135 draw_image(canvas, fImages[idx].get());
136 }
137 // Simulate a frame boundary by flushing. This should notify GrResourceCache.
Robert Phillips2c21a112020-11-20 13:49:37 -0500138 if (dContext) {
139 dContext->flush();
140 }
bsalomonb3cb2142016-09-08 12:35:32 -0700141 }
142 }
143 }
144
145private:
146 static constexpr int kImagesToDraw = 100;
147 static constexpr int kSimulatedFrames = 5;
148
149 int fBudgetSize;
150 bool fShuffle;
151 SkString fName;
152 sk_sp<SkImage> fImages[kImagesToDraw];
Ben Wagner7ecc5962016-11-02 17:07:33 -0400153 std::unique_ptr<int[]> fIndices;
bsalomonb3cb2142016-09-08 12:35:32 -0700154 size_t fOldBytes;
bsalomonb3cb2142016-09-08 12:35:32 -0700155
John Stiles7571f9e2020-09-02 22:42:33 -0400156 using INHERITED = Benchmark;
bsalomonb3cb2142016-09-08 12:35:32 -0700157};
158
159DEF_BENCH( return new ImageCacheBudgetBench(105, false); )
160
161DEF_BENCH( return new ImageCacheBudgetBench(90, false); )
162
163DEF_BENCH( return new ImageCacheBudgetBench(80, false); )
164
165DEF_BENCH( return new ImageCacheBudgetBench(50, false); )
166
167DEF_BENCH( return new ImageCacheBudgetBench(105, true); )
168
169DEF_BENCH( return new ImageCacheBudgetBench(90, true); )
170
171DEF_BENCH( return new ImageCacheBudgetBench(80, true); )
172
173DEF_BENCH( return new ImageCacheBudgetBench(50, true); )
174
175//////////////////////////////////////////////////////////////////////////////
176
177/**
178 * Similar to above but changes between being over and under budget by varying the number of images
179 * rendered. This is not directly comparable to the non-dynamic benchmarks.
180 */
181class ImageCacheBudgetDynamicBench : public Benchmark {
182public:
183 enum class Mode {
184 // Increase from min to max images drawn gradually over simulated frames and then back.
185 kPingPong,
186 // Alternate between under and over budget every other simulated frame.
187 kFlipFlop
188 };
189
190 ImageCacheBudgetDynamicBench(Mode mode) : fMode(mode) {}
191
192 bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
193
194protected:
195 const char* onGetName() override {
196 switch (fMode) {
197 case Mode::kPingPong:
198 return "image_cache_budget_dynamic_ping_pong";
199 case Mode::kFlipFlop:
200 return "image_cache_budget_dynamic_flip_flop";
201 }
202 return "";
203 }
204
205 void onPerCanvasPreDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -0400206 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -0700207 SkASSERT(context);
208 context->getResourceCacheLimits(&fOldCount, &fOldBytes);
209 make_images(fImages, kMaxImagesToDraw);
210 set_cache_budget(canvas, kImagesInBudget);
211 }
212
213 void onPerCanvasPostDraw(SkCanvas* canvas) override {
Robert Phillipsf0288102020-07-06 13:45:34 -0400214 auto context = canvas->recordingContext()->asDirectContext();
bsalomonb3cb2142016-09-08 12:35:32 -0700215 SkASSERT(context);
216 context->setResourceCacheLimits(fOldCount, fOldBytes);
217 for (int i = 0; i < kMaxImagesToDraw; ++i) {
218 fImages[i].reset();
219 }
220 }
221
222 void onDraw(int loops, SkCanvas* canvas) override {
Robert Phillips2c21a112020-11-20 13:49:37 -0500223 auto dContext = GrAsDirectContext(canvas->recordingContext());
224
bsalomonb3cb2142016-09-08 12:35:32 -0700225 int delta = 0;
226 switch (fMode) {
227 case Mode::kPingPong:
228 delta = 1;
229 break;
230 case Mode::kFlipFlop:
231 delta = kMaxImagesToDraw - kMinImagesToDraw;
232 break;
233 }
234 for (int i = 0; i < loops; ++i) {
235 int imgsToDraw = kMinImagesToDraw;
236 for (int frame = 0; frame < kSimulatedFrames; ++frame) {
237 for (int j = 0; j < imgsToDraw; ++j) {
238 draw_image(canvas, fImages[j].get());
239 }
240 imgsToDraw += delta;
241 if (imgsToDraw > kMaxImagesToDraw || imgsToDraw < kMinImagesToDraw) {
242 delta = -delta;
243 imgsToDraw += 2 * delta;
244 }
245 // Simulate a frame boundary by flushing. This should notify GrResourceCache.
Robert Phillips2c21a112020-11-20 13:49:37 -0500246 if (dContext) {
247 dContext->flush();
248 }
bsalomonb3cb2142016-09-08 12:35:32 -0700249 }
250 }
251 }
252
253private:
254 static constexpr int kImagesInBudget = 25;
255 static constexpr int kMinImagesToDraw = 15;
256 static constexpr int kMaxImagesToDraw = 35;
257 static constexpr int kSimulatedFrames = 80;
258
259 Mode fMode;
260 sk_sp<SkImage> fImages[kMaxImagesToDraw];
261 size_t fOldBytes;
262 int fOldCount;
263
John Stiles7571f9e2020-09-02 22:42:33 -0400264 using INHERITED = Benchmark;
bsalomonb3cb2142016-09-08 12:35:32 -0700265};
266
267DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kPingPong); )
268DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kFlipFlop); )