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