123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- /*
- * 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 "bench/Benchmark.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkImage.h"
- #include "include/core/SkSurface.h"
- #include "tools/ToolUtils.h"
- #include "include/gpu/GrContext.h"
- #include "src/gpu/GrContextPriv.h"
- #include <utility>
- /** 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 =
- ToolUtils::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->priv().testingOnly_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->priv().testingOnly_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;
- using std::swap;
- swap(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];
- std::unique_ptr<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); )
|