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