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