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