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