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 { |
| 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 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 */ |
| 174 class ImageCacheBudgetDynamicBench : public Benchmark { |
| 175 public: |
| 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 == 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 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 < kMinImagesToDr
aw) { |
| 233 delta = -delta; |
| 234 imgsToDraw += 2 * delta; |
| 235 } |
| 236 // Simulate a frame boundary by flushing. This should notify GrR
esourceCache. |
| 237 canvas->flush(); |
| 238 } |
| 239 } |
| 240 } |
| 241 |
| 242 private: |
| 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 ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench:
:Mode::kPingPong); ) |
| 257 DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench:
:Mode::kFlipFlop); ) |
OLD | NEW |