Revert "begin caching Programs in SkVMBlitter"

This reverts commit d1d4cbf2e4.

Reason for revert: static initializers broken checks in Chrome Roll

e.g. gProgramCache{8}

Original change's description:
> begin caching Programs in SkVMBlitter
> 
> My first drafts put caching inside SkVM.cpp, which I rejected for
> feeling a little too magic, and then in SkVMBuilder.cpp using a
> fingerprint of the skvm::Builder to skip Builder::done().
> 
> This version makes an explicit Key structure holding all the parameters
> that currently matter to the Blitter, and caches using that.  This is
> nice because it can be done much more cheaply than running the JIT, and
> much more cheaply even than running the Builder.  It also makes the
> parameterization of the Blitter explicit, which I like.
> 
> This does sometimes create programs that are equivalent but have
> different keys, but that's not that common, and it's pretty harmless.
> E.g. today if the device colorType() is opaque or premul, we'll think
> those are different programs, but actually end up making the exact
> same calls to Builder.  No big deal, and maybe even gives us a
> suggestion to do something when the destination is opaque to skip work.
> I've left that as a TODO followup.
> 
> We really only need a small cache to get a good hit rate.  Running all
> GMs in one process serially, we're now down to 22 calls to done() from
> >100K at head (and >500K yesterday).  (Would be 13 if we treated opaque
> and premul the same.)
> 
> There are two places I'd like to have used tryAcquire() on an SkSpinlock
> but the thread safety static analysis is wrong and prevents me.  Left
> some TODOs.
> 
> Change-Id: I83a365fc895720c76b56b0e5a78f4c673fcd9d64
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/232817
> Commit-Queue: Mike Klein <mtklein@google.com>
> Reviewed-by: Herb Derby <herb@google.com>

TBR=mtklein@google.com,herb@google.com

Change-Id: I9a32f24b79054f7174d82bb8e6aca2a9f65894e8
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/233037
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2019-08-07 12:15:14 +00:00 committed by Skia Commit-Bot
parent ed19e97294
commit 63db48c3ce

View File

