skia2/bench/BulkRectBench.cpp
Brian Osman 449b1157a7 Plumb SkMatrixProvider throughout Ganesh
Renames the provider to SkMatrixProvider, which is now also able to
provide the local-to-device matrix. Everywhere that does paint
conversion and FP generation now has access to the entire matrix
provider, instead of just the CTM.

This will allow the SkSL FP (and others) to fetch other matrix state.

Change-Id: Iffb00bae0d438da0e8de3eebe75183ed6d440fd6
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284040
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2020-04-20 13:48:40 +00:00

273 lines
10 KiB
C++

/*
* Copyright 2019 Google LLC
*
* 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/SkPaint.h"
#include "include/gpu/GrContext.h"
#include "include/utils/SkRandom.h"
#include "src/gpu/GrClip.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/SkGr.h"
// Benchmarks that exercise the bulk image and solid color quad APIs, under a variety of patterns:
enum class ImageMode {
kShared, // 1. One shared image referenced by every rectangle
kUnique, // 2. Unique image for every rectangle
kNone // 3. No image, solid color shading per rectangle
};
// X
enum class DrawMode {
kBatch, // Bulk API submission, one call to draw every rectangle
kRef, // One standard SkCanvas draw call per rectangle
kQuad // One experimental draw call per rectangle, only for solid color draws
};
// X
enum class RectangleLayout {
kRandom, // Random overlapping rectangles
kGrid // Small, non-overlapping rectangles in a grid covering the output surface
};
// Benchmark runner that can be configured by template arguments.
template<int kRectCount, RectangleLayout kLayout, ImageMode kImageMode, DrawMode kDrawMode>
class BulkRectBench : public Benchmark {
public:
static_assert(kImageMode == ImageMode::kNone || kDrawMode != DrawMode::kQuad,
"kQuad only supported for solid color draws");
static constexpr int kWidth = 1024;
static constexpr int kHeight = 1024;
// There will either be 0 images, 1 image, or 1 image per rect
static constexpr int kImageCount = kImageMode == ImageMode::kShared ?
1 : (kImageMode == ImageMode::kNone ? 0 : kRectCount);
bool isSuitableFor(Backend backend) override {
if (kDrawMode == DrawMode::kBatch && kImageMode == ImageMode::kNone) {
// Currently the bulk color quad API is only available on GrRenderTargetContext
return backend == kGPU_Backend;
} else {
return this->INHERITED::isSuitableFor(backend);
}
}
protected:
SkRect fRects[kRectCount];
sk_sp<SkImage> fImages[kImageCount];
SkColor4f fColors[kRectCount];
SkString fName;
void computeName() {
fName = "bulkrect";
fName.appendf("_%d", kRectCount);
if (kLayout == RectangleLayout::kRandom) {
fName.append("_random");
} else {
fName.append("_grid");
}
if (kImageMode == ImageMode::kShared) {
fName.append("_sharedimage");
} else if (kImageMode == ImageMode::kUnique) {
fName.append("_uniqueimages");
} else {
fName.append("_solidcolor");
}
if (kDrawMode == DrawMode::kBatch) {
fName.append("_batch");
} else if (kDrawMode == DrawMode::kRef) {
fName.append("_ref");
} else {
fName.append("_quad");
}
}
void drawImagesBatch(SkCanvas* canvas) const {
SkASSERT(kImageMode != ImageMode::kNone);
SkASSERT(kDrawMode == DrawMode::kBatch);
SkCanvas::ImageSetEntry batch[kRectCount];
for (int i = 0; i < kRectCount; ++i) {
int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
batch[i].fImage = fImages[imageIndex];
batch[i].fSrcRect = SkRect::MakeIWH(fImages[imageIndex]->width(),
fImages[imageIndex]->height());
batch[i].fDstRect = fRects[i];
batch[i].fAAFlags = SkCanvas::kAll_QuadAAFlags;
}
SkPaint paint;
paint.setAntiAlias(true);
paint.setFilterQuality(kLow_SkFilterQuality);
canvas->experimental_DrawEdgeAAImageSet(batch, kRectCount, nullptr, nullptr, &paint,
SkCanvas::kFast_SrcRectConstraint);
}
void drawImagesRef(SkCanvas* canvas) const {
SkASSERT(kImageMode != ImageMode::kNone);
SkASSERT(kDrawMode == DrawMode::kRef);
SkPaint paint;
paint.setAntiAlias(true);
paint.setFilterQuality(kLow_SkFilterQuality);
for (int i = 0; i < kRectCount; ++i) {
int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
SkIRect srcRect = SkIRect::MakeWH(fImages[imageIndex]->width(),
fImages[imageIndex]->height());
canvas->drawImageRect(fImages[imageIndex].get(), srcRect, fRects[i], &paint,
SkCanvas::kFast_SrcRectConstraint);
}
}
void drawSolidColorsBatch(SkCanvas* canvas) const {
SkASSERT(kImageMode == ImageMode::kNone);
SkASSERT(kDrawMode == DrawMode::kBatch);
GrContext* context = canvas->getGrContext();
SkASSERT(context);
GrRenderTargetContext::QuadSetEntry batch[kRectCount];
for (int i = 0; i < kRectCount; ++i) {
batch[i].fRect = fRects[i];
batch[i].fColor = fColors[i].premul();
batch[i].fLocalMatrix = SkMatrix::I();
batch[i].fAAFlags = GrQuadAAFlags::kAll;
}
SkPaint paint;
paint.setColor(SK_ColorWHITE);
paint.setAntiAlias(true);
GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
SkMatrix view = canvas->getTotalMatrix();
SkSimpleMatrixProvider matrixProvider(view);
GrPaint grPaint;
SkPaintToGrPaint(context, rtc->colorInfo(), paint, matrixProvider, &grPaint);
rtc->drawQuadSet(GrNoClip(), std::move(grPaint), GrAA::kYes, view, batch, kRectCount);
}
void drawSolidColorsRef(SkCanvas* canvas) const {
SkASSERT(kImageMode == ImageMode::kNone);
SkASSERT(kDrawMode == DrawMode::kRef || kDrawMode == DrawMode::kQuad);
SkPaint paint;
paint.setAntiAlias(true);
for (int i = 0; i < kRectCount; ++i) {
if (kDrawMode == DrawMode::kRef) {
paint.setColor4f(fColors[i]);
canvas->drawRect(fRects[i], paint);
} else {
canvas->experimental_DrawEdgeAAQuad(fRects[i], nullptr, SkCanvas::kAll_QuadAAFlags,
fColors[i], SkBlendMode::kSrcOver);
}
}
}
const char* onGetName() override {
if (fName.isEmpty()) {
this->computeName();
}
return fName.c_str();
}
void onDelayedSetup() override {
static constexpr SkScalar kMinRectSize = 0.2f;
static constexpr SkScalar kMaxRectSize = 300.f;
SkRandom rand;
for (int i = 0; i < kRectCount; i++) {
if (kLayout == RectangleLayout::kRandom) {
SkScalar w = rand.nextF() * (kMaxRectSize - kMinRectSize) + kMinRectSize;
SkScalar h = rand.nextF() * (kMaxRectSize - kMinRectSize) + kMinRectSize;
SkScalar x = rand.nextF() * (kWidth - w);
SkScalar y = rand.nextF() * (kHeight - h);
fRects[i].setXYWH(x, y, w, h);
} else {
int gridSize = SkScalarCeilToInt(SkScalarSqrt(kRectCount));
SkASSERT(gridSize * gridSize >= kRectCount);
SkScalar w = (kWidth - 1.f) / gridSize;
SkScalar h = (kHeight - 1.f) / gridSize;
SkScalar x = (i % gridSize) * w + 0.5f; // Offset to ensure AA doesn't get disabled
SkScalar y = (i / gridSize) * h + 0.5f;
fRects[i].setXYWH(x, y, w, h);
}
// Make sure we don't extend outside the render target, don't want to include clipping
// in the benchmark.
SkASSERT(SkRect::MakeWH(kWidth, kHeight).contains(fRects[i]));
fColors[i] = {rand.nextF(), rand.nextF(), rand.nextF(), 1.f};
}
}
void onPerCanvasPreDraw(SkCanvas* canvas) override {
// Push the skimages to the GPU when using the GPU backend so that the texture creation is
// not part of the bench measurements. Always remake the images since they are so simple,
// and since they are context-specific, this works when the bench runs multiple GPU backends
GrContext* context = canvas->getGrContext();
for (int i = 0; i < kImageCount; ++i) {
SkBitmap bm;
bm.allocN32Pixels(256, 256);
bm.eraseColor(fColors[i].toSkColor());
auto image = SkImage::MakeFromBitmap(bm);
fImages[i] = context ? image->makeTextureImage(context) : std::move(image);
}
}
void onDraw(int loops, SkCanvas* canvas) override {
for (int i = 0; i < loops; i++) {
if (kImageMode == ImageMode::kNone) {
if (kDrawMode == DrawMode::kBatch) {
this->drawSolidColorsBatch(canvas);
} else {
this->drawSolidColorsRef(canvas);
}
} else {
if (kDrawMode == DrawMode::kBatch) {
this->drawImagesBatch(canvas);
} else {
this->drawImagesRef(canvas);
}
}
}
}
SkIPoint onGetSize() override {
return { kWidth, kHeight };
}
typedef Benchmark INHERITED;
};
// constructor call is wrapped in () so the macro doesn't break parsing the commas in the template
#define ADD_BENCH(n, layout, imageMode, drawMode) \
DEF_BENCH( return (new BulkRectBench<n, layout, imageMode, drawMode>()); )
#define ADD_BENCH_FAMILY(n, layout) \
ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kBatch) \
ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kRef) \
ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kBatch) \
ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kRef) \
ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kBatch) \
ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kRef) \
ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kQuad)
ADD_BENCH_FAMILY(1000, RectangleLayout::kRandom)
ADD_BENCH_FAMILY(1000, RectangleLayout::kGrid)
#undef ADD_BENCH_FAMILY
#undef ADD_BENCH