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