5e8f45faf1
This updates all our callsites in preparation for removing the #include "include/core/SkColorSpace.h" from SkImageInfo.h According to go/chrome-includes [1], this will save ~150MB (0.07%) from the compilation size. I think SkColorSpace is a big include because it loads the skcms header, which is big. The follow-on CL will remove that link, once clients have been updated as well. [1] https://commondatastorage.googleapis.com/chromium-browser-clang/chrome_includes_2022-03-31_124042.html#view=edges&filter=%5Ethird_party%2Fskia%2Finclude%2Fcore%2FSkImageInfo%5C.h%24&sort=asize&reverse=&includer=%5Ethird_party%2Fskia%2Finclude%2Fcore%2FSkImageInfo%5C.h%24&included=&limit=1000 Change-Id: I1b5ff491ac495317b0e5af3a2082b080d43697ae Bug: skia:13052 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/525639 Reviewed-by: Robert Phillips <robertphillips@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com> Reviewed-by: Florin Malita <fmalita@google.com> Commit-Queue: Kevin Lubick <kjlubick@google.com>
371 lines
19 KiB
C++
371 lines
19 KiB
C++
/*
|
|
* Copyright 2018 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include <memory>
|
|
|
|
#include "bench/Benchmark.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkColorSpace.h"
|
|
#include "include/core/SkImage.h"
|
|
#include "include/core/SkSurface.h"
|
|
#include "include/gpu/GrDirectContext.h"
|
|
#include "include/private/SkTemplates.h"
|
|
#include "include/utils/SkRandom.h"
|
|
|
|
enum class ClampingMode {
|
|
// Submit image set entries with the fast constraint
|
|
kAlwaysFast,
|
|
// Submit image set entries with the strict constraint
|
|
kAlwaysStrict,
|
|
// Submit non-right/bottom tiles as fast, the bottom-right corner as strict, and bottom or right
|
|
// edge tiles as strict with geometry modification to match content area. These will be
|
|
// submitted from left-to-right, top-to-bottom so will necessarily be split into many batches.
|
|
kChromeTiling_RowMajor,
|
|
// As above, but group all fast tiles first, then bottom and right edge tiles in a second batch.
|
|
kChromeTiling_Optimal
|
|
};
|
|
|
|
enum class TransformMode {
|
|
// Tiles will be axis aligned on integer pixels
|
|
kNone,
|
|
// Subpixel, tiles will be axis aligned but adjusted to subpixel coordinates
|
|
kSubpixel,
|
|
// Rotated, tiles will be rotated globally; they won't overlap but their device space bounds may
|
|
kRotated,
|
|
// Perspective, tiles will have global perspective
|
|
kPerspective
|
|
};
|
|
|
|
/**
|
|
* Simulates drawing layers images in a grid a la a tile based compositor.
|
|
*/
|
|
class CompositingImages : public Benchmark {
|
|
public:
|
|
CompositingImages(SkISize imageSize, SkISize tileSize, SkISize tileGridSize,
|
|
ClampingMode clampMode, TransformMode transformMode, int layerCnt)
|
|
: fImageSize(imageSize)
|
|
, fTileSize(tileSize)
|
|
, fTileGridSize(tileGridSize)
|
|
, fClampMode(clampMode)
|
|
, fTransformMode(transformMode)
|
|
, fLayerCnt(layerCnt) {
|
|
fName.appendf("compositing_images_tile_size_%dx%d_grid_%dx%d_layers_%d",
|
|
fTileSize.fWidth, fTileSize.fHeight, fTileGridSize.fWidth,
|
|
fTileGridSize.fHeight, fLayerCnt);
|
|
if (imageSize != tileSize) {
|
|
fName.appendf("_image_%dx%d", imageSize.fWidth, imageSize.fHeight);
|
|
}
|
|
switch(clampMode) {
|
|
case ClampingMode::kAlwaysFast:
|
|
fName.append("_fast");
|
|
break;
|
|
case ClampingMode::kAlwaysStrict:
|
|
fName.append("_strict");
|
|
break;
|
|
case ClampingMode::kChromeTiling_RowMajor:
|
|
fName.append("_chrome");
|
|
break;
|
|
case ClampingMode::kChromeTiling_Optimal:
|
|
fName.append("_chrome_optimal");
|
|
break;
|
|
}
|
|
switch(transformMode) {
|
|
case TransformMode::kNone:
|
|
break;
|
|
case TransformMode::kSubpixel:
|
|
fName.append("_subpixel");
|
|
break;
|
|
case TransformMode::kRotated:
|
|
fName.append("_rotated");
|
|
break;
|
|
case TransformMode::kPerspective:
|
|
fName.append("_persp");
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
|
|
|
|
protected:
|
|
const char* onGetName() override { return fName.c_str(); }
|
|
|
|
void onPerCanvasPreDraw(SkCanvas* canvas) override {
|
|
// Use image size, which may be larger than the tile size (emulating how Chrome specifies
|
|
// their tiles).
|
|
auto ii = SkImageInfo::Make(fImageSize.fWidth, fImageSize.fHeight, kRGBA_8888_SkColorType,
|
|
kPremul_SkAlphaType, nullptr);
|
|
SkRandom random;
|
|
int numImages = fLayerCnt * fTileGridSize.fWidth * fTileGridSize.fHeight;
|
|
fImages = std::make_unique<sk_sp<SkImage>[]>(numImages);
|
|
for (int i = 0; i < numImages; ++i) {
|
|
auto surf = canvas->makeSurface(ii);
|
|
SkColor color = random.nextU();
|
|
surf->getCanvas()->clear(color);
|
|
SkPaint paint;
|
|
paint.setColor(~color);
|
|
paint.setBlendMode(SkBlendMode::kSrc);
|
|
// While the image may be bigger than fTileSize, prepare its content as if fTileSize
|
|
// is what will be visible.
|
|
surf->getCanvas()->drawRect(
|
|
SkRect::MakeLTRB(3, 3, fTileSize.fWidth - 3, fTileSize.fHeight - 3), paint);
|
|
fImages[i] = surf->makeImageSnapshot();
|
|
}
|
|
}
|
|
|
|
void onPerCanvasPostDraw(SkCanvas*) override { fImages.reset(); }
|
|
|
|
void onDraw(int loops, SkCanvas* canvas) override {
|
|
SkPaint paint;
|
|
paint.setAntiAlias(true);
|
|
SkSamplingOptions sampling(SkFilterMode::kLinear);
|
|
|
|
canvas->save();
|
|
canvas->concat(this->getTransform());
|
|
|
|
for (int loop = 0; loop < loops; ++loop) {
|
|
for (int l = 0; l < fLayerCnt; ++l) {
|
|
SkAutoTArray<SkCanvas::ImageSetEntry> set(
|
|
fTileGridSize.fWidth * fTileGridSize.fHeight);
|
|
|
|
if (fClampMode == ClampingMode::kAlwaysFast ||
|
|
fClampMode == ClampingMode::kAlwaysStrict) {
|
|
// Simple 2D for loop, submit everything as a single batch
|
|
int i = 0;
|
|
for (int y = 0; y < fTileGridSize.fHeight; ++y) {
|
|
for (int x = 0; x < fTileGridSize.fWidth; ++x) {
|
|
set[i++] = this->getEntry(x, y, l);
|
|
}
|
|
}
|
|
|
|
SkCanvas::SrcRectConstraint constraint =
|
|
fClampMode == ClampingMode::kAlwaysFast
|
|
? SkCanvas::kFast_SrcRectConstraint
|
|
: SkCanvas::kStrict_SrcRectConstraint;
|
|
canvas->experimental_DrawEdgeAAImageSet(set.get(), i, nullptr, nullptr,
|
|
sampling, &paint, constraint);
|
|
} else if (fClampMode == ClampingMode::kChromeTiling_RowMajor) {
|
|
// Same tile order, but break batching between fast and strict sections, and
|
|
// adjust bottom and right tiles to encode content area distinct from src rect.
|
|
int i = 0;
|
|
for (int y = 0; y < fTileGridSize.fHeight - 1; ++y) {
|
|
int rowStart = i;
|
|
for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
|
|
set[i++] = this->getEntry(x, y, l);
|
|
}
|
|
// Flush "fast" horizontal row
|
|
canvas->experimental_DrawEdgeAAImageSet(set.get() + rowStart,
|
|
fTileGridSize.fWidth - 1, nullptr, nullptr, sampling, &paint,
|
|
SkCanvas::kFast_SrcRectConstraint);
|
|
// Then flush a single adjusted entry for the right edge
|
|
SkPoint dstQuad[4];
|
|
set[i++] = this->getAdjustedEntry(fTileGridSize.fWidth - 1, y, l, dstQuad);
|
|
canvas->experimental_DrawEdgeAAImageSet(
|
|
set.get() + fTileGridSize.fWidth - 1, 1, dstQuad, nullptr, sampling,
|
|
&paint, SkCanvas::kStrict_SrcRectConstraint);
|
|
}
|
|
// For last row, accumulate it as a single strict batch
|
|
int rowStart = i;
|
|
SkAutoTArray<SkPoint> dstQuads(4 * (fTileGridSize.fWidth - 1));
|
|
for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
|
|
set[i++] = this->getAdjustedEntry(x, fTileGridSize.fHeight - 1, l,
|
|
dstQuads.get() + x * 4);
|
|
}
|
|
// The corner can use conventional strict mode without geometric adjustment
|
|
set[i++] = this->getEntry(
|
|
fTileGridSize.fWidth - 1, fTileGridSize.fHeight - 1, l);
|
|
canvas->experimental_DrawEdgeAAImageSet(set.get() + rowStart,
|
|
fTileGridSize.fWidth, dstQuads.get(), nullptr, sampling, &paint,
|
|
SkCanvas::kStrict_SrcRectConstraint);
|
|
} else {
|
|
SkASSERT(fClampMode == ClampingMode::kChromeTiling_Optimal);
|
|
int i = 0;
|
|
// Interior fast tiles
|
|
for (int y = 0; y < fTileGridSize.fHeight - 1; ++y) {
|
|
for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
|
|
set[i++] = this->getEntry(x, y, l);
|
|
}
|
|
}
|
|
canvas->experimental_DrawEdgeAAImageSet(set.get(), i, nullptr, nullptr,
|
|
sampling, &paint,
|
|
SkCanvas::kFast_SrcRectConstraint);
|
|
|
|
// Right edge
|
|
int strictStart = i;
|
|
SkAutoTArray<SkPoint> dstQuads(
|
|
4 * (fTileGridSize.fWidth + fTileGridSize.fHeight - 2));
|
|
for (int y = 0; y < fTileGridSize.fHeight - 1; ++y) {
|
|
set[i++] = this->getAdjustedEntry(fTileGridSize.fWidth - 1, y, l,
|
|
dstQuads.get() + y * 4);
|
|
}
|
|
canvas->experimental_DrawEdgeAAImageSet(set.get() + strictStart,
|
|
i - strictStart, dstQuads.get(), nullptr, sampling, &paint,
|
|
SkCanvas::kStrict_SrcRectConstraint);
|
|
int quadStart = 4 * (fTileGridSize.fHeight - 1);
|
|
strictStart = i;
|
|
for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
|
|
set[i++] = this->getAdjustedEntry(x, fTileGridSize.fHeight - 1, l,
|
|
dstQuads.get() + quadStart + x * 4);
|
|
}
|
|
set[i++] = this->getEntry(
|
|
fTileGridSize.fWidth - 1, fTileGridSize.fHeight - 1, l);
|
|
canvas->experimental_DrawEdgeAAImageSet(set.get() + strictStart,
|
|
i - strictStart, dstQuads.get() + quadStart, nullptr, sampling, &paint,
|
|
SkCanvas::kStrict_SrcRectConstraint);
|
|
}
|
|
}
|
|
// Prevent any batching between composited "frames".
|
|
auto surface = canvas->getSurface();
|
|
if (surface) {
|
|
surface->flush();
|
|
}
|
|
}
|
|
canvas->restore();
|
|
}
|
|
|
|
private:
|
|
SkMatrix getTransform() const {
|
|
SkMatrix m;
|
|
switch(fTransformMode) {
|
|
case TransformMode::kNone:
|
|
m.setIdentity();
|
|
break;
|
|
case TransformMode::kSubpixel:
|
|
m.setTranslate(0.5f, 0.5f);
|
|
break;
|
|
case TransformMode::kRotated:
|
|
m.setRotate(15.f);
|
|
break;
|
|
case TransformMode::kPerspective: {
|
|
m.setIdentity();
|
|
m.setPerspY(0.001f);
|
|
m.setSkewX(SkIntToScalar(8) / 25);
|
|
break;
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
|
|
SkIPoint onGetSize() override {
|
|
SkRect size = SkRect::MakeWH(1.25f * fTileSize.fWidth * fTileGridSize.fWidth,
|
|
1.25f * fTileSize.fHeight * fTileGridSize.fHeight);
|
|
this->getTransform().mapRect(&size);
|
|
return SkIPoint::Make(SkScalarCeilToInt(size.width()), SkScalarCeilToInt(size.height()));
|
|
}
|
|
|
|
unsigned getEdgeFlags(int x, int y) const {
|
|
unsigned flags = SkCanvas::kNone_QuadAAFlags;
|
|
if (x == 0) {
|
|
flags |= SkCanvas::kLeft_QuadAAFlag;
|
|
} else if (x == fTileGridSize.fWidth - 1) {
|
|
flags |= SkCanvas::kRight_QuadAAFlag;
|
|
}
|
|
|
|
if (y == 0) {
|
|
flags |= SkCanvas::kTop_QuadAAFlag;
|
|
} else if (y == fTileGridSize.fHeight - 1) {
|
|
flags |= SkCanvas::kBottom_QuadAAFlag;
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
SkCanvas::ImageSetEntry getEntry(int x, int y, int layer) const {
|
|
int imageIdx =
|
|
fTileGridSize.fWidth * fTileGridSize.fHeight * layer + fTileGridSize.fWidth * y + x;
|
|
SkRect srcRect = SkRect::Make(fTileSize);
|
|
// Make a non-identity transform between src and dst so bilerp isn't disabled.
|
|
float dstWidth = srcRect.width() * 1.25f;
|
|
float dstHeight = srcRect.height() * 1.25f;
|
|
SkRect dstRect = SkRect::MakeXYWH(dstWidth * x, dstHeight * y, dstWidth, dstHeight);
|
|
return SkCanvas::ImageSetEntry(fImages[imageIdx], srcRect, dstRect, 1.f,
|
|
this->getEdgeFlags(x, y));
|
|
}
|
|
|
|
SkCanvas::ImageSetEntry getAdjustedEntry(int x, int y, int layer, SkPoint dstQuad[4]) const {
|
|
SkASSERT(x == fTileGridSize.fWidth - 1 || y == fTileGridSize.fHeight - 1);
|
|
|
|
SkCanvas::ImageSetEntry entry = this->getEntry(x, y, layer);
|
|
SkRect contentRect = SkRect::Make(fImageSize);
|
|
if (x == fTileGridSize.fWidth - 1) {
|
|
// Right edge, so restrict horizontal content to tile width
|
|
contentRect.fRight = fTileSize.fWidth;
|
|
}
|
|
if (y == fTileGridSize.fHeight - 1) {
|
|
// Bottom edge, so restrict vertical content to tile height
|
|
contentRect.fBottom = fTileSize.fHeight;
|
|
}
|
|
|
|
SkMatrix srcToDst = SkMatrix::RectToRect(entry.fSrcRect, entry.fDstRect);
|
|
|
|
// Story entry's dstRect into dstQuad, and use contentRect and contentDst as its src and dst
|
|
entry.fDstRect.toQuad(dstQuad);
|
|
entry.fSrcRect = contentRect;
|
|
entry.fDstRect = srcToDst.mapRect(contentRect);
|
|
entry.fHasClip = true;
|
|
|
|
return entry;
|
|
}
|
|
|
|
std::unique_ptr<sk_sp<SkImage>[]> fImages;
|
|
SkString fName;
|
|
SkISize fImageSize;
|
|
SkISize fTileSize;
|
|
SkISize fTileGridSize;
|
|
ClampingMode fClampMode;
|
|
TransformMode fTransformMode;
|
|
int fLayerCnt;
|
|
|
|
using INHERITED = Benchmark;
|
|
};
|
|
|
|
// Subpixel = false; all of the draw commands align with integer pixels so AA will be automatically
|
|
// turned off within the operation
|
|
DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
|
|
DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
|
|
|
|
DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
|
|
DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
|
|
|
|
DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
|
|
DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
|
|
|
|
// Subpixel = true; force the draw commands to not align with pixels exactly so AA remains on
|
|
DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
|
|
DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
|
|
|
|
DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
|
|
DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
|
|
|
|
DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
|
|
DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
|
|
|
|
// Test different tiling scenarios inspired by Chrome's compositor
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kNone, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kNone, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kNone, 1));
|
|
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kSubpixel, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kSubpixel, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kSubpixel, 1));
|
|
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kRotated, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kRotated, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kRotated, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kRotated, 1));
|
|
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kPerspective, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kPerspective, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kPerspective, 1));
|
|
DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kPerspective, 1));
|