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