1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2016 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "bench/Benchmark.h" 9cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 10cb93a386Sopenharmony_ci#include "include/core/SkImage.h" 11cb93a386Sopenharmony_ci#include "include/core/SkSurface.h" 12cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h" 13cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h" 14cb93a386Sopenharmony_ci#include "src/gpu/GrResourceCache.h" 15cb93a386Sopenharmony_ci#include "tools/ToolUtils.h" 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_ci#include <utility> 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_ci/** These benchmarks were designed to measure changes to GrResourceCache's replacement policy */ 21cb93a386Sopenharmony_ci 22cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci// The width/height of the images to draw. The small size underestimates the value of a good 25cb93a386Sopenharmony_ci// replacement strategy since the texture uploads are quite small. However, the effects are still 26cb93a386Sopenharmony_ci// significant and this lets the benchmarks complete a lot faster, especially on mobile. 27cb93a386Sopenharmony_cistatic constexpr int kS = 25; 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_cistatic void make_images(sk_sp<SkImage> imgs[], int cnt) { 30cb93a386Sopenharmony_ci for (int i = 0; i < cnt; ++i) { 31cb93a386Sopenharmony_ci imgs[i] = ToolUtils::create_checkerboard_image(kS, kS, SK_ColorBLACK, SK_ColorCYAN, 10); 32cb93a386Sopenharmony_ci } 33cb93a386Sopenharmony_ci} 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_cistatic void draw_image(SkCanvas* canvas, SkImage* img) { 36cb93a386Sopenharmony_ci // Make the paint transparent to avoid any issues of deferred tiler blending 37cb93a386Sopenharmony_ci // optmizations 38cb93a386Sopenharmony_ci SkPaint paint; 39cb93a386Sopenharmony_ci paint.setAlpha(0x10); 40cb93a386Sopenharmony_ci canvas->drawImage(img, 0, 0, SkSamplingOptions(), &paint); 41cb93a386Sopenharmony_ci} 42cb93a386Sopenharmony_ci 43cb93a386Sopenharmony_civoid set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) { 44cb93a386Sopenharmony_ci // This is inexact but we attempt to figure out a baseline number of resources GrContext needs 45cb93a386Sopenharmony_ci // to render an SkImage and add one additional resource for each image we'd like to fit. 46cb93a386Sopenharmony_ci auto context = canvas->recordingContext()->asDirectContext(); 47cb93a386Sopenharmony_ci SkASSERT(context); 48cb93a386Sopenharmony_ci context->flushAndSubmit(); 49cb93a386Sopenharmony_ci context->priv().getResourceCache()->purgeUnlockedResources(); 50cb93a386Sopenharmony_ci sk_sp<SkImage> image; 51cb93a386Sopenharmony_ci make_images(&image, 1); 52cb93a386Sopenharmony_ci draw_image(canvas, image.get()); 53cb93a386Sopenharmony_ci context->flushAndSubmit(); 54cb93a386Sopenharmony_ci int baselineCount; 55cb93a386Sopenharmony_ci context->getResourceCacheUsage(&baselineCount, nullptr); 56cb93a386Sopenharmony_ci baselineCount -= 1; // for the image's textures. 57cb93a386Sopenharmony_ci context->setResourceCacheLimits(baselineCount + approxImagesInBudget, 1 << 30); 58cb93a386Sopenharmony_ci context->priv().getResourceCache()->purgeUnlockedResources(); 59cb93a386Sopenharmony_ci} 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci/** 64cb93a386Sopenharmony_ci * Tests repeatedly drawing the same set of images in each frame. Different instances of the bench 65cb93a386Sopenharmony_ci * run with different cache sizes and either repeat the image order each frame or use a random 66cb93a386Sopenharmony_ci * order. Every variation of this bench draws the same image set, only the budget and order of 67cb93a386Sopenharmony_ci * images differs. Since the total fill is the same they can be cross-compared. 68cb93a386Sopenharmony_ci */ 69cb93a386Sopenharmony_ciclass ImageCacheBudgetBench : public Benchmark { 70cb93a386Sopenharmony_cipublic: 71cb93a386Sopenharmony_ci /** budgetSize is the number of images that can fit in the cache. 100 images will be drawn. */ 72cb93a386Sopenharmony_ci ImageCacheBudgetBench(int budgetSize, bool shuffle) 73cb93a386Sopenharmony_ci : fBudgetSize(budgetSize) 74cb93a386Sopenharmony_ci , fShuffle(shuffle) 75cb93a386Sopenharmony_ci , fIndices(nullptr) { 76cb93a386Sopenharmony_ci float imagesOverBudget = float(kImagesToDraw) / budgetSize; 77cb93a386Sopenharmony_ci // Make the benchmark name contain the percentage of the budget that is used in each 78cb93a386Sopenharmony_ci // simulated frame. 79cb93a386Sopenharmony_ci fName.printf("image_cache_budget_%.0f%s", imagesOverBudget * 100, 80cb93a386Sopenharmony_ci (shuffle ? "_shuffle" : "")); 81cb93a386Sopenharmony_ci } 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } 84cb93a386Sopenharmony_ci 85cb93a386Sopenharmony_ciprotected: 86cb93a386Sopenharmony_ci const char* onGetName() override { 87cb93a386Sopenharmony_ci return fName.c_str(); 88cb93a386Sopenharmony_ci } 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci void onPerCanvasPreDraw(SkCanvas* canvas) override { 91cb93a386Sopenharmony_ci auto context = canvas->recordingContext()->asDirectContext(); 92cb93a386Sopenharmony_ci SkASSERT(context); 93cb93a386Sopenharmony_ci fOldBytes = context->getResourceCacheLimit(); 94cb93a386Sopenharmony_ci set_cache_budget(canvas, fBudgetSize); 95cb93a386Sopenharmony_ci make_images(fImages, kImagesToDraw); 96cb93a386Sopenharmony_ci if (fShuffle) { 97cb93a386Sopenharmony_ci SkRandom random; 98cb93a386Sopenharmony_ci fIndices.reset(new int[kSimulatedFrames * kImagesToDraw]); 99cb93a386Sopenharmony_ci for (int frame = 0; frame < kSimulatedFrames; ++frame) { 100cb93a386Sopenharmony_ci int* base = fIndices.get() + frame * kImagesToDraw; 101cb93a386Sopenharmony_ci for (int i = 0; i < kImagesToDraw; ++i) { 102cb93a386Sopenharmony_ci base[i] = i; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci for (int i = 0; i < kImagesToDraw - 1; ++i) { 105cb93a386Sopenharmony_ci int other = random.nextULessThan(kImagesToDraw - i) + i; 106cb93a386Sopenharmony_ci using std::swap; 107cb93a386Sopenharmony_ci swap(base[i], base[other]); 108cb93a386Sopenharmony_ci } 109cb93a386Sopenharmony_ci } 110cb93a386Sopenharmony_ci } 111cb93a386Sopenharmony_ci } 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci void onPerCanvasPostDraw(SkCanvas* canvas) override { 114cb93a386Sopenharmony_ci auto context = canvas->recordingContext()->asDirectContext(); 115cb93a386Sopenharmony_ci SkASSERT(context); 116cb93a386Sopenharmony_ci context->setResourceCacheLimit(fOldBytes); 117cb93a386Sopenharmony_ci for (int i = 0; i < kImagesToDraw; ++i) { 118cb93a386Sopenharmony_ci fImages[i].reset(); 119cb93a386Sopenharmony_ci } 120cb93a386Sopenharmony_ci fIndices.reset(nullptr); 121cb93a386Sopenharmony_ci } 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci void onDraw(int loops, SkCanvas* canvas) override { 124cb93a386Sopenharmony_ci auto dContext = GrAsDirectContext(canvas->recordingContext()); 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ci for (int i = 0; i < loops; ++i) { 127cb93a386Sopenharmony_ci for (int frame = 0; frame < kSimulatedFrames; ++frame) { 128cb93a386Sopenharmony_ci for (int j = 0; j < kImagesToDraw; ++j) { 129cb93a386Sopenharmony_ci int idx; 130cb93a386Sopenharmony_ci if (fShuffle) { 131cb93a386Sopenharmony_ci idx = fIndices[frame * kImagesToDraw + j]; 132cb93a386Sopenharmony_ci } else { 133cb93a386Sopenharmony_ci idx = j; 134cb93a386Sopenharmony_ci } 135cb93a386Sopenharmony_ci draw_image(canvas, fImages[idx].get()); 136cb93a386Sopenharmony_ci } 137cb93a386Sopenharmony_ci // Simulate a frame boundary by flushing. This should notify GrResourceCache. 138cb93a386Sopenharmony_ci if (dContext) { 139cb93a386Sopenharmony_ci dContext->flush(); 140cb93a386Sopenharmony_ci } 141cb93a386Sopenharmony_ci } 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci } 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_ciprivate: 146cb93a386Sopenharmony_ci inline static constexpr int kImagesToDraw = 100; 147cb93a386Sopenharmony_ci inline static constexpr int kSimulatedFrames = 5; 148cb93a386Sopenharmony_ci 149cb93a386Sopenharmony_ci int fBudgetSize; 150cb93a386Sopenharmony_ci bool fShuffle; 151cb93a386Sopenharmony_ci SkString fName; 152cb93a386Sopenharmony_ci sk_sp<SkImage> fImages[kImagesToDraw]; 153cb93a386Sopenharmony_ci std::unique_ptr<int[]> fIndices; 154cb93a386Sopenharmony_ci size_t fOldBytes; 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_ci using INHERITED = Benchmark; 157cb93a386Sopenharmony_ci}; 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(105, false); ) 160cb93a386Sopenharmony_ci 161cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(90, false); ) 162cb93a386Sopenharmony_ci 163cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(80, false); ) 164cb93a386Sopenharmony_ci 165cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(50, false); ) 166cb93a386Sopenharmony_ci 167cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(105, true); ) 168cb93a386Sopenharmony_ci 169cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(90, true); ) 170cb93a386Sopenharmony_ci 171cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(80, true); ) 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetBench(50, true); ) 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 176cb93a386Sopenharmony_ci 177cb93a386Sopenharmony_ci/** 178cb93a386Sopenharmony_ci * Similar to above but changes between being over and under budget by varying the number of images 179cb93a386Sopenharmony_ci * rendered. This is not directly comparable to the non-dynamic benchmarks. 180cb93a386Sopenharmony_ci */ 181cb93a386Sopenharmony_ciclass ImageCacheBudgetDynamicBench : public Benchmark { 182cb93a386Sopenharmony_cipublic: 183cb93a386Sopenharmony_ci enum class Mode { 184cb93a386Sopenharmony_ci // Increase from min to max images drawn gradually over simulated frames and then back. 185cb93a386Sopenharmony_ci kPingPong, 186cb93a386Sopenharmony_ci // Alternate between under and over budget every other simulated frame. 187cb93a386Sopenharmony_ci kFlipFlop 188cb93a386Sopenharmony_ci }; 189cb93a386Sopenharmony_ci 190cb93a386Sopenharmony_ci ImageCacheBudgetDynamicBench(Mode mode) : fMode(mode) {} 191cb93a386Sopenharmony_ci 192cb93a386Sopenharmony_ci bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } 193cb93a386Sopenharmony_ci 194cb93a386Sopenharmony_ciprotected: 195cb93a386Sopenharmony_ci const char* onGetName() override { 196cb93a386Sopenharmony_ci switch (fMode) { 197cb93a386Sopenharmony_ci case Mode::kPingPong: 198cb93a386Sopenharmony_ci return "image_cache_budget_dynamic_ping_pong"; 199cb93a386Sopenharmony_ci case Mode::kFlipFlop: 200cb93a386Sopenharmony_ci return "image_cache_budget_dynamic_flip_flop"; 201cb93a386Sopenharmony_ci } 202cb93a386Sopenharmony_ci return ""; 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_ci void onPerCanvasPreDraw(SkCanvas* canvas) override { 206cb93a386Sopenharmony_ci auto context = canvas->recordingContext()->asDirectContext(); 207cb93a386Sopenharmony_ci SkASSERT(context); 208cb93a386Sopenharmony_ci context->getResourceCacheLimits(&fOldCount, &fOldBytes); 209cb93a386Sopenharmony_ci make_images(fImages, kMaxImagesToDraw); 210cb93a386Sopenharmony_ci set_cache_budget(canvas, kImagesInBudget); 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ci void onPerCanvasPostDraw(SkCanvas* canvas) override { 214cb93a386Sopenharmony_ci auto context = canvas->recordingContext()->asDirectContext(); 215cb93a386Sopenharmony_ci SkASSERT(context); 216cb93a386Sopenharmony_ci context->setResourceCacheLimits(fOldCount, fOldBytes); 217cb93a386Sopenharmony_ci for (int i = 0; i < kMaxImagesToDraw; ++i) { 218cb93a386Sopenharmony_ci fImages[i].reset(); 219cb93a386Sopenharmony_ci } 220cb93a386Sopenharmony_ci } 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ci void onDraw(int loops, SkCanvas* canvas) override { 223cb93a386Sopenharmony_ci auto dContext = GrAsDirectContext(canvas->recordingContext()); 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_ci int delta = 0; 226cb93a386Sopenharmony_ci switch (fMode) { 227cb93a386Sopenharmony_ci case Mode::kPingPong: 228cb93a386Sopenharmony_ci delta = 1; 229cb93a386Sopenharmony_ci break; 230cb93a386Sopenharmony_ci case Mode::kFlipFlop: 231cb93a386Sopenharmony_ci delta = kMaxImagesToDraw - kMinImagesToDraw; 232cb93a386Sopenharmony_ci break; 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci for (int i = 0; i < loops; ++i) { 235cb93a386Sopenharmony_ci int imgsToDraw = kMinImagesToDraw; 236cb93a386Sopenharmony_ci for (int frame = 0; frame < kSimulatedFrames; ++frame) { 237cb93a386Sopenharmony_ci for (int j = 0; j < imgsToDraw; ++j) { 238cb93a386Sopenharmony_ci draw_image(canvas, fImages[j].get()); 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci imgsToDraw += delta; 241cb93a386Sopenharmony_ci if (imgsToDraw > kMaxImagesToDraw || imgsToDraw < kMinImagesToDraw) { 242cb93a386Sopenharmony_ci delta = -delta; 243cb93a386Sopenharmony_ci imgsToDraw += 2 * delta; 244cb93a386Sopenharmony_ci } 245cb93a386Sopenharmony_ci // Simulate a frame boundary by flushing. This should notify GrResourceCache. 246cb93a386Sopenharmony_ci if (dContext) { 247cb93a386Sopenharmony_ci dContext->flush(); 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci } 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ciprivate: 254cb93a386Sopenharmony_ci inline static constexpr int kImagesInBudget = 25; 255cb93a386Sopenharmony_ci inline static constexpr int kMinImagesToDraw = 15; 256cb93a386Sopenharmony_ci inline static constexpr int kMaxImagesToDraw = 35; 257cb93a386Sopenharmony_ci inline static constexpr int kSimulatedFrames = 80; 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci Mode fMode; 260cb93a386Sopenharmony_ci sk_sp<SkImage> fImages[kMaxImagesToDraw]; 261cb93a386Sopenharmony_ci size_t fOldBytes; 262cb93a386Sopenharmony_ci int fOldCount; 263cb93a386Sopenharmony_ci 264cb93a386Sopenharmony_ci using INHERITED = Benchmark; 265cb93a386Sopenharmony_ci}; 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kPingPong); ) 268cb93a386Sopenharmony_ciDEF_BENCH( return new ImageCacheBudgetDynamicBench(ImageCacheBudgetDynamicBench::Mode::kFlipFlop); ) 269