Chromium Code Reviews| Index: bench/ImageCacheBudgetBench.cpp |
| diff --git a/bench/ImageCacheBudgetBench.cpp b/bench/ImageCacheBudgetBench.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e024ef219ae57b17f28f84abe7cc5820c26ec147 |
| --- /dev/null |
| +++ b/bench/ImageCacheBudgetBench.cpp |
| @@ -0,0 +1,257 @@ |
| +/* |
| + * Copyright 2016 Google Inc. |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "Benchmark.h" |
| +#include "GrContext.h" |
| +#include "sk_tool_utils.h" |
| +#include "SkCanvas.h" |
| +#include "SkImage.h" |
| +#include "SkSurface.h" |
| + |
| +/** These benchmarks were designed to measure changes to GrResourceCache's replacement policy */ |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +// The width/height of the images to draw. The small size underestimates the value of a good |
| +// replacement strategy since the texture uploads are quite small. However, the effects are still |
| +// significant and this lets the benchmarks complete a lot faster, especially on mobile. |
| +static constexpr int kS = 25; |
| + |
| +static void make_images(sk_sp<SkImage> imgs[], int cnt) { |
| + for (int i = 0; i < cnt; ++i) { |
| + SkBitmap bmp = sk_tool_utils::create_checkerboard_bitmap(kS, kS, SK_ColorBLACK, |
| + SK_ColorCYAN, 10); |
| + imgs[i] = SkImage::MakeFromBitmap(bmp); |
| + } |
| +} |
| + |
| +static void draw_image(SkCanvas* canvas, SkImage* img) { |
| + // Make the paint transparent to avoid any issues of deferred tiler blending |
| + // optmizations |
| + SkPaint paint; |
| + paint.setAlpha(0x10); |
| + canvas->drawImage(img, 0, 0, &paint); |
| +} |
| + |
| +void set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) { |
| + // This is inexact but we attempt to figure out a baseline number of resources GrContext needs |
| + // to render an SkImage and add one additional resource for each image we'd like to fit. |
| + GrContext* context = canvas->getGrContext(); |
| + SkASSERT(context); |
| + context->flush(); |
| + context->purgeAllUnlockedResources(); |
| + sk_sp<SkImage> image; |
| + make_images(&image, 1); |
| + draw_image(canvas, image.get()); |
| + context->flush(); |
| + int baselineCount; |
| + context->getResourceCacheUsage(&baselineCount, nullptr); |
| + baselineCount -= 1; // for the image's textures. |
| + context->setResourceCacheLimits(baselineCount + approxImagesInBudget, 1 << 30); |
| + context->purgeAllUnlockedResources(); |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +/** |
| + * Tests repeatedly drawing the same set of images in each frame. Different instances of the bench |
| + * run with different cache sizes and either repeat the image order each frame or use a random |
| + * order. Every variation of this bench draws the same image set, only the budget and order of |
| + * images differs. Since the total fill is the same they can be cross-compared. |
| + */ |
| +class ImageCacheBudgetBench : public Benchmark { |
| +public: |
| + /** budgetSize is the number of images that can fit in the cache. 100 images will be drawn. */ |
| + ImageCacheBudgetBench(int budgetSize, bool shuffle) |
| + : fBudgetSize(budgetSize) |
| + , fShuffle(shuffle) |
| + , fIndices(nullptr) { |
| + float imagesOverBudget = float(kImagesToDraw) / budgetSize; |
| + // Make the benchmark name contain the percentage of the budget that is used in each |
| + // simulated frame. |
| + fName.printf("image_cache_budget_%.0f%s", imagesOverBudget * 100, |
| + (shuffle ? "_shuffle" : "")); |
| + } |
| + |
| + bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } |
| + |
| +protected: |
| + const char* onGetName() override { |
| + return fName.c_str(); |
| + } |
| + |
| + void onPerCanvasPreDraw(SkCanvas* canvas) override { |
|
robertphillips
2016/09/08 16:55:48
extra space here after '=' ?
bsalomon
2016/09/08 18:34:25
Done.
|
| + GrContext* context = canvas->getGrContext(); |
| + SkASSERT(context); |
| + context->getResourceCacheLimits(&fOldCount, &fOldBytes); |
| + set_cache_budget(canvas, fBudgetSize); |
| + make_images(fImages, kImagesToDraw); |
| + if (fShuffle) { |
| + SkRandom random; |
| + fIndices.reset(new int[kSimulatedFrames * kImagesToDraw]); |
| + for (int frame = 0; frame < kSimulatedFrames; ++frame) { |
| + int* base = fIndices.get() + frame * kImagesToDraw; |
| + for (int i = 0; i < kImagesToDraw; ++i) { |
| + base[i] = i; |
| + } |
| + for (int i = 0; i < kImagesToDraw - 1; ++i) { |
| + int other = random.nextULessThan(kImagesToDraw - i) + i; |
| + SkTSwap(base[i], base[other]); |
| + } |
| + } |
| + } |
| + } |
| + |
| + void onPerCanvasPostDraw(SkCanvas* canvas) override { |
| + GrContext* context = canvas->getGrContext(); |
| + SkASSERT(context); |
| + context->setResourceCacheLimits(fOldCount, fOldBytes); |
| + for (int i = 0; i < kImagesToDraw; ++i) { |
| + fImages[i].reset(); |
| + } |
| + fIndices.reset(nullptr); |
| + } |
| + |
| + 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.
|
| + for (int i = 0; i < loops; i++) { |
| + for (int frame = 0; frame < kSimulatedFrames; ++frame) { |
| + for (int j = 0; j < kImagesToDraw; ++j) { |
| + int idx; |
| + if (fShuffle) { |
| + idx = fIndices[frame * kImagesToDraw + j]; |
| + } else { |
| + idx = j; |
| + } |
| + draw_image(canvas, fImages[idx].get()); |
| + } |
| + // Simulate a frame boundary by flushing. This should notify GrResourceCache. |
| + canvas->flush(); |
| + } |
| + } |
| + } |
| + |
| +private: |
| + static constexpr int kImagesToDraw = 100; |
| + static constexpr int kSimulatedFrames = 5; |
| + |
| + int fBudgetSize; |
| + bool fShuffle; |
| + SkString fName; |
| + sk_sp<SkImage> fImages[kImagesToDraw]; |
| + SkAutoTDeleteArray<int> fIndices; |
| + size_t fOldBytes; |
| + int fOldCount; |
| + |
| + typedef Benchmark INHERITED; |
| +}; |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(105, false); ) |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(90, false); ) |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(80, false); ) |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(50, false); ) |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(105, true); ) |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(90, true); ) |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(80, true); ) |
| + |
| +DEF_BENCH( return new ImageCacheBudgetBench(50, true); ) |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +/** |
| + * Similar to above but changes between being over and under budget by varying the number of images |
| + * rendered. This is not directly comparable to the non-dynamic benchmarks. |
| + */ |
|
robertphillips
2016/09/08 16:55:48
ImageCacheBudgetDynamicBench ?
bsalomon
2016/09/08 18:34:24
Done.
|
| +class ImageCacheBudgetDynamic : public Benchmark { |
| +public: |
| + enum class Mode { |
|
robertphillips
2016/09/08 16:55:48
an -> and ?
bsalomon
2016/09/08 18:34:24
Done.
|
| + // Increase from min to max images drawn gradually over simulated frames an then back. |
| + kPingPong, |
| + // Alternate between under and over budget every other simulated frame. |
| + kFlipFlop |
| + }; |
| + |
| + ImageCacheBudgetDynamic(Mode mode) : fMode(mode) {} |
| + |
| + bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } |
| + |
| +protected: |
| + const char* onGetName() override { |
| + switch (fMode) { |
| + case Mode::kPingPong: |
| + return "image_cache_budget_dynamic_ping_pong"; |
| + case Mode::kFlipFlop: |
| + return "image_cache_budget_dynamic_flip_flop"; |
| + } |
| + } |
| + |
| + void onPerCanvasPreDraw(SkCanvas* canvas) override { |
| + GrContext* context = canvas->getGrContext(); |
| + SkASSERT(context); |
| + context->getResourceCacheLimits(&fOldCount, &fOldBytes); |
| + make_images(fImages, kMaxImagesToDraw); |
| + set_cache_budget(canvas, kImagesInBudget); |
| + } |
| + |
| + void onPerCanvasPostDraw(SkCanvas* canvas) override { |
| + GrContext* context = canvas->getGrContext(); |
| + SkASSERT(context); |
| + context->setResourceCacheLimits(fOldCount, fOldBytes); |
| + for (int i = 0; i < kMaxImagesToDraw; ++i) { |
| + fImages[i].reset(); |
| + } |
| + } |
| + |
| + void onDraw(int loops, SkCanvas* canvas) override { |
| + int delta; |
| + switch (fMode) { |
| + case Mode::kPingPong: |
| + delta = 1; |
| + break; |
| + case Mode::kFlipFlop: |
| + delta = kMaxImagesToDraw - kMinImagesToDraw; |
| + break; |
| + } |
| + for (int i = 0; i < loops; i++) { |
| + int imgsToDraw = kMinImagesToDraw; |
| + for (int frame = 0; frame < kSimulatedFrames; ++frame) { |
| + for (int j = 0; j < imgsToDraw; ++j) { |
| + draw_image(canvas, fImages[j].get()); |
| + } |
| + imgsToDraw += delta; |
| + if (imgsToDraw > kMaxImagesToDraw || imgsToDraw < kMinImagesToDraw) { |
| + delta = -delta; |
| + imgsToDraw += 2 * delta; |
| + } |
| + // Simulate a frame boundary by flushing. This should notify GrResourceCache. |
| + canvas->flush(); |
| + } |
| + } |
| + } |
| + |
| +private: |
|
robertphillips
2016/09/08 16:55:48
comment wrong ?
bsalomon
2016/09/08 18:34:25
Done.
|
| + // width/height of the images |
| + static constexpr int kImagesInBudget = 25; |
| + static constexpr int kMinImagesToDraw = 15; |
| + static constexpr int kMaxImagesToDraw = 35; |
| + static constexpr int kSimulatedFrames = 80; |
| + |
| + Mode fMode; |
| + sk_sp<SkImage> fImages[kMaxImagesToDraw]; |
| + size_t fOldBytes; |
| + int fOldCount; |
| + |
| + typedef Benchmark INHERITED; |
| +}; |
| + |
| +DEF_BENCH( return new ImageCacheBudgetDynamic(ImageCacheBudgetDynamic::Mode::kPingPong); ) |
| +DEF_BENCH( return new ImageCacheBudgetDynamic(ImageCacheBudgetDynamic::Mode::kFlipFlop); ) |