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