OLD | NEW |
---|---|
(Empty) | |
1 /* | |
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 repla cement policy */ | |
16 | |
17 ////////////////////////////////////////////////////////////////////////////// | |
18 | |
19 // The width/height of the images to draw. The small size underestimates the val ue 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. | |
22 static constexpr int kS = 25; | |
23 | |
24 static 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_Colo rBLACK, | |
27 SK_ColorCYAN, 1 0); | |
28 imgs[i] = SkImage::MakeFromBitmap(bmp); | |
29 } | |
30 } | |
31 | |
32 static 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 | |
40 void set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) { | |
41 // This is inexact but we attempt to figure out a baseline number of resourc es 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 << 3 0); | |
55 context->purgeAllUnlockedResources(); | |
56 } | |
57 | |
58 ////////////////////////////////////////////////////////////////////////////// | |
59 | |
60 /** | |
61 * Tests repeatedly drawing the same set of images in each frame. Different inst ances of the bench | |
62 * run with different cache sizes and either repeat the image order each frame o r use a random | |
63 * order. Every variation of this bench draws the same image set, only the budge t and order of | |
64 * images differs. Since the total fill is the same they can be cross-compared. | |
65 */ | |
66 class ImageCacheBudgetBench : public Benchmark { | |
67 public: | |
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 == backen d; } | |
81 | |
82 protected: | |
83 const char* onGetName() override { | |
84 return fName.c_str(); | |
85 } | |
86 | |
87 void onPerCanvasPreDraw(SkCanvas* canvas) override { | |
robertphillips
2016/09/08 16:55:48
extra space here after '=' ?
bsalomon
2016/09/08 18:34:25
Done.
| |
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 { | |
robertphillips
2016/09/08 16:55:48
pre++ to match everyone else ?
bsalomon
2016/09/08 18:34:25
Done.
| |
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 GrR esourceCache. | |
132 canvas->flush(); | |
133 } | |
134 } | |
135 } | |
136 | |
137 private: | |
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 | |
152 DEF_BENCH( return new ImageCacheBudgetBench(105, false); ) | |
153 | |
154 DEF_BENCH( return new ImageCacheBudgetBench(90, false); ) | |
155 | |
156 DEF_BENCH( return new ImageCacheBudgetBench(80, false); ) | |
157 | |
158 DEF_BENCH( return new ImageCacheBudgetBench(50, false); ) | |
159 | |
160 DEF_BENCH( return new ImageCacheBudgetBench(105, true); ) | |
161 | |
162 DEF_BENCH( return new ImageCacheBudgetBench(90, true); ) | |
163 | |
164 DEF_BENCH( return new ImageCacheBudgetBench(80, true); ) | |
165 | |
166 DEF_BENCH( return new ImageCacheBudgetBench(50, true); ) | |
167 | |
168 ////////////////////////////////////////////////////////////////////////////// | |
169 | |
170 /** | |
171 * Similar to above but changes between being over and under budget by varying t he number of images | |
172 * rendered. This is not directly comparable to the non-dynamic benchmarks. | |
173 */ | |
robertphillips
2016/09/08 16:55:48
ImageCacheBudgetDynamicBench ?
bsalomon
2016/09/08 18:34:24
Done.
| |
174 class ImageCacheBudgetDynamic : public Benchmark { | |
175 public: | |
176 enum class Mode { | |
robertphillips
2016/09/08 16:55:48
an -> and ?
bsalomon
2016/09/08 18:34:24
Done.
| |
177 // Increase from min to max images drawn gradually over simulated frames an then back. | |
178 kPingPong, | |
179 // Alternate between under and over budget every other simulated frame. | |
180 kFlipFlop | |
181 }; | |
182 | |
183 ImageCacheBudgetDynamic(Mode mode) : fMode(mode) {} | |
184 | |
185 bool isSuitableFor(Backend backend) override { return kGPU_Backend == backen d; } | |
186 | |
187 protected: | |
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 } | |
196 | |
197 void onPerCanvasPreDraw(SkCanvas* canvas) override { | |
198 GrContext* context = canvas->getGrContext(); | |
199 SkASSERT(context); | |
200 context->getResourceCacheLimits(&fOldCount, &fOldBytes); | |
201 make_images(fImages, kMaxImagesToDraw); | |
202 set_cache_budget(canvas, kImagesInBudget); | |
203 } | |
204 | |
205 void onPerCanvasPostDraw(SkCanvas* canvas) override { | |
206 GrContext* context = canvas->getGrContext(); | |
207 SkASSERT(context); | |
208 context->setResourceCacheLimits(fOldCount, fOldBytes); | |
209 for (int i = 0; i < kMaxImagesToDraw; ++i) { | |
210 fImages[i].reset(); | |
211 } | |
212 } | |
213 | |
214 void onDraw(int loops, SkCanvas* canvas) override { | |
215 int delta; | |
216 switch (fMode) { | |
217 case Mode::kPingPong: | |
218 delta = 1; | |
219 break; | |
220 case Mode::kFlipFlop: | |
221 delta = kMaxImagesToDraw - kMinImagesToDraw; | |
222 break; | |
223 } | |
224 for (int i = 0; i < loops; i++) { | |
225 int imgsToDraw = kMinImagesToDraw; | |
226 for (int frame = 0; frame < kSimulatedFrames; ++frame) { | |
227 for (int j = 0; j < imgsToDraw; ++j) { | |
228 draw_image(canvas, fImages[j].get()); | |
229 } | |
230 imgsToDraw += delta; | |
231 if (imgsToDraw > kMaxImagesToDraw || imgsToDraw < kMinImagesToDr aw) { | |
232 delta = -delta; | |
233 imgsToDraw += 2 * delta; | |
234 } | |
235 // Simulate a frame boundary by flushing. This should notify GrR esourceCache. | |
236 canvas->flush(); | |
237 } | |
238 } | |
239 } | |
240 | |
241 private: | |
robertphillips
2016/09/08 16:55:48
comment wrong ?
bsalomon
2016/09/08 18:34:25
Done.
| |
242 // width/height of the images | |
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 | |
256 DEF_BENCH( return new ImageCacheBudgetDynamic(ImageCacheBudgetDynamic::Mode::kPi ngPong); ) | |
257 DEF_BENCH( return new ImageCacheBudgetDynamic(ImageCacheBudgetDynamic::Mode::kFl ipFlop); ) | |
OLD | NEW |