• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/private/SkImageInfoPriv.h"
9 #include "include/private/SkMacros.h"
10 #include "src/core/SkArenaAlloc.h"
11 #include "src/core/SkBlendModePriv.h"
12 #include "src/core/SkBlenderBase.h"
13 #include "src/core/SkColorFilterBase.h"
14 #include "src/core/SkColorSpacePriv.h"
15 #include "src/core/SkColorSpaceXformSteps.h"
16 #include "src/core/SkCoreBlitters.h"
17 #include "src/core/SkLRUCache.h"
18 #include "src/core/SkMatrixProvider.h"
19 #include "src/core/SkOpts.h"
20 #include "src/core/SkPaintPriv.h"
21 #include "src/core/SkVM.h"
22 #include "src/core/SkVMBlitter.h"
23 #include "src/shaders/SkColorFilterShader.h"
24 
25 #include <cinttypes>
26 
27 namespace {
28 
29     // Uniforms set by the Blitter itself,
30     // rather than by the Shader, which follow this struct in the skvm::Uniforms buffer.
31     struct BlitterUniforms {
32         int       right;  // First device x + blit run length n, used to get device x coordinate.
33         int       y;      // Device y coordinate.
34     };
35     static_assert(SkIsAlign4(sizeof(BlitterUniforms)), "");
36     inline static constexpr int kBlitterUniformsCount = sizeof(BlitterUniforms) / 4;
37 
device_coord(skvm::Builder * p,skvm::Uniforms * uniforms)38     static skvm::Coord device_coord(skvm::Builder* p, skvm::Uniforms* uniforms) {
39         skvm::I32 dx = p->uniform32(uniforms->base, offsetof(BlitterUniforms, right))
40                      - p->index(),
41                   dy = p->uniform32(uniforms->base, offsetof(BlitterUniforms, y));
42         return {
43             to_F32(dx) + 0.5f,
44             to_F32(dy) + 0.5f,
45         };
46     }
47 
48     struct NoopColorFilter : public SkColorFilterBase {
onProgram__anon97a78eef0111::NoopColorFilter49         skvm::Color onProgram(skvm::Builder*, skvm::Color c,
50                               const SkColorInfo&, skvm::Uniforms*, SkArenaAlloc*) const override {
51             return c;
52         }
53 
onAppendStages__anon97a78eef0111::NoopColorFilter54         bool onAppendStages(const SkStageRec&, bool) const override { return true; }
55 
56         // Only created here, should never be flattened / unflattened.
getFactory__anon97a78eef0111::NoopColorFilter57         Factory getFactory() const override { return nullptr; }
getTypeName__anon97a78eef0111::NoopColorFilter58         const char* getTypeName() const override { return "NoopColorFilter"; }
59     };
60 
61     struct SpriteShader : public SkShaderBase {
SpriteShader__anon97a78eef0111::SpriteShader62         explicit SpriteShader(SkPixmap sprite) : fSprite(sprite) {}
63 
64         SkPixmap fSprite;
65 
66         // Only created here temporarily... never serialized.
getFactory__anon97a78eef0111::SpriteShader67         Factory      getFactory() const override { return nullptr; }
getTypeName__anon97a78eef0111::SpriteShader68         const char* getTypeName() const override { return "SpriteShader"; }
69 
isOpaque__anon97a78eef0111::SpriteShader70         bool isOpaque() const override { return fSprite.isOpaque(); }
71 
onProgram__anon97a78eef0111::SpriteShader72         skvm::Color onProgram(skvm::Builder* p,
73                               skvm::Coord /*device*/, skvm::Coord /*local*/, skvm::Color /*paint*/,
74                               const SkMatrixProvider&, const SkMatrix* /*localM*/,
75                               const SkColorInfo& dst,
76                               skvm::Uniforms* uniforms, SkArenaAlloc*) const override {
77             const SkColorType ct = fSprite.colorType();
78 
79             skvm::PixelFormat fmt = skvm::SkColorType_to_PixelFormat(ct);
80 
81             skvm::Color c = p->load(fmt, p->varying(SkColorTypeBytesPerPixel(ct)));
82 
83             return SkColorSpaceXformSteps{fSprite, dst}.program(p, uniforms, c);
84         }
85     };
86 
87     struct DitherShader : public SkShaderBase {
DitherShader__anon97a78eef0111::DitherShader88         explicit DitherShader(sk_sp<SkShader> shader) : fShader(std::move(shader)) {}
89 
90         sk_sp<SkShader> fShader;
91 
92         // Only created here temporarily... never serialized.
getFactory__anon97a78eef0111::DitherShader93         Factory      getFactory() const override { return nullptr; }
getTypeName__anon97a78eef0111::DitherShader94         const char* getTypeName() const override { return "DitherShader"; }
95 
isOpaque__anon97a78eef0111::DitherShader96         bool isOpaque() const override { return fShader->isOpaque(); }
97 
onProgram__anon97a78eef0111::DitherShader98         skvm::Color onProgram(skvm::Builder* p,
99                               skvm::Coord device, skvm::Coord local, skvm::Color paint,
100                               const SkMatrixProvider& matrices, const SkMatrix* localM,
101                               const SkColorInfo& dst,
102                               skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
103             // Run our wrapped shader.
104             skvm::Color c = as_SB(fShader)->program(p, device,local, paint,
105                                                     matrices,localM, dst, uniforms,alloc);
106             if (!c) {
107                 return {};
108             }
109 
110             float rate = 0.0f;
111             switch (dst.colorType()) {
112                 case kARGB_4444_SkColorType:    rate =   1/15.0f; break;
113                 case   kRGB_565_SkColorType:    rate =   1/63.0f; break;
114                 case    kGray_8_SkColorType:
115                 case  kRGB_888x_SkColorType:
116                 case kRGBA_8888_SkColorType:
117                 case kBGRA_8888_SkColorType:
118                 case kSRGBA_8888_SkColorType:   rate =  1/255.0f; break;
119                 case kRGB_101010x_SkColorType:
120                 case kRGBA_1010102_SkColorType:
121                 case kBGR_101010x_SkColorType:
122                 case kBGRA_1010102_SkColorType: rate = 1/1023.0f; break;
123 
124                 case kUnknown_SkColorType:
125                 case kAlpha_8_SkColorType:
126                 case kRGBA_F16_SkColorType:
127                 case kRGBA_F16Norm_SkColorType:
128                 case kRGBA_F32_SkColorType:
129                 case kR8G8_unorm_SkColorType:
130                 case kA16_float_SkColorType:
131                 case kA16_unorm_SkColorType:
132                 case kR16G16_float_SkColorType:
133                 case kR16G16_unorm_SkColorType:
134                 case kR16G16B16A16_unorm_SkColorType: return c;
135             }
136 
137             // See SkRasterPipeline dither stage.
138             // This is 8x8 ordered dithering.  From here we'll only need dx and dx^dy.
139             SkASSERT(local.x.id == device.x.id);
140             SkASSERT(local.y.id == device.y.id);
141             skvm::I32 X =     trunc(device.x - 0.5f),
142                       Y = X ^ trunc(device.y - 0.5f);
143 
144             // If X's low bits are abc and Y's def, M is fcebda,
145             // 6 bits producing all values [0,63] shuffled over an 8x8 grid.
146             skvm::I32 M = shl(Y & 1, 5)
147                         | shl(X & 1, 4)
148                         | shl(Y & 2, 2)
149                         | shl(X & 2, 1)
150                         | shr(Y & 4, 1)
151                         | shr(X & 4, 2);
152 
153             // Scale to [0,1) by /64, then to (-0.5,0.5) using 63/128 (~0.492) as 0.5-ε,
154             // and finally scale all that by rate.  We keep dither strength strictly
155             // within ±0.5 to not change exact values like 0 or 1.
156 
157             // rate could be a uniform, but since it's based on the destination SkColorType,
158             // we can bake it in without hurting the cache hit rate.
159             float scale = rate * (  2/128.0f),
160                   bias  = rate * (-63/128.0f);
161             skvm::F32 dither = to_F32(M) * scale + bias;
162             c.r += dither;
163             c.g += dither;
164             c.b += dither;
165 
166             c.r = clamp(c.r, 0.0f, c.a);
167             c.g = clamp(c.g, 0.0f, c.a);
168             c.b = clamp(c.b, 0.0f, c.a);
169             return c;
170         }
171     };
172 
173     // This is similar to using SkShaders::Color(paint.getColor4f(), nullptr),
174     // but uses the blitter-provided paint color uniforms instead of pushing its own.
175     struct PaintColorShader : public SkShaderBase {
PaintColorShader__anon97a78eef0111::PaintColorShader176         explicit PaintColorShader(bool isOpaque) : fIsOpaque(isOpaque) {}
177 
178         const bool fIsOpaque;
179 
180         // Only created here temporarily... never serialized.
getFactory__anon97a78eef0111::PaintColorShader181         Factory      getFactory() const override { return nullptr; }
getTypeName__anon97a78eef0111::PaintColorShader182         const char* getTypeName() const override { return "PaintColorShader"; }
183 
isOpaque__anon97a78eef0111::PaintColorShader184         bool isOpaque() const override { return fIsOpaque; }
185 
onProgram__anon97a78eef0111::PaintColorShader186         skvm::Color onProgram(skvm::Builder*,
187                               skvm::Coord, skvm::Coord, skvm::Color paint,
188                               const SkMatrixProvider&, const SkMatrix*, const SkColorInfo&,
189                               skvm::Uniforms*, SkArenaAlloc*) const override {
190             // Incoming `paint` is unpremul in the destination color space,
191             // so we just need to premul it.
192             return premul(paint);
193         }
194     };
195 }  // namespace
196 
operator ==(const Key & that) const197 bool SkVMBlitter::Key::operator==(const Key& that) const {
198     return this->shader      == that.shader
199         && this->clip        == that.clip
200         && this->blender     == that.blender
201         && this->colorSpace  == that.colorSpace
202         && this->colorType   == that.colorType
203         && this->alphaType   == that.alphaType
204         && this->coverage    == that.coverage;
205 }
206 
withCoverage(Coverage c) const207 SkVMBlitter::Key SkVMBlitter::Key::withCoverage(Coverage c) const {
208     Key k = *this;
209     k.coverage = SkToU8(c);
210     return k;
211 }
212 
withCoverage(Coverage c) const213 SkVMBlitter::Params SkVMBlitter::Params::withCoverage(Coverage c) const {
214     Params p = *this;
215     p.coverage = c;
216     return p;
217 }
218 
EffectiveParams(const SkPixmap & device,const SkPixmap * sprite,SkPaint paint,const SkMatrixProvider & matrices,sk_sp<SkShader> clip)219 SkVMBlitter::Params SkVMBlitter::EffectiveParams(const SkPixmap& device,
220                                                  const SkPixmap* sprite,
221                                                  SkPaint paint,
222                                                  const SkMatrixProvider& matrices,
223                                                  sk_sp<SkShader> clip) {
224     // Sprites take priority over any shader.  (There's rarely one set, and it's meaningless.)
225     if (sprite) {
226         paint.setShader(sk_make_sp<SpriteShader>(*sprite));
227     }
228 
229     // Normal blitters will have already folded color filters into their shader,
230     // but we may still need to do that here for SpriteShaders.
231     if (paint.getColorFilter()) {
232         SkPaintPriv::RemoveColorFilter(&paint, device.colorSpace());
233     }
234     SkASSERT(!paint.getColorFilter());
235 
236     // If there's no explicit shader, the paint color is the shader,
237     // but if there is a shader, it's modulated by the paint alpha.
238     sk_sp<SkShader> shader = paint.refShader();
239     if (!shader) {
240         shader = sk_make_sp<PaintColorShader>(paint.getColor4f().isOpaque());
241     } else if (paint.getAlphaf() < 1.0f) {
242         shader = sk_make_sp<SkColorFilterShader>(std::move(shader),
243                                                  paint.getAlphaf(),
244                                                  sk_make_sp<NoopColorFilter>());
245     }
246 
247     // Add dither to the end of the shader pipeline if requested and needed.
248     if (paint.isDither() && !as_SB(shader)->isConstant()) {
249         shader = sk_make_sp<DitherShader>(std::move(shader));
250     }
251 
252     // Add the blender.
253     sk_sp<SkBlender> blender = paint.refBlender();
254     if (!blender) {
255         blender = SkBlender::Mode(SkBlendMode::kSrcOver);
256     }
257 
258     // The most common blend mode is SrcOver, and it can be strength-reduced
259     // _greatly_ to Src mode when the shader is opaque.
260     //
261     // In general all the information we use to make decisions here need to
262     // be reflected in Params and Key to make program caching sound, and it
263     // might appear that shader->isOpaque() is a property of the shader's
264     // uniforms than its fundamental program structure and so unsafe to use.
265     //
266     // Opacity is such a powerful property that SkShaderBase::program()
267     // forces opacity for any shader subclass that claims isOpaque(), so
268     // the opaque bit is strongly guaranteed to be part of the program and
269     // not just a property of the uniforms.  The shader program hash includes
270     // this information, making it safe to use anywhere in the blitter codegen.
271     if (as_BB(blender)->asBlendMode() == SkBlendMode::kSrcOver && shader->isOpaque()) {
272         blender = SkBlender::Mode(SkBlendMode::kSrc);
273     }
274 
275     SkColor4f paintColor = paint.getColor4f();
276     SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
277                            device.colorSpace(), kUnpremul_SkAlphaType}
278             .apply(paintColor.vec());
279 
280     return {
281         std::move(shader),
282         std::move(clip),
283         std::move(blender),
284         { device.colorType(), device.alphaType(), device.refColorSpace() },
285         Coverage::Full,  // Placeholder... withCoverage() will change as needed.
286         paintColor,
287         matrices,
288     };
289 }
290 
DstColor(skvm::Builder * p,const Params & params)291 skvm::Color SkVMBlitter::DstColor(skvm::Builder* p, const Params& params) {
292     skvm::PixelFormat dstFormat = skvm::SkColorType_to_PixelFormat(params.dst.colorType());
293     skvm::Ptr dst_ptr = p->varying(SkColorTypeBytesPerPixel(params.dst.colorType()));
294     return p->load(dstFormat, dst_ptr);
295 }
296 
BuildProgram(skvm::Builder * p,const Params & params,skvm::Uniforms * uniforms,SkArenaAlloc * alloc)297 void SkVMBlitter::BuildProgram(skvm::Builder* p, const Params& params,
298                                skvm::Uniforms* uniforms, SkArenaAlloc* alloc) {
299     // First two arguments are always uniforms and the destination buffer.
300     uniforms->base    = p->uniform();
301     skvm::Ptr dst_ptr = p->varying(SkColorTypeBytesPerPixel(params.dst.colorType()));
302     // A SpriteShader (in this file) may next use one argument as its varying source.
303     // Subsequent arguments depend on params.coverage:
304     //    - Full:      (no more arguments)
305     //    - Mask3D:    mul varying, add varying, 8-bit coverage varying
306     //    - MaskA8:    8-bit coverage varying
307     //    - MaskLCD16: 565 coverage varying
308     //    - UniformF:  float coverage uniform
309 
310     skvm::Coord device = device_coord(p, uniforms);
311     skvm::Color paint = p->uniformColor(params.paint, uniforms);
312 
313     // See note about arguments above: a SpriteShader will call p->arg() once during program().
314     skvm::Color src = as_SB(params.shader)->program(p, device, /*local=*/device, paint,
315                                                     params.matrices, /*localM=*/nullptr,
316                                                     params.dst, uniforms, alloc);
317     SkASSERT(src);
318     if (params.coverage == Coverage::Mask3D) {
319         skvm::F32 M = from_unorm(8, p->load8(p->varying<uint8_t>())),
320                   A = from_unorm(8, p->load8(p->varying<uint8_t>()));
321 
322         src.r = min(src.r * M + A, src.a);
323         src.g = min(src.g * M + A, src.a);
324         src.b = min(src.b * M + A, src.a);
325     }
326 
327     // GL clamps all its color channels to limits of the format just before the blend step (~here).
328     // TODO: Below, we also clamp after the blend step. If we can prove that none of the work here
329     // (especially blending, for built-in blend modes) will produce colors outside [0, 1] we may be
330     // able to skip the second clamp. For now, we clamp twice.
331     if (SkColorTypeIsNormalized(params.dst.colorType())) {
332         src = clamp01(src);
333     }
334 
335     // Load the destination color.
336     skvm::PixelFormat dstFormat = skvm::SkColorType_to_PixelFormat(params.dst.colorType());
337     skvm::Color dst = p->load(dstFormat, dst_ptr);
338     if (params.dst.isOpaque()) {
339         // When a destination is known opaque, we may assume it both starts and stays fully
340         // opaque, ignoring any math that disagrees.  This sometimes trims a little work.
341         dst.a = p->splat(1.0f);
342     } else if (params.dst.alphaType() == kUnpremul_SkAlphaType) {
343         // All our blending works in terms of premul.
344         dst = premul(dst);
345     }
346 
347     // Load coverage.
348     skvm::Color cov;
349     switch (params.coverage) {
350         case Coverage::Full:
351             cov.r = cov.g = cov.b = cov.a = p->splat(1.0f);
352             break;
353 
354         case Coverage::UniformF:
355             cov.r = cov.g = cov.b = cov.a = p->uniformF(p->uniform(), 0);
356             break;
357 
358         case Coverage::Mask3D:
359         case Coverage::MaskA8:
360             cov.r = cov.g = cov.b = cov.a = from_unorm(8, p->load8(p->varying<uint8_t>()));
361             break;
362 
363         case Coverage::MaskLCD16: {
364             skvm::PixelFormat fmt = skvm::SkColorType_to_PixelFormat(kRGB_565_SkColorType);
365             cov = p->load(fmt, p->varying<uint16_t>());
366             cov.a = select(src.a < dst.a, min(cov.r, min(cov.g, cov.b)),
367                            max(cov.r, max(cov.g, cov.b)));
368         } break;
369     }
370     if (params.clip) {
371         skvm::Color clip = as_SB(params.clip)->program(p, device, /*local=*/device, paint,
372                                                        params.matrices, /*localM=*/nullptr,
373                                                        params.dst, uniforms, alloc);
374         SkAssertResult(clip);
375         cov.r *= clip.a;  // We use the alpha channel of clip for all four.
376         cov.g *= clip.a;
377         cov.b *= clip.a;
378         cov.a *= clip.a;
379     }
380 
381     const SkBlenderBase* blender = as_BB(params.blender);
382     const auto as_blendmode = blender->asBlendMode();
383 
384     // The math for some blend modes lets us fold coverage into src before the blend, which is
385     // simpler than the canonical post-blend lerp().
386     bool applyPostBlendCoverage = true;
387     if (as_blendmode &&
388         SkBlendMode_ShouldPreScaleCoverage(as_blendmode.value(),
389                                            params.coverage == Coverage::MaskLCD16)) {
390         applyPostBlendCoverage = false;
391         src.r *= cov.r;
392         src.g *= cov.g;
393         src.b *= cov.b;
394         src.a *= cov.a;
395     }
396 
397     // Apply our blend function to the computed color.
398     src = blender->program(p, src, dst, params.dst, uniforms, alloc);
399 
400     if (applyPostBlendCoverage) {
401         src.r = lerp(dst.r, src.r, cov.r);
402         src.g = lerp(dst.g, src.g, cov.g);
403         src.b = lerp(dst.b, src.b, cov.b);
404         src.a = lerp(dst.a, src.a, cov.a);
405     }
406 
407     if (params.dst.isOpaque()) {
408         // (See the note above when loading the destination color.)
409         src.a = p->splat(1.0f);
410     } else if (params.dst.alphaType() == kUnpremul_SkAlphaType) {
411         src = unpremul(src);
412     }
413 
414     // Clamp to fit destination color format if needed.
415     if (SkColorTypeIsNormalized(params.dst.colorType())) {
416         src = clamp01(src);
417     }
418 
419     // Write it out!
420     store(dstFormat, dst_ptr, src);
421 }
422 
423 // If BuildProgram() can't build this program, CacheKey() sets *ok to false.
CacheKey(const Params & params,skvm::Uniforms * uniforms,SkArenaAlloc * alloc,bool * ok)424 SkVMBlitter::Key SkVMBlitter::CacheKey(
425         const Params& params, skvm::Uniforms* uniforms, SkArenaAlloc* alloc, bool* ok) {
426     // Take care to match buildProgram()'s reuse of the paint color uniforms.
427     skvm::Uniform r = uniforms->pushF(params.paint.fR),
428                   g = uniforms->pushF(params.paint.fG),
429                   b = uniforms->pushF(params.paint.fB),
430                   a = uniforms->pushF(params.paint.fA);
431 
432     auto hash_shader = [&](skvm::Builder& p, const sk_sp<SkShader>& shader,
433                            skvm::Color* outColor) {
434         const SkShaderBase* sb = as_SB(shader);
435 
436         skvm::Coord device = device_coord(&p, uniforms);
437         skvm::Color paint = {
438             p.uniformF(r),
439             p.uniformF(g),
440             p.uniformF(b),
441             p.uniformF(a),
442         };
443 
444         uint64_t hash = 0;
445         *outColor = sb->program(&p, device, /*local=*/device, paint, params.matrices,
446                 /*localM=*/nullptr, params.dst, uniforms, alloc);
447         if (*outColor) {
448             hash = p.hash();
449             // p.hash() folds in all instructions to produce r,g,b,a but does not know
450             // precisely which value we'll treat as which channel.  Imagine the shader
451             // called std::swap(*r,*b)... it draws differently, but p.hash() is unchanged.
452             // We'll fold the hash of their IDs in order to disambiguate.
453             const skvm::Val outputs[] = {
454                 outColor->r.id,
455                 outColor->g.id,
456                 outColor->b.id,
457                 outColor->a.id
458             };
459             hash ^= SkOpts::hash(outputs, sizeof(outputs));
460         } else {
461             *ok = false;
462         }
463         return hash;
464     };
465 
466     // Use this builder for shader, clip and blender, so that color objects that pass
467     // from one to the other all 'make sense' -- i.e. have the same builder and/or have
468     // meaningful values for the hash.
469     //
470     // Question: better if we just pass in mock uniform colors, so we don't need to
471     //           explicitly use the output color from one stage as input to another?
472     //
473     skvm::Builder p;
474 
475     // Calculate a hash for the color shader.
476     SkASSERT(params.shader);
477     skvm::Color src;
478     uint64_t shaderHash = hash_shader(p, params.shader, &src);
479 
480     // Calculate a hash for the clip shader, if one exists.
481     uint64_t clipHash = 0;
482     if (params.clip) {
483         skvm::Color cov;
484         clipHash = hash_shader(p, params.clip, &cov);
485         if (clipHash == 0) {
486             clipHash = 1;
487         }
488     }
489 
490     // Calculate a hash for the blender.
491     uint64_t blendHash = 0;
492     if (auto bm = as_BB(params.blender)->asBlendMode()) {
493         blendHash = static_cast<uint8_t>(bm.value());
494     } else if (*ok) {
495         const SkBlenderBase* blender = as_BB(params.blender);
496 
497         skvm::Color dst = DstColor(&p, params);
498         skvm::Color outColor = blender->program(&p, src, dst, params.dst, uniforms, alloc);
499         if (outColor) {
500             blendHash = p.hash();
501             // Like in `hash_shader` above, we must fold the color component IDs into our hash.
502             const skvm::Val outputs[] = {
503                 outColor.r.id,
504                 outColor.g.id,
505                 outColor.b.id,
506                 outColor.a.id
507             };
508             blendHash ^= SkOpts::hash(outputs, sizeof(outputs));
509         } else {
510             *ok = false;
511         }
512         if (blendHash == 0) {
513             blendHash = 1;
514         }
515     }
516 
517     return {
518         shaderHash,
519         clipHash,
520         blendHash,
521         params.dst.colorSpace() ? params.dst.colorSpace()->hash() : 0,
522         SkToU8(params.dst.colorType()),
523         SkToU8(params.dst.alphaType()),
524         SkToU8(params.coverage),
525     };
526 }
527 
SkVMBlitter(const SkPixmap & device,const SkPaint & paint,const SkPixmap * sprite,SkIPoint spriteOffset,const SkMatrixProvider & matrices,sk_sp<SkShader> clip,bool * ok)528 SkVMBlitter::SkVMBlitter(const SkPixmap& device,
529                          const SkPaint& paint,
530                          const SkPixmap* sprite,
531                          SkIPoint spriteOffset,
532                          const SkMatrixProvider& matrices,
533                          sk_sp<SkShader> clip,
534                          bool* ok)
535         : fDevice(device)
536         , fSprite(sprite ? *sprite : SkPixmap{})
537         , fSpriteOffset(spriteOffset)
538         , fUniforms(skvm::UPtr{0}, kBlitterUniformsCount)
539         , fParams(EffectiveParams(device, sprite, paint, matrices, std::move(clip)))
540         , fKey(CacheKey(fParams, &fUniforms, &fAlloc, ok)) {}
541 
~SkVMBlitter()542 SkVMBlitter::~SkVMBlitter() {
543     if (SkLRUCache<Key, skvm::Program>* cache = TryAcquireProgramCache()) {
544         auto cache_program = [&](skvm::Program&& program, Coverage coverage) {
545             if (!program.empty()) {
546                 cache->insert_or_update(fKey.withCoverage(coverage), std::move(program));
547             }
548         };
549         cache_program(std::move(fBlitH),         Coverage::Full);
550         cache_program(std::move(fBlitAntiH),     Coverage::UniformF);
551         cache_program(std::move(fBlitMaskA8),    Coverage::MaskA8);
552         cache_program(std::move(fBlitMask3D),    Coverage::Mask3D);
553         cache_program(std::move(fBlitMaskLCD16), Coverage::MaskLCD16);
554 
555         ReleaseProgramCache();
556     }
557 }
558 
TryAcquireProgramCache()559 SkLRUCache<SkVMBlitter::Key, skvm::Program>* SkVMBlitter::TryAcquireProgramCache() {
560 #if defined(SKVM_JIT)
561     thread_local static SkLRUCache<Key, skvm::Program> cache{64};
562     return &cache;
563 #else
564     // iOS now supports thread_local since iOS 9.
565     // On the other hand, we'll never be able to JIT there anyway.
566     // It's probably fine to not cache any interpreted programs, anywhere.
567     return nullptr;
568 #endif
569 }
570 
DebugName(const Key & key)571 SkString SkVMBlitter::DebugName(const Key& key) {
572     return SkStringPrintf("Shader-%" PRIx64 "_Clip-%" PRIx64 "_Blender-%" PRIx64
573                           "_CS-%" PRIx64 "_CT-%d_AT-%d_Cov-%d",
574                           key.shader,
575                           key.clip,
576                           key.blender,
577                           key.colorSpace,
578                           key.colorType,
579                           key.alphaType,
580                           key.coverage);
581 }
582 
ReleaseProgramCache()583 void SkVMBlitter::ReleaseProgramCache() {}
584 
buildProgram(Coverage coverage)585 skvm::Program SkVMBlitter::buildProgram(Coverage coverage) {
586     Key key = fKey.withCoverage(coverage);
587     {
588         skvm::Program p;
589         if (SkLRUCache<Key, skvm::Program>* cache = TryAcquireProgramCache()) {
590             if (skvm::Program* found = cache->find(key)) {
591                 p = std::move(*found);
592             }
593             ReleaseProgramCache();
594         }
595         if (!p.empty()) {
596             return p;
597         }
598     }
599     // We don't really _need_ to rebuild fUniforms here.
600     // It's just more natural to have effects unconditionally emit them,
601     // and more natural to rebuild fUniforms than to emit them into a temporary buffer.
602     // fUniforms should reuse the exact same memory, so this is very cheap.
603     SkDEBUGCODE(size_t prev = fUniforms.buf.size();)
604     fUniforms.buf.resize(kBlitterUniformsCount);
605     skvm::Builder builder;
606     BuildProgram(&builder, fParams.withCoverage(coverage), &fUniforms, &fAlloc);
607     SkASSERTF(fUniforms.buf.size() == prev,
608               "%zu, prev was %zu", fUniforms.buf.size(), prev);
609 
610     skvm::Program program = builder.done(DebugName(key).c_str());
611     if (false) {
612         static std::atomic<int> missed{0},
613                 total{0};
614         if (!program.hasJIT()) {
615             SkDebugf("\ncouldn't JIT %s\n", DebugName(key).c_str());
616             builder.dump();
617             program.dump();
618 
619             missed++;
620         }
621         if (0 == total++) {
622             atexit([]{ SkDebugf("SkVMBlitter compiled %d programs, %d without JIT.\n",
623                                 total.load(), missed.load()); });
624         }
625     }
626     return program;
627 }
628 
updateUniforms(int right,int y)629 void SkVMBlitter::updateUniforms(int right, int y) {
630     BlitterUniforms uniforms{right, y};
631     memcpy(fUniforms.buf.data(), &uniforms, sizeof(BlitterUniforms));
632 }
633 
isSprite(int x,int y) const634 const void* SkVMBlitter::isSprite(int x, int y) const {
635     if (fSprite.colorType() != kUnknown_SkColorType) {
636         return fSprite.addr(x - fSpriteOffset.x(),
637                             y - fSpriteOffset.y());
638     }
639     return nullptr;
640 }
641 
blitH(int x,int y,int w)642 void SkVMBlitter::blitH(int x, int y, int w) {
643     if (fBlitH.empty()) {
644         fBlitH = this->buildProgram(Coverage::Full);
645     }
646     this->updateUniforms(x+w, y);
647     if (const void* sprite = this->isSprite(x,y)) {
648         fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y), sprite);
649     } else {
650         fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y));
651     }
652 }
653 
blitAntiH(int x,int y,const SkAlpha cov[],const int16_t runs[])654 void SkVMBlitter::blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) {
655     if (fBlitAntiH.empty()) {
656         fBlitAntiH = this->buildProgram(Coverage::UniformF);
657     }
658     if (fBlitH.empty()) {
659         fBlitH = this->buildProgram(Coverage::Full);
660     }
661     for (int16_t run = *runs; run > 0; run = *runs) {
662         const SkAlpha coverage = *cov;
663         if (coverage != 0x00) {
664             this->updateUniforms(x+run, y);
665             const void* sprite = this->isSprite(x,y);
666             if (coverage == 0xFF) {
667                 if (sprite) {
668                     fBlitH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), sprite);
669                 } else {
670                     fBlitH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y));
671                 }
672             } else {
673                 const float covF = *cov * (1/255.0f);
674                 if (sprite) {
675                     fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), sprite, &covF);
676                 } else {
677                     fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), &covF);
678                 }
679             }
680         }
681         x    += run;
682         runs += run;
683         cov  += run;
684     }
685 }
686 
blitMask(const SkMask & mask,const SkIRect & clip)687 void SkVMBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
688     if (mask.fFormat == SkMask::kBW_Format) {
689         return SkBlitter::blitMask(mask, clip);
690     }
691 
692     const skvm::Program* program = nullptr;
693     switch (mask.fFormat) {
694         default: SkUNREACHABLE;     // ARGB and SDF masks shouldn't make it here.
695 
696         case SkMask::k3D_Format:
697             if (fBlitMask3D.empty()) {
698                 fBlitMask3D = this->buildProgram(Coverage::Mask3D);
699             }
700             program = &fBlitMask3D;
701             break;
702 
703         case SkMask::kA8_Format:
704             if (fBlitMaskA8.empty()) {
705                 fBlitMaskA8 = this->buildProgram(Coverage::MaskA8);
706             }
707             program = &fBlitMaskA8;
708             break;
709 
710         case SkMask::kLCD16_Format:
711             if (fBlitMaskLCD16.empty()) {
712                 fBlitMaskLCD16 = this->buildProgram(Coverage::MaskLCD16);
713             }
714             program = &fBlitMaskLCD16;
715             break;
716     }
717 
718     SkASSERT(program);
719     if (program) {
720         for (int y = clip.top(); y < clip.bottom(); y++) {
721              int x = clip.left(),
722                  w = clip.width();
723             void* dptr =        fDevice.writable_addr(x,y);
724             auto  mptr = (const uint8_t*)mask.getAddr(x,y);
725             this->updateUniforms(x+w,y);
726 
727             if (program == &fBlitMask3D) {
728                 size_t plane = mask.computeImageSize();
729                 if (const void* sprite = this->isSprite(x,y)) {
730                     program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr + 1*plane
731                                                                        , mptr + 2*plane
732                                                                        , mptr + 0*plane);
733                 } else {
734                     program->eval(w, fUniforms.buf.data(), dptr, mptr + 1*plane
735                                                                , mptr + 2*plane
736                                                                , mptr + 0*plane);
737                 }
738             } else {
739                 if (const void* sprite = this->isSprite(x,y)) {
740                     program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr);
741                 } else {
742                     program->eval(w, fUniforms.buf.data(), dptr, mptr);
743                 }
744             }
745         }
746     }
747 }
748 
Make(const SkPixmap & device,const SkPaint & paint,const SkMatrixProvider & matrices,SkArenaAlloc * alloc,sk_sp<SkShader> clip)749 SkVMBlitter* SkVMBlitter::Make(const SkPixmap& device,
750                                const SkPaint& paint,
751                                const SkMatrixProvider& matrices,
752                                SkArenaAlloc* alloc,
753                                sk_sp<SkShader> clip) {
754     bool ok = true;
755     SkVMBlitter* blitter = alloc->make<SkVMBlitter>(
756             device, paint, /*sprite=*/nullptr, SkIPoint{0,0}, matrices, std::move(clip), &ok);
757     return ok ? blitter : nullptr;
758 }
759 
Make(const SkPixmap & device,const SkPaint & paint,const SkPixmap & sprite,int left,int top,SkArenaAlloc * alloc,sk_sp<SkShader> clip)760 SkVMBlitter* SkVMBlitter::Make(const SkPixmap& device,
761                                const SkPaint& paint,
762                                const SkPixmap& sprite,
763                                int left, int top,
764                                SkArenaAlloc* alloc,
765                                sk_sp<SkShader> clip) {
766     if (paint.getMaskFilter()) {
767         // TODO: SkVM support for mask filters?  definitely possible!
768         return nullptr;
769     }
770     bool ok = true;
771     auto blitter = alloc->make<SkVMBlitter>(
772             device, paint, &sprite, SkIPoint{left,top},
773             SkSimpleMatrixProvider{SkMatrix{}}, std::move(clip), &ok);
774     return ok ? blitter : nullptr;
775 }
776