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