3695bdb587
There was only one virtual method, so switch that to a bool stored in the base class. The derived types exist as hints for the reader, and an easy way to adjust how the new localToDevice is constructed. With this change, we don't need SkSimpleMatrixProvider. SkMatrixProvider is concrete, so we can use it directly. SkOverrideDeviceMatrixProvider no longer needs the original provider for anything, so remove that parameter. It now exists solely to inhibit the hitsPixelCenters flag. Fix a few spots (SkParticleBinding, some sites in SkRuntimeEffect) where we used SkSimpleMatrixProvider, even though the local coordinates being passed did not obey the hits-pixel-centers constraints. Most importantly, document how localToDeviceHitsPixelCenters works. Change-Id: Ibe9060bac0822d0edf52a507d390bd198d8e6dbd Reviewed-on: https://skia-review.googlesource.com/c/skia/+/482176 Reviewed-by: John Stiles <johnstiles@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
283 lines
11 KiB
C++
283 lines
11 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/SkBitmap.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkImage.h"
|
|
#include "include/core/SkPaint.h"
|
|
#include "include/gpu/GrDirectContext.h"
|
|
#include "include/utils/SkRandom.h"
|
|
#include "src/core/SkCanvasPriv.h"
|
|
#include "src/gpu/GrOpsTypes.h"
|
|
#include "src/gpu/SkGr.h"
|
|
#include "src/gpu/v1/SurfaceDrawContext_v1.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");
|
|
|
|
inline static constexpr int kWidth = 1024;
|
|
inline static constexpr int kHeight = 1024;
|
|
|
|
// There will either be 0 images, 1 image, or 1 image per rect
|
|
inline 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 skgpu::v1::SurfaceDrawContext
|
|
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);
|
|
|
|
canvas->experimental_DrawEdgeAAImageSet(batch, kRectCount, nullptr, nullptr,
|
|
SkSamplingOptions(SkFilterMode::kLinear), &paint,
|
|
SkCanvas::kFast_SrcRectConstraint);
|
|
}
|
|
|
|
void drawImagesRef(SkCanvas* canvas) const {
|
|
SkASSERT(kImageMode != ImageMode::kNone);
|
|
SkASSERT(kDrawMode == DrawMode::kRef);
|
|
|
|
SkPaint paint;
|
|
paint.setAntiAlias(true);
|
|
|
|
for (int i = 0; i < kRectCount; ++i) {
|
|
int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
|
|
SkRect srcRect = SkRect::MakeIWH(fImages[imageIndex]->width(),
|
|
fImages[imageIndex]->height());
|
|
canvas->drawImageRect(fImages[imageIndex].get(), srcRect, fRects[i],
|
|
SkSamplingOptions(SkFilterMode::kLinear), &paint,
|
|
SkCanvas::kFast_SrcRectConstraint);
|
|
}
|
|
}
|
|
|
|
void drawSolidColorsBatch(SkCanvas* canvas) const {
|
|
SkASSERT(kImageMode == ImageMode::kNone);
|
|
SkASSERT(kDrawMode == DrawMode::kBatch);
|
|
|
|
auto context = canvas->recordingContext();
|
|
SkASSERT(context);
|
|
|
|
GrQuadSetEntry 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);
|
|
|
|
auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
|
|
SkMatrix view = canvas->getLocalToDeviceAs3x3();
|
|
SkMatrixProvider matrixProvider(view);
|
|
GrPaint grPaint;
|
|
SkPaintToGrPaint(context, sdc->colorInfo(), paint, matrixProvider, &grPaint);
|
|
sdc->drawQuadSet(nullptr, 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
|
|
auto direct = GrAsDirectContext(canvas->recordingContext());
|
|
for (int i = 0; i < kImageCount; ++i) {
|
|
SkBitmap bm;
|
|
bm.allocN32Pixels(256, 256);
|
|
bm.eraseColor(fColors[i].toSkColor());
|
|
auto image = bm.asImage();
|
|
|
|
fImages[i] = direct ? image->makeTextureImage(direct) : std::move(image);
|
|
}
|
|
}
|
|
|
|
void onPerCanvasPostDraw(SkCanvas* canvas) override {
|
|
for (int i = 0; i < kImageCount; ++i) {
|
|
// For Vulkan we need to make sure the bench isn't holding onto any refs to the
|
|
// GrContext when we go to delete the vulkan context (which happens before the bench is
|
|
// deleted). So reset all the images here so they aren't holding GrContext refs.
|
|
fImages[i].reset();
|
|
}
|
|
}
|
|
|
|
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 };
|
|
}
|
|
|
|
using INHERITED = Benchmark;
|
|
};
|
|
|
|
// 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
|