@ -5,48 +5,16 @@
* found in the LICENSE file.
*/
#include "include/private/SkMacros.h"
#include "include/private/SkSpinlock.h"
#include "src/core/SkArenaAlloc.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkCoreBlitters.h"
#include "src/core/SkLRUCache.h"
#include "src/core/SkVM.h"
namespace {
enum class Coverage { Full, UniformA8, MaskA8, MaskLCD16, Mask3D };
SK_BEGIN_REQUIRE_DENSE;
struct Key {
SkColorType colorType;
SkAlphaType alphaType;
Coverage coverage;
SkBlendMode blendMode;
SkShader* shader;
SkColorFilter* colorFilter;
Key withCoverage(Coverage c) const {
Key k = *this;
k.coverage = c;
return k;
}
};
SK_END_REQUIRE_DENSE;
static bool operator==(const Key& x, const Key& y) {
return x.colorType == y.colorType
&& x.alphaType == y.alphaType
&& x.coverage == y.coverage
&& x.blendMode == y.blendMode
&& x.shader == y.shader
&& x.colorFilter == y.colorFilter;
}
static SkSpinlock gProgramCacheLock;
static SkLRUCache<Key, skvm::Program> gProgramCache{8};
struct Uniforms {
uint32_t paint_color;
uint8_t coverage; // Used when Coverage::UniformA8.
@ -115,46 +83,42 @@ namespace {
skvm::I32 min(skvm::I32 x, skvm::I32 y) { return select(lt(x,y), x,y); }
skvm::I32 max(skvm::I32 x, skvm::I32 y) { return select(gt(x,y), x,y); }
static bool CanBuild(const Key& key) {
static bool CanBuild(const SkPixmap& device, const SkPaint& paint) {
// These checks parallel the TODOs in Builder::Builder().
if (key.shader) { return false; }
if (key.colorFilter) { return false; }
switch (key.colorType) {
if (paint.getShader()) { return false; }
if (paint.getColorFilter()) { return false; }
switch (device.colorType()) {
default: return false;
case kRGB_565_SkColorType: break;
case kRGBA_8888_SkColorType: break;
case kBGRA_8888_SkColorType: break;
}
if (key.alphaType == kUnpremul_SkAlphaType) { return false; }
switch (key.blendMode) {
if (device.alphaType() == kUnpremul_SkAlphaType) { return false; }
switch (paint.getBlendMode()) {
default: return false;
case SkBlendMode::kSrc: break;
case SkBlendMode::kSrcOver: break;
}
return true;
}
explicit Builder(const Key& key) {
Builder(const SkPixmap& device, const SkPaint& paint, Coverage coverage) {
#define TODO SkUNREACHABLE
SkASSERT(CanBuild(key));
SkASSERT(CanBuild(device, paint));
skvm::Arg uniforms = uniform(),
dst_ptr = arg(SkColorTypeBytesPerPixel(key.colorType));
dst_ptr = arg(SkColorTypeBytesPerPixel(device.colorType()));
// When coverage is MaskA8 or MaskLCD16 there will be one more mask varying,
// and when coverage is Mask3D there will be three more mask varyings.
// When there's no shader and no color filter, the source color is the paint color.
if (key.shader) { TODO; }
if (key.colorFilter) { TODO; }
if (paint.getShader()) { TODO; }
if (paint.getColorFilter()) { TODO; }
Color src = unpack_8888(uniform32(uniforms, offsetof(Uniforms, paint_color)));
// Load up the destination color.
Color dst;
switch (key.colorType) {
switch (device.colorType()) {
default: TODO;
case kRGB_565_SkColorType: dst = unpack_565 (load16(dst_ptr)); break;
@ -165,14 +129,11 @@ namespace {
break;
}
bool force_opaque = false && key.alphaType == kOpaque_SkAlphaType; // TODO: try this?
if (force_opaque) { dst.a = splat(0xff); }
// We'd need to premul dst after loading and unpremul before storing.
if (key.alphaType == kUnpremul_SkAlphaType) { TODO; }
if (device.alphaType() == kUnpremul_SkAlphaType) { TODO; }
// Blend src and dst.
switch (key.blendMode) {
switch (paint.getBlendMode()) {
default: TODO;
case SkBlendMode::kSrc: break;
@ -188,7 +149,7 @@ namespace {
// Lerp with coverage if needed.
bool apply_coverage = true;
skvm::I32 cr,cg,cb,ca;
switch (key.coverage) {
switch (coverage) {
case Coverage::Full: apply_coverage = false;
break;
@ -218,10 +179,8 @@ namespace {
src.a = mix(dst.a, src.a, ca);
}
if (force_opaque) { src.a = splat(0xff); }
// Store back to the destination.
switch (key.colorType) {
switch (device.colorType()) {
default: SkUNREACHABLE;
case kRGB_565_SkColorType: store16(dst_ptr, pack_565(src)); break;
@ -237,91 +196,38 @@ namespace {
public:
bool ok = false;
Blitter(const SkPixmap& device, const SkPaint& paint)
: fDevice(device)
, fKey {
device.colorType(),
device.alphaType(),
Coverage::Full,
paint.getBlendMode(),
paint.getShader(),
paint.getColorFilter(),
}
{
Blitter(const SkPixmap& device, const SkPaint& paint) : fDevice(device), fPaint(paint) {
SkColor4f color = paint.getColor4f();
SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
device.colorSpace(), kUnpremul_SkAlphaType}.apply(color.vec());
if (color.fitsInBytes() && Builder::CanBuild(fKey)) {
if (color.fitsInBytes() && Builder::CanBuild(device, paint)) {
fUniforms.paint_color = color.premul().toBytes_RGBA();
ok = true;
}
}
~Blitter() override {
// TODO: tryAcquire() if we can get thread safety analysis to understand it.
if ((void)gProgramCacheLock.acquire(), true) {
auto cache = [&](skvm::Program&& p, Coverage coverage) {
if (!p.empty()) {
Key key = fKey.withCoverage(coverage);
if (skvm::Program* found = gProgramCache.find(key)) {
*found = std::move(p);
} else {
gProgramCache.insert(key, std::move(p));
}
}
};
cache(std::move(fBlitH), Coverage::Full);
cache(std::move(fBlitAntiH), Coverage::UniformA8);
cache(std::move(fBlitMaskA8), Coverage::MaskA8);
cache(std::move(fBlitMaskLCD16), Coverage::MaskLCD16);
gProgramCacheLock.release();
}
}
private:
SkPixmap fDevice; // TODO: can this be const&?
const Key fKey;
// TODO: I kind of forget whether these need to be copies or if they can be const&
SkPixmap fDevice;
SkPaint fPaint;
Uniforms fUniforms;
skvm::Program fBlitH,
fBlitAntiH,
fBlitMaskA8,
fBlitMaskLCD16;
skvm::Program buildProgram(Coverage coverage) {
Key key = fKey.withCoverage(coverage);
{
skvm::Program p;
// TODO: tryAcquire() is fine here too.
if ((void)gProgramCacheLock.acquire(), true) {
if (skvm::Program* found = gProgramCache.find(key)) {
p = std::move(*found);
}
gProgramCacheLock.release();
}
if (!p.empty()) {
return p;
}
}
#if 0
static std::atomic<int> done{0};
if (0 == done++) {
atexit([]{ SkDebugf("%d calls to done\n", done.load()); });
}
#endif
return Builder{key}.done();
}
void blitH(int x, int y, int w) override {
if (fBlitH.empty()) {
fBlitH = this->buildProgram(Coverage::Full);
fBlitH = Builder{fDevice, fPaint, Coverage::Full}.done();
}
fBlitH.eval(w, &fUniforms, fDevice.addr(x,y));
}
void blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) override {
if (fBlitAntiH.empty()) {
fBlitAntiH = this->buildProgram(Coverage::UniformA8);
fBlitAntiH = Builder{fDevice, fPaint, Coverage::UniformA8}.done();
}
for (int16_t run = *runs; run > 0; run = *runs) {
fUniforms.coverage = *cov;
@ -346,14 +252,14 @@ namespace {
case SkMask::k3D_Format: // TODO: the mul and add 3D mask planes too
case SkMask::kA8_Format:
if (fBlitMaskA8.empty()) {
fBlitMaskA8 = this->buildProgram(Coverage::MaskA8);
fBlitMaskA8 = Builder{fDevice, fPaint, Coverage::MaskA8}.done();
}
program = &fBlitMaskA8;
break;
case SkMask::kLCD16_Format:
if (fBlitMaskLCD16.empty()) {
fBlitMaskLCD16 = this->buildProgram(Coverage::MaskLCD16);
fBlitMaskLCD16 = Builder{fDevice, fPaint, Coverage::MaskLCD16}.done();
}
program = &fBlitMaskLCD16;
break;