aboutsummaryrefslogtreecommitdiffhomepage
path: root/bench/ImageCacheBudgetBench.cpp
diff options
context:
space:
mode:
authorGravatar bsalomon <bsalomon@google.com>2016-09-08 12:35:32 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-09-08 12:35:32 -0700
commitb3cb2142725f9d0cd88a7200770536bc21f73b14 (patch)
tree59e6dab5750bba0e87bfc4e08becfea4d4f3942e /bench/ImageCacheBudgetBench.cpp
parent2ecc35ffa5feb56cd088478589ba1e48382b2e1f (diff)
Benchmarks for testing changes to GrResourceCache replacement policy when over budget.
Diffstat (limited to 'bench/ImageCacheBudgetBench.cpp')
-rw-r--r--bench/ImageCacheBudgetBench.cpp257
1 files changed, 257 insertions, 0 deletions
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<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 {
+ 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<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.
+ */
+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<SkImage> 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); )