From b3cb2142725f9d0cd88a7200770536bc21f73b14 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Thu, 8 Sep 2016 12:35:32 -0700 Subject: Benchmarks for testing changes to GrResourceCache replacement policy when over budget. GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2317263002 Review-Url: https://codereview.chromium.org/2317263002 --- bench/ImageCacheBudgetBench.cpp | 257 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 bench/ImageCacheBudgetBench.cpp (limited to 'bench/ImageCacheBudgetBench.cpp') diff --git a/bench/ImageCacheBudgetBench.cpp b/bench/ImageCacheBudgetBench.cpp new file mode 100644 index 0000000000..32804bae13 --- /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 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 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 { + 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 { + 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 fImages[kImagesToDraw]; + SkAutoTDeleteArray 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. + */ +class ImageCacheBudgetDynamicBench : public Benchmark { +public: + enum class Mode { + // Increase from min to max images drawn gradually over simulated frames and then back. + kPingPong, + // Alternate between under and over budget every other simulated frame. + kFlipFlop + }; + + ImageCacheBudgetDynamicBench(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"; + } + return ""; + } + + 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 = 0; + 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: + static constexpr int kImagesInBudget = 25; + static constexpr int kMinImagesToDraw = 15; + static constexpr int kMaxImagesToDraw = 35; + static constexpr int kSimulatedFrames = 80; + + Mode fMode; + sk_sp fImages[kMaxImagesToDraw]; + size_t fOldBytes; + int fOldCount; + + typedef Benchmark INHERITED; +}; + +DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kPingPong); ) +DEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kFlipFlop); ) -- cgit v1.2.3