f08d1d0ce1
Use std::swap instead. It does not appear that any external user specializes SkTSwap, but some may still use it. This removes all use in Skia so that SkTSwap can later be removed in a smaller CL. After that the <utility> include can be removed from SkTypes.h. Change-Id: If03d4ee07dbecda961aa9f0dc34d171ef5168753 Reviewed-on: https://skia-review.googlesource.com/135578 Reviewed-by: Hal Canary <halcanary@google.com> Reviewed-by: Mike Klein <mtklein@google.com> Commit-Queue: Ben Wagner <bungeman@google.com>
263 lines
9.4 KiB
C++
263 lines
9.4 KiB
C++
/*
|
|
* 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 "sk_tool_utils.h"
|
|
#include "SkCanvas.h"
|
|
#include "SkImage.h"
|
|
#include "SkSurface.h"
|
|
|
|
#include "GrContext.h"
|
|
#include "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 = 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->contextPriv().purgeAllUnlockedResources_ForTesting();
|
|
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->contextPriv().purgeAllUnlockedResources_ForTesting();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* 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); )
|