From d50044129eb30ac0025d5dda2a997e2809abe211 Mon Sep 17 00:00:00 2001 From: Mike Klein Date: Tue, 28 Jul 2020 13:13:26 -0500 Subject: [PATCH] skvm sprite blitter Two diffs, - image_scale_aligned, all over - savelayer_coverage, in bottom-right corner I think maybe these are both working as intended? image_scale_aligned is now much more snapped to pixels, and the bottom-right corner of savelayer_coverage now looks like the top-right, where previously it looked like the bottom-left. Looking on Gold it seems this new image closely matches images marked positive. In any case, probably things that can be followed up on later if they should be different. I don't see any major logical flow flaw. Change-Id: I4cb308a8e1afffefec2464ec0b6f399dae34639b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/306308 Reviewed-by: Mike Reed Commit-Queue: Mike Klein --- src/core/SkBlitter_Sprite.cpp | 4 +- src/core/SkCoreBlitters.h | 9 ++- src/core/SkVMBlitter.cpp | 125 ++++++++++++++++++++++++++++++---- 3 files changed, 119 insertions(+), 19 deletions(-) diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp index 05f25bec16..c83c74fb95 100644 --- a/src/core/SkBlitter_Sprite.cpp +++ b/src/core/SkBlitter_Sprite.cpp @@ -188,9 +188,7 @@ SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint, SkASSERT(alloc != nullptr); if (gUseSkVMBlitter) { - // TODO: one day, focused SkVMBlitters with the sprite as a varying? - // For now, returning nullptr here will make it fall back to normal non-sprite blitting. - return nullptr; + return SkCreateSkVMSpriteBlitter(dst, paint, source,left,top, alloc, std::move(clipShader)); } // TODO: in principle SkRasterPipelineSpriteBlitter could be made to handle this. diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h index 5b3c2184d6..ee25a732ee 100644 --- a/src/core/SkCoreBlitters.h +++ b/src/core/SkCoreBlitters.h @@ -173,10 +173,17 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, bool shader_is_opaque, SkArenaAlloc*, sk_sp clipShader); -SkBlitter* SkCreateSkVMBlitter(const SkPixmap&, +SkBlitter* SkCreateSkVMBlitter(const SkPixmap& dst, const SkPaint&, const SkMatrixProvider&, SkArenaAlloc*, sk_sp clipShader); +SkBlitter* SkCreateSkVMSpriteBlitter(const SkPixmap& dst, + const SkPaint&, + const SkPixmap& sprite, + int left, int top, + SkArenaAlloc*, + sk_sp clipShader); + #endif diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp index c25909be1d..da829d406c 100644 --- a/src/core/SkVMBlitter.cpp +++ b/src/core/SkVMBlitter.cpp @@ -14,7 +14,9 @@ #include "src/core/SkColorSpaceXformSteps.h" #include "src/core/SkCoreBlitters.h" #include "src/core/SkLRUCache.h" +#include "src/core/SkMatrixProvider.h" #include "src/core/SkOpts.h" +#include "src/core/SkPaintPriv.h" #include "src/core/SkVM.h" #include "src/shaders/SkColorFilterShader.h" @@ -180,7 +182,8 @@ namespace { // First two arguments are always uniforms and the destination buffer. uniforms->base = p->uniform(); skvm::Arg dst_ptr = p->arg(SkColorTypeBytesPerPixel(params.dst.colorType())); - // Other arguments depend on params.coverage: + // A SpriteShader (in this file) may next use one argument as its varying source. + // Subsequent arguments depend on params.coverage: // - Full: (no more arguments) // - Mask3D: mul varying, add varying, 8-bit coverage varying // - MaskA8: 8-bit coverage varying @@ -201,6 +204,7 @@ namespace { p->uniformF(uniforms->base, offsetof(BlitterUniforms, paint.fA)), }; + // See note about arguments above... a SpriteShader will call p->arg() once here. skvm::Color src = as_SB(params.shader)->program(p, device,local, paint, params.matrices, /*localM=*/nullptr, params.quality, params.dst, @@ -367,6 +371,35 @@ namespace { const char* getTypeName() const override { return "NoopColorFilter"; } }; + struct SpriteShader : public SkShaderBase { + explicit SpriteShader(SkPixmap sprite) : fSprite(sprite) {} + + SkPixmap fSprite; + + // Only created here temporarily... never serialized. + Factory getFactory() const override { return nullptr; } + const char* getTypeName() const override { return "SpriteShader"; } + + bool isOpaque() const override { return fSprite.isOpaque(); } + + skvm::Color onProgram(skvm::Builder* p, + skvm::Coord /*device*/, skvm::Coord /*local*/, skvm::Color /*paint*/, + const SkMatrixProvider&, const SkMatrix* /*localM*/, + SkFilterQuality, const SkColorInfo& dst, + skvm::Uniforms* uniforms, SkArenaAlloc*) const override { + const SkColorType ct = fSprite.colorType(); + + skvm::PixelFormat fmt; + SkAssertResult(SkColorType_to_PixelFormat(ct, &fmt)); + + skvm::Color c = p->load(fmt, p->arg(SkColorTypeBytesPerPixel(ct))); + + return SkColorSpaceXformSteps{fSprite.colorSpace(), fSprite.alphaType(), + dst.colorSpace(), kPremul_SkAlphaType} + .program(p, uniforms, c); + } + }; + struct DitherShader : public SkShaderBase { explicit DitherShader(sk_sp shader) : fShader(std::move(shader)) {} @@ -453,10 +486,20 @@ namespace { }; static Params effective_params(const SkPixmap& device, - const SkPaint& paint, + const SkPixmap* sprite, + SkPaint paint, const SkMatrixProvider& matrices, sk_sp clip) { - // Color filters have been handled for us by SkBlitter::Choose(). + // Sprites take priority over any shader. (There's rarely one set, and it's meaningless.) + if (sprite) { + paint.setShader(sk_make_sp(*sprite)); + } + + // Normal blitters will have already folded color filters into their shader, + // but we may still need to do that here for SpriteShaders. + if (paint.getColorFilter()) { + SkPaintPriv::RemoveColorFilter(&paint, device.colorSpace()); + } SkASSERT(!paint.getColorFilter()); // If there's no explicit shader, the paint color is the shader, @@ -506,14 +549,18 @@ namespace { class Blitter final : public SkBlitter { public: - Blitter(const SkPixmap& device, - const SkPaint& paint, + Blitter(const SkPixmap& device, + const SkPaint& paint, + const SkPixmap* sprite, + SkIPoint spriteOffset, const SkMatrixProvider& matrices, - sk_sp clip, + sk_sp clip, bool* ok) : fDevice(device) + , fSprite(sprite ? *sprite : SkPixmap{}) + , fSpriteOffset(spriteOffset) , fUniforms(kBlitterUniformsCount) - , fParams(effective_params(device, paint, matrices, std::move(clip))) + , fParams(effective_params(device, sprite, paint, matrices, std::move(clip))) , fKey(cache_key(fParams, &fUniforms, &fAlloc, ok)) , fPaint([&]{ SkColor4f color = paint.getColor4f(); @@ -547,6 +594,8 @@ namespace { private: SkPixmap fDevice; + const SkPixmap fSprite; // See isSprite(). + const SkIPoint fSpriteOffset; skvm::Uniforms fUniforms; // Most data is copied directly into fUniforms, SkArenaAlloc fAlloc{2*sizeof(void*)}; // but a few effects need to ref large content. const Params fParams; @@ -611,12 +660,24 @@ namespace { memcpy(fUniforms.buf.data(), &uniforms, sizeof(BlitterUniforms)); } + const void* isSprite(int x, int y) const { + if (fSprite.colorType() != kUnknown_SkColorType) { + return fSprite.addr(x - fSpriteOffset.x(), + y - fSpriteOffset.y()); + } + return nullptr; + } + void blitH(int x, int y, int w) override { if (fBlitH.empty()) { fBlitH = this->buildProgram(Coverage::Full); } this->updateUniforms(x+w, y); - fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y)); + if (const void* sprite = this->isSprite(x,y)) { + fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y), sprite); + } else { + fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y)); + } } void blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) override { @@ -625,8 +686,11 @@ namespace { } for (int16_t run = *runs; run > 0; run = *runs) { this->updateUniforms(x+run, y); - fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), cov); - + if (const void* sprite = this->isSprite(x,y)) { + fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), sprite, cov); + } else { + fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), cov); + } x += run; runs += run; cov += run; @@ -675,11 +739,21 @@ namespace { if (program == &fBlitMask3D) { size_t plane = mask.computeImageSize(); - program->eval(w, fUniforms.buf.data(), dptr, mptr + 1*plane - , mptr + 2*plane - , mptr + 0*plane); + if (const void* sprite = this->isSprite(x,y)) { + program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr + 1*plane + , mptr + 2*plane + , mptr + 0*plane); + } else { + program->eval(w, fUniforms.buf.data(), dptr, mptr + 1*plane + , mptr + 2*plane + , mptr + 0*plane); + } } else { - program->eval(w, fUniforms.buf.data(), dptr, mptr); + if (const void* sprite = this->isSprite(x,y)) { + program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr); + } else { + program->eval(w, fUniforms.buf.data(), dptr, mptr); + } } } } @@ -694,6 +768,27 @@ SkBlitter* SkCreateSkVMBlitter(const SkPixmap& device, SkArenaAlloc* alloc, sk_sp clip) { bool ok = true; - auto blitter = alloc->make(device, paint, matrices, std::move(clip), &ok); + auto blitter = alloc->make(device, paint, /*sprite=*/nullptr, SkIPoint{0,0}, + matrices, std::move(clip), &ok); + return ok ? blitter : nullptr; +} + +SkBlitter* SkCreateSkVMSpriteBlitter(const SkPixmap& device, + const SkPaint& paint, + const SkPixmap& sprite, + int left, int top, + SkArenaAlloc* alloc, + sk_sp clip) { + if (paint.getMaskFilter()) { + // TODO: SkVM support for mask filters? definitely possible! + return nullptr; + } + // Almost all SkColorTypes pass this check. This mostly just guards against 128-bit F32 now. + if (skvm::PixelFormat unused; !SkColorType_to_PixelFormat(sprite.colorType(), &unused)) { + return nullptr; + } + bool ok = true; + auto blitter = alloc->make(device, paint, &sprite, SkIPoint{left,top}, + SkSimpleMatrixProvider{SkMatrix{}}, std::move(clip), &ok); return ok ? blitter : nullptr; }