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