1 /*
2 * Copyright 2021 Google LLC
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 "tests/Test.h"
9
10 #if defined(SK_GRAPHITE)
11
12 #include "include/core/SkBitmap.h"
13 #include "include/core/SkBlurTypes.h"
14 #include "include/core/SkCanvas.h"
15 #include "include/core/SkM44.h"
16 #include "include/core/SkMaskFilter.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPathBuilder.h"
19 #include "include/core/SkPicture.h"
20 #include "include/core/SkPictureRecorder.h"
21 #include "include/core/SkRRect.h"
22 #include "include/core/SkShader.h"
23 #include "include/core/SkTextBlob.h"
24 #include "include/core/SkVertices.h"
25 #include "include/core/SkYUVAPixmaps.h"
26 #include "include/effects/SkBlenders.h"
27 #include "include/effects/SkColorMatrix.h"
28 #include "include/effects/SkGradientShader.h"
29 #include "include/effects/SkHighContrastFilter.h"
30 #include "include/effects/SkImageFilters.h"
31 #include "include/effects/SkLumaColorFilter.h"
32 #include "include/effects/SkOverdrawColorFilter.h"
33 #include "include/effects/SkPerlinNoiseShader.h"
34 #include "include/effects/SkRuntimeEffect.h"
35 #include "include/gpu/graphite/Image.h"
36 #include "include/gpu/graphite/Recorder.h"
37 #include "include/gpu/graphite/Surface.h"
38 #include "include/gpu/graphite/PrecompileContext.h"
39 #include "include/gpu/graphite/precompile/Precompile.h"
40 #include "include/gpu/graphite/precompile/PrecompileBlender.h"
41 #include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
42 #include "include/gpu/graphite/precompile/PrecompileImageFilter.h"
43 #include "include/gpu/graphite/precompile/PrecompileMaskFilter.h"
44 #include "include/gpu/graphite/precompile/PrecompileRuntimeEffect.h"
45 #include "include/gpu/graphite/precompile/PrecompileShader.h"
46 #include "src/base/SkRandom.h"
47 #include "src/core/SkBlenderBase.h"
48 #include "src/core/SkColorFilterPriv.h"
49 #include "src/core/SkRuntimeEffectPriv.h"
50 #include "src/gpu/graphite/ContextPriv.h"
51 #include "src/gpu/graphite/ContextUtils.h"
52 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
53 #include "src/gpu/graphite/KeyContext.h"
54 #include "src/gpu/graphite/KeyHelpers.h"
55 #include "src/gpu/graphite/PaintParams.h"
56 #include "src/gpu/graphite/PipelineData.h"
57 #include "src/gpu/graphite/PrecompileContextPriv.h"
58 #include "src/gpu/graphite/RecorderPriv.h"
59 #include "src/gpu/graphite/RenderPassDesc.h"
60 #include "src/gpu/graphite/Renderer.h"
61 #include "src/gpu/graphite/ResourceProvider.h"
62 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
63 #include "src/gpu/graphite/ShaderCodeDictionary.h"
64 #include "src/gpu/graphite/UniquePaintParamsID.h"
65 #include "src/gpu/graphite/geom/Geometry.h"
66 #include "src/gpu/graphite/precompile/PaintOptionsPriv.h"
67 #include "src/gpu/graphite/precompile/PrecompileColorFiltersPriv.h"
68 #include "src/gpu/graphite/precompile/PrecompileShadersPriv.h"
69 #include "src/shaders/SkImageShader.h"
70 #include "tools/ToolUtils.h"
71 #include "tools/fonts/FontToolUtils.h"
72 #include "tools/graphite/GraphiteTestContext.h"
73 #include "tools/graphite/UniqueKeyUtils.h"
74
75 // Set this to 1 for more expansive (aka far slower) local testing
76 #define EXPANDED_SET 0
77
78 // This flag is set to true if during the PaintOption creation an SkPictureShader is created.
79 // The SkPictureShader will need an additional program in order to draw the contents of its
80 // SkPicture.
81 bool gNeedSKPPaintOption = false;
82
83 constexpr uint32_t kDefaultSeed = 0;
84
85 using namespace skgpu::graphite;
86
87 namespace {
88
89 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_random_shader(SkRandom*, Recorder*);
90 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_random_blender(SkRandom*);
91 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_random_colorfilter(SkRandom*);
92
93 [[maybe_unused]] std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>>
94 create_random_image_filter(Recorder*, SkRandom*);
95
96 //--------------------------------------------------------------------------------------------------
97 //--------------------------------------------------------------------------------------------------
98 // M(Empty)
99 #define SK_ALL_TEST_SHADERS(M) \
100 M(Blend) \
101 M(ColorFilter) \
102 M(CoordClamp) \
103 M(ConicalGradient) \
104 M(Image) \
105 M(LinearGradient) \
106 M(LocalMatrix) \
107 M(None) \
108 M(PerlinNoise) \
109 M(Picture) \
110 M(RadialGradient) \
111 M(Runtime) \
112 M(SolidColor) \
113 M(SweepGradient) \
114 M(YUVImage) \
115 M(WorkingColorSpace)
116
117 enum class ShaderType {
118 #define M(type) k##type,
119 SK_ALL_TEST_SHADERS(M)
120 #undef M
121
122 kLast = kWorkingColorSpace
123 };
124
125 static constexpr int kShaderTypeCount = static_cast<int>(ShaderType::kLast) + 1;
126
to_str(ShaderType s)127 const char* to_str(ShaderType s) {
128 switch (s) {
129 #define M(type) case ShaderType::k##type : return "ShaderType::k" #type;
130 SK_ALL_TEST_SHADERS(M)
131 #undef M
132 }
133
134 SkUNREACHABLE;
135 }
136
137 //--------------------------------------------------------------------------------------------------
138 //--------------------------------------------------------------------------------------------------
139 #define SK_ALL_TEST_MASKFILTERS(M) \
140 M(None) \
141 M(Blur)
142
143 enum class MaskFilterType {
144 #define M(type) k##type,
145 SK_ALL_TEST_MASKFILTERS(M)
146 #undef M
147
148 kLast = kBlur
149 };
150
151 static constexpr int kMaskFilterTypeCount = static_cast<int>(MaskFilterType::kLast) + 1;
152
to_str(MaskFilterType mf)153 const char* to_str(MaskFilterType mf) {
154 switch (mf) {
155 #define M(type) case MaskFilterType::k##type : return "MaskFilterType::k" #type;
156 SK_ALL_TEST_MASKFILTERS(M)
157 #undef M
158 }
159
160 SkUNREACHABLE;
161 }
162
163 //--------------------------------------------------------------------------------------------------
164 //--------------------------------------------------------------------------------------------------
165 #define SK_ALL_TEST_BLENDERS(M) \
166 M(None) \
167 M(PorterDuff) \
168 M(ShaderBased) \
169 M(Arithmetic) \
170 M(Runtime)
171
172 // TODO: do we need to add a separable category and/or a category for dstRead requiring blends?
173 enum class BlenderType {
174 #define M(type) k##type,
175 SK_ALL_TEST_BLENDERS(M)
176 #undef M
177
178 kLast = kRuntime
179 };
180
181 static constexpr int kBlenderTypeCount = static_cast<int>(BlenderType::kLast) + 1;
182
to_str(BlenderType b)183 const char* to_str(BlenderType b) {
184 switch (b) {
185 #define M(type) case BlenderType::k##type : return "BlenderType::k" #type;
186 SK_ALL_TEST_BLENDERS(M)
187 #undef M
188 }
189
190 SkUNREACHABLE;
191 }
192
193 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_blender(SkRandom*, BlenderType);
194
195 //--------------------------------------------------------------------------------------------------
196 //--------------------------------------------------------------------------------------------------
197 #define SK_ALL_TEST_COLORFILTERS(M) \
198 M(None) \
199 M(BlendMode) \
200 M(ColorSpaceXform) \
201 M(Compose) \
202 M(Gaussian) \
203 M(HighContrast) \
204 M(HSLAMatrix) \
205 M(Lerp) \
206 M(Lighting) \
207 M(LinearToSRGB) \
208 M(Luma) \
209 M(Matrix) \
210 M(Overdraw) \
211 M(Runtime) \
212 M(SRGBToLinear) \
213 M(Table) \
214 M(WorkingFormat)
215
216 enum class ColorFilterType {
217 #define M(type) k##type,
218 SK_ALL_TEST_COLORFILTERS(M)
219 #undef M
220
221 kLast = kWorkingFormat
222 };
223
224 static constexpr int kColorFilterTypeCount = static_cast<int>(ColorFilterType::kLast) + 1;
225
to_str(ColorFilterType cf)226 const char* to_str(ColorFilterType cf) {
227 switch (cf) {
228 #define M(type) case ColorFilterType::k##type : return "ColorFilterType::k" #type;
229 SK_ALL_TEST_COLORFILTERS(M)
230 #undef M
231 }
232
233 SkUNREACHABLE;
234 }
235
236 //--------------------------------------------------------------------------------------------------
237 //--------------------------------------------------------------------------------------------------
238 #define SK_ALL_TEST_CLIPS(M) \
239 M(None) \
240 M(Shader) \
241 M(Shader_Diff)
242
243 enum class ClipType {
244 #define M(type) k##type,
245 SK_ALL_TEST_CLIPS(M)
246 #undef M
247
248 kLast = kShader_Diff
249 };
250
251 static constexpr int kClipTypeCount = static_cast<int>(ClipType::kLast) + 1;
252
to_str(ClipType c)253 const char* to_str(ClipType c) {
254 switch (c) {
255 #define M(type) case ClipType::k##type : return "ClipType::k" #type;
256 SK_ALL_TEST_CLIPS(M)
257 #undef M
258 }
259
260 SkUNREACHABLE;
261 }
262
263 //--------------------------------------------------------------------------------------------------
264 //--------------------------------------------------------------------------------------------------
265 #define SK_ALL_TEST_IMAGE_FILTERS(M) \
266 M(None) \
267 M(Arithmetic) \
268 M(BlendMode) \
269 M(RuntimeBlender) \
270 M(Blur) \
271 M(ColorFilter) \
272 M(Displacement) \
273 M(Lighting) \
274 M(MatrixConvolution) \
275 M(Morphology)
276
277 enum class ImageFilterType {
278 #define M(type) k##type,
279 SK_ALL_TEST_IMAGE_FILTERS(M)
280 #undef M
281 kLast = kMorphology
282 };
283
284 static constexpr int kImageFilterTypeCount = static_cast<int>(ImageFilterType::kLast) + 1;
285
to_str(ImageFilterType c)286 const char* to_str(ImageFilterType c) {
287 switch (c) {
288 #define M(type) case ImageFilterType::k##type : return "ImageFilterType::k" #type;
289 SK_ALL_TEST_IMAGE_FILTERS(M)
290 #undef M
291 }
292 SkUNREACHABLE;
293 }
294
to_str(DrawTypeFlags dt)295 const char* to_str(DrawTypeFlags dt) {
296 SkASSERT(SkPopCount(static_cast<uint32_t>(dt)) == 1);
297
298 switch (dt) {
299 case DrawTypeFlags::kBitmapText_Mask: return "DrawTypeFlags::kBitmapText_Mask";
300 case DrawTypeFlags::kBitmapText_LCD: return "DrawTypeFlags::kBitmapText_LCD";
301 case DrawTypeFlags::kBitmapText_Color: return "DrawTypeFlags::kBitmapText_Color";
302 case DrawTypeFlags::kSDFText: return "DrawTypeFlags::kSDFText";
303 case DrawTypeFlags::kSDFText_LCD: return "DrawTypeFlags::kSDFText_LCD";
304 case DrawTypeFlags::kDrawVertices: return "DrawTypeFlags::kDrawVertices";
305 case DrawTypeFlags::kSimpleShape: return "DrawTypeFlags::kSimpleShape";
306 case DrawTypeFlags::kNonSimpleShape: return "DrawTypeFlags::kNonSimpleShape";
307 default: SkASSERT(0); return "DrawTypeFlags::kNone";
308 }
309
310 SkUNREACHABLE;
311 }
312
log_run(const char * label,uint32_t seed,ShaderType s,BlenderType bm,ColorFilterType cf,MaskFilterType mf,ImageFilterType imageFilter,ClipType clip,DrawTypeFlags drawTypeFlags)313 void log_run(const char* label,
314 uint32_t seed,
315 ShaderType s,
316 BlenderType bm,
317 ColorFilterType cf,
318 MaskFilterType mf,
319 ImageFilterType imageFilter,
320 ClipType clip,
321 DrawTypeFlags drawTypeFlags) {
322 SkDebugf("%s:\n"
323 "//------------------------\n"
324 "uint32_t seed = %u;\n"
325 "ShaderType shaderType = %s;\n"
326 "BlenderType blenderType = %s;\n"
327 "ColorFilterType colorFilterType = %s;\n"
328 "MaskFilterType maskFilterType = %s;\n"
329 "ImageFilterType imageFilterType = %s;\n"
330 "ClipType clipType = %s;\n"
331 "DrawTypeFlags drawTypeFlags = %s;\n"
332 "//-----------------------\n",
333 label, seed,
334 to_str(s), to_str(bm), to_str(cf), to_str(mf), to_str(imageFilter), to_str(clip),
335 to_str(drawTypeFlags));
336 }
337
338 //--------------------------------------------------------------------------------------------------
339 //--------------------------------------------------------------------------------------------------
340 static constexpr skcms_TransferFunction gTransferFunctions[] = {
341 SkNamedTransferFn::kSRGB,
342 SkNamedTransferFn::k2Dot2,
343 SkNamedTransferFn::kLinear,
344 SkNamedTransferFn::kRec2020,
345 SkNamedTransferFn::kPQ,
346 SkNamedTransferFn::kHLG,
347 };
348
349 static constexpr int kTransferFunctionCount = std::size(gTransferFunctions);
350
random_xfer_function(SkRandom * rand)351 const skcms_TransferFunction& random_xfer_function(SkRandom* rand) {
352 return gTransferFunctions[rand->nextULessThan(kTransferFunctionCount)];
353 }
354
355 static constexpr skcms_Matrix3x3 gGamuts[] = {
356 SkNamedGamut::kSRGB,
357 SkNamedGamut::kAdobeRGB,
358 SkNamedGamut::kDisplayP3,
359 SkNamedGamut::kRec2020,
360 SkNamedGamut::kXYZ,
361 };
362
363 static constexpr int kGamutCount = std::size(gGamuts);
364
random_gamut(SkRandom * rand)365 const skcms_Matrix3x3& random_gamut(SkRandom* rand) {
366 return gGamuts[rand->nextULessThan(kGamutCount)];
367 }
368
369 enum class ColorSpaceType {
370 kNone,
371 kSRGB,
372 kSRGBLinear,
373 kRGB,
374
375 kLast = kRGB
376 };
377
378 static constexpr int kColorSpaceTypeCount = static_cast<int>(ColorSpaceType::kLast) + 1;
379
random_colorspacetype(SkRandom * rand)380 ColorSpaceType random_colorspacetype(SkRandom* rand) {
381 return static_cast<ColorSpaceType>(rand->nextULessThan(kColorSpaceTypeCount));
382 }
383
random_colorspace(SkRandom * rand)384 sk_sp<SkColorSpace> random_colorspace(SkRandom* rand) {
385 ColorSpaceType cs = random_colorspacetype(rand);
386
387 switch (cs) {
388 case ColorSpaceType::kNone:
389 return nullptr;
390 case ColorSpaceType::kSRGB:
391 return SkColorSpace::MakeSRGB();
392 case ColorSpaceType::kSRGBLinear:
393 return SkColorSpace::MakeSRGBLinear();
394 case ColorSpaceType::kRGB:
395 return SkColorSpace::MakeRGB(random_xfer_function(rand), random_gamut(rand));
396 }
397
398 SkUNREACHABLE;
399 }
400
401 enum class ColorConstraint {
402 kNone,
403 kOpaque,
404 kTransparent,
405 };
406
random_color(SkRandom * rand,ColorConstraint constraint)407 SkColor random_color(SkRandom* rand, ColorConstraint constraint) {
408 uint32_t color = rand->nextU();
409
410 switch (constraint) {
411 case ColorConstraint::kNone: return color;
412 case ColorConstraint::kOpaque: return 0xff000000 | color;
413 case ColorConstraint::kTransparent: return 0x80000000 | color;
414 }
415
416 SkUNREACHABLE;
417 }
418
random_color4f(SkRandom * rand,ColorConstraint constraint)419 SkColor4f random_color4f(SkRandom* rand, ColorConstraint constraint) {
420 SkColor4f result = { rand->nextRangeF(0.0f, 1.0f),
421 rand->nextRangeF(0.0f, 1.0f),
422 rand->nextRangeF(0.0f, 1.0f),
423 rand->nextRangeF(0.0f, 1.0f) };
424
425 switch (constraint) {
426 case ColorConstraint::kNone: return result;
427 case ColorConstraint::kOpaque: result.fA = 1.0f; return result;
428 case ColorConstraint::kTransparent: result.fA = 0.5f; return result;
429 }
430
431 SkUNREACHABLE;
432 }
433
random_tilemode(SkRandom * rand)434 SkTileMode random_tilemode(SkRandom* rand) {
435 return static_cast<SkTileMode>(rand->nextULessThan(kSkTileModeCount));
436 }
437
random_shadertype(SkRandom * rand)438 ShaderType random_shadertype(SkRandom* rand) {
439 return static_cast<ShaderType>(rand->nextULessThan(kShaderTypeCount));
440 }
441
random_porter_duff_bm(SkRandom * rand)442 SkBlendMode random_porter_duff_bm(SkRandom* rand) {
443 // NOTE: The porter duff modes refer to being able to be consolidated into a single
444 // call to sk_porter_duff_blend(), which has slightly fewer compatible blend modes than the
445 // "coefficient" blend mode that supports HW blending.
446 return static_cast<SkBlendMode>(rand->nextRangeU((unsigned int) SkBlendMode::kClear,
447 (unsigned int) SkBlendMode::kPlus));
448 }
449
random_complex_bm(SkRandom * rand)450 SkBlendMode random_complex_bm(SkRandom* rand) {
451 return static_cast<SkBlendMode>(rand->nextRangeU((unsigned int) SkBlendMode::kPlus,
452 (unsigned int) SkBlendMode::kLastMode));
453 }
454
random_blend_mode(SkRandom * rand)455 SkBlendMode random_blend_mode(SkRandom* rand) {
456 return static_cast<SkBlendMode>(rand->nextULessThan(kSkBlendModeCount));
457 }
458
random_maskfiltertype(SkRandom * rand)459 [[maybe_unused]] MaskFilterType random_maskfiltertype(SkRandom* rand) {
460 if (rand->nextBool()) {
461 return MaskFilterType::kNone; // bias this towards no mask filter
462 }
463
464 return static_cast<MaskFilterType>(rand->nextULessThan(kMaskFilterTypeCount));
465 }
466
random_blendertype(SkRandom * rand)467 BlenderType random_blendertype(SkRandom* rand) {
468 return static_cast<BlenderType>(rand->nextULessThan(kBlenderTypeCount));
469 }
470
random_colorfiltertype(SkRandom * rand)471 ColorFilterType random_colorfiltertype(SkRandom* rand) {
472 return static_cast<ColorFilterType>(rand->nextULessThan(kColorFilterTypeCount));
473 }
474
random_imagefiltertype(SkRandom * rand)475 ImageFilterType random_imagefiltertype(SkRandom* rand) {
476 return static_cast<ImageFilterType>(rand->nextULessThan(kImageFilterTypeCount));
477 }
478
random_cliptype(SkRandom * rand)479 [[maybe_unused]] ClipType random_cliptype(SkRandom* rand) {
480 if (rand->nextBool()) {
481 return ClipType::kNone; // bias this towards no clip
482 }
483
484 return static_cast<ClipType>(rand->nextULessThan(kClipTypeCount));
485 }
486
random_drawtype(SkRandom * rand)487 [[maybe_unused]] DrawTypeFlags random_drawtype(SkRandom* rand) {
488 uint32_t index = rand->nextULessThan(8);
489
490 switch (index) {
491 case 0: return DrawTypeFlags::kBitmapText_Mask;
492 case 1: return DrawTypeFlags::kBitmapText_LCD;
493 case 2: return DrawTypeFlags::kBitmapText_Color;
494 case 3: return DrawTypeFlags::kSDFText;
495 case 4: return DrawTypeFlags::kSDFText_LCD;
496 case 5: return DrawTypeFlags::kDrawVertices;
497 case 6: return DrawTypeFlags::kSimpleShape;
498 case 7: return DrawTypeFlags::kNonSimpleShape;
499 }
500
501 SkASSERT(0);
502 return DrawTypeFlags::kNone;
503 }
504
505 enum LocalMatrixConstraint {
506 kNone,
507 kWithPerspective,
508 };
509
random_local_matrix(SkRandom * rand,SkMatrix * storage,LocalMatrixConstraint constaint=LocalMatrixConstraint::kNone)510 SkMatrix* random_local_matrix(SkRandom* rand,
511 SkMatrix* storage,
512 LocalMatrixConstraint constaint = LocalMatrixConstraint::kNone) {
513 // Only return nullptr if constraint == kNone.
514 uint32_t matrix = rand->nextULessThan(constaint == LocalMatrixConstraint::kNone ? 4 : 3);
515
516 switch (matrix) {
517 case 0: storage->setTranslate(2.0f, 2.0f); break;
518 case 1: storage->setIdentity(); break;
519 case 2: storage->setScale(0.25f, 0.25f, 0.0f, 0.0f); break;
520 case 3: return nullptr;
521 }
522
523 if (constaint == LocalMatrixConstraint::kWithPerspective) {
524 storage->setPerspX(0.5f);
525 }
526
527 return storage;
528 }
529
make_image(SkRandom * rand,Recorder * recorder)530 sk_sp<SkImage> make_image(SkRandom* rand, Recorder* recorder) {
531 SkColorType ct = SkColorType::kRGBA_8888_SkColorType;
532 if (rand->nextBool()) {
533 ct = SkColorType::kAlpha_8_SkColorType;
534 }
535
536 SkImageInfo info = SkImageInfo::Make(32, 32, ct, kPremul_SkAlphaType, random_colorspace(rand));
537
538 SkBitmap bitmap;
539 bitmap.allocPixels(info);
540 bitmap.eraseColor(SK_ColorBLACK);
541
542 sk_sp<SkImage> img = bitmap.asImage();
543
544 // TODO: fuzz mipmappedness
545 return SkImages::TextureFromImage(recorder, img, {false});
546 }
547
make_yuv_image(SkRandom * rand,Recorder * recorder)548 sk_sp<SkImage> make_yuv_image(SkRandom* rand, Recorder* recorder) {
549
550 SkYUVAInfo::PlaneConfig planeConfig = SkYUVAInfo::PlaneConfig::kY_UV;
551 if (rand->nextBool()) {
552 planeConfig = SkYUVAInfo::PlaneConfig::kY_U_V_A;
553 }
554
555 SkYUVAInfo yuvaInfo({ 32, 32, },
556 planeConfig,
557 SkYUVAInfo::Subsampling::k420,
558 kJPEG_Full_SkYUVColorSpace);
559
560 SkASSERT(yuvaInfo.isValid());
561
562 SkYUVAPixmapInfo pmInfo(yuvaInfo, SkYUVAPixmapInfo::DataType::kUnorm8, nullptr);
563
564 SkYUVAPixmaps pixmaps = SkYUVAPixmaps::Allocate(pmInfo);
565
566 for (int i = 0; i < pixmaps.numPlanes(); ++i) {
567 pixmaps.plane(i).erase(SK_ColorBLACK);
568 }
569
570 sk_sp<SkColorSpace> cs;
571 if (rand->nextBool()) {
572 cs = SkColorSpace::MakeSRGBLinear();
573 }
574
575 return SkImages::TextureFromYUVAPixmaps(recorder,
576 pixmaps,
577 {/* fMipmapped= */ false},
578 /* limitToMaxTextureSize= */ false,
579 std::move(cs));
580 }
581
582 //--------------------------------------------------------------------------------------------------
make_picture(SkRandom * rand)583 sk_sp<SkPicture> make_picture(SkRandom* rand) {
584 constexpr SkRect kRect = SkRect::MakeWH(128, 128);
585
586 SkPictureRecorder recorder;
587
588 SkCanvas* canvas = recorder.beginRecording(kRect);
589
590 SkPaint paint; // Explicitly using the default SkPaint here
591
592 canvas->drawRect(kRect, paint);
593
594 return recorder.finishRecordingAsPicture();
595 }
596
597 //--------------------------------------------------------------------------------------------------
create_coord_clamp_shader(SkRandom * rand,Recorder * recorder)598 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_coord_clamp_shader(SkRandom* rand,
599 Recorder* recorder) {
600 auto [s, o] = create_random_shader(rand, recorder);
601 SkASSERT(!s == !o);
602
603 if (!s) {
604 return { nullptr, nullptr };
605 }
606
607 constexpr SkRect kSubset{0, 0, 256, 256}; // this is somewhat arbitrary but we need some subset
608 sk_sp<SkShader> ccs = SkShaders::CoordClamp(std::move(s), kSubset);
609 sk_sp<PrecompileShader> cco = PrecompileShaders::CoordClamp({ std::move(o) });
610
611 return { ccs, cco };
612 }
613
614 #if 0
615 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_empty_shader(SkRandom* /* rand */) {
616 sk_sp<SkShader> s = SkShaders::Empty();
617 sk_sp<PrecompileShader> o = PrecompileShaders::Empty();
618
619 return { s, o };
620 }
621 #endif
622
create_perlin_noise_shader(SkRandom * rand)623 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_perlin_noise_shader(SkRandom* rand) {
624 sk_sp<SkShader> s;
625 sk_sp<PrecompileShader> o;
626
627 if (rand->nextBool()) {
628 s = SkShaders::MakeFractalNoise(/* baseFrequencyX= */ 0.3f,
629 /* baseFrequencyY= */ 0.3f,
630 /* numOctaves= */ 2,
631 /* seed= */ 4);
632 o = PrecompileShaders::MakeFractalNoise();
633 } else {
634 s = SkShaders::MakeTurbulence(/* baseFrequencyX= */ 0.3f,
635 /* baseFrequencyY= */ 0.3f,
636 /* numOctaves= */ 2,
637 /* seed= */ 4);
638 o = PrecompileShaders::MakeTurbulence();
639 }
640
641 return { s, o };
642 }
643
create_picture_shader(SkRandom * rand)644 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_picture_shader(SkRandom* rand) {
645 sk_sp<SkPicture> picture = make_picture(rand);
646
647 gNeedSKPPaintOption = true;
648
649 SkMatrix lmStorage;
650 SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage);
651
652 // TODO: can the clamp, filter mode, or tileRect affect the final program?
653 sk_sp<SkShader> s = picture->makeShader(SkTileMode::kClamp,
654 SkTileMode::kClamp,
655 SkFilterMode::kLinear,
656 lmPtr,
657 /* tileRect= */ nullptr);
658 sk_sp<PrecompileShader> o = PrecompileShadersPriv::Picture(SkToBool(lmPtr));
659
660 return { s, o };
661 }
662
create_runtime_shader(SkRandom *)663 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_runtime_shader(SkRandom* /* rand */) {
664 static SkRuntimeEffect* sEffect = SkMakeRuntimeEffect(
665 SkRuntimeEffect::MakeForShader,
666 // draw a circle centered at "center" w/ inner and outer radii in "radii"
667 "uniform float2 center;"
668 "uniform float2 radii;"
669 "half4 main(float2 xy) {"
670 "float len = length(xy - center);"
671 "half value = len < radii.x ? 0.0 : (len > radii.y ? 0.0 : 1.0);"
672 "return half4(value);"
673 "}"
674 );
675
676 static const float kUniforms[4] = { 50.0f, 50.0f, 40.0f, 50.0f };
677
678 sk_sp<SkData> uniforms = SkData::MakeWithCopy(kUniforms, sizeof(kUniforms));
679
680 sk_sp<SkShader> s = sEffect->makeShader(std::move(uniforms), /* children= */ {});
681 sk_sp<PrecompileShader> o = PrecompileRuntimeEffects::MakePrecompileShader(sk_ref_sp(sEffect));
682 return { std::move(s), std::move(o) };
683 }
684
create_solid_shader(SkRandom * rand,ColorConstraint constraint=ColorConstraint::kNone)685 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_solid_shader(
686 SkRandom* rand,
687 ColorConstraint constraint = ColorConstraint::kNone) {
688 sk_sp<SkShader> s;
689 sk_sp<PrecompileShader> o;
690
691 if (rand->nextBool()) {
692 s = SkShaders::Color(random_color(rand, constraint));
693 o = PrecompileShaders::Color();
694 } else {
695 sk_sp<SkColorSpace> cs = random_colorspace(rand);
696 s = SkShaders::Color(random_color4f(rand, constraint), cs);
697 o = PrecompileShaders::Color(std::move(cs));
698 }
699
700 return { s, o };
701 }
702
create_gradient_shader(SkRandom * rand,SkShaderBase::GradientType type,ColorConstraint constraint=ColorConstraint::kOpaque)703 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_gradient_shader(
704 SkRandom* rand,
705 SkShaderBase::GradientType type,
706 ColorConstraint constraint = ColorConstraint::kOpaque) {
707 // TODO: fuzz more of the gradient parameters
708
709 static constexpr int kMaxNumStops = 9;
710 SkColor colors[kMaxNumStops] = {
711 random_color(rand, constraint),
712 random_color(rand, constraint),
713 random_color(rand, constraint),
714 random_color(rand, constraint),
715 random_color(rand, constraint),
716 random_color(rand, constraint),
717 random_color(rand, constraint),
718 random_color(rand, constraint),
719 random_color(rand, constraint)
720 };
721 static const SkPoint kPts[kMaxNumStops] = {
722 { -100.0f, -100.0f },
723 { -50.0f, -50.0f },
724 { -25.0f, -25.0f },
725 { -12.5f, -12.5f },
726 { 0.0f, 0.0f },
727 { 12.5f, 12.5f },
728 { 25.0f, 25.0f },
729 { 50.0f, 50.0f },
730 { 100.0f, 100.0f }
731 };
732 static const float kOffsets[kMaxNumStops] =
733 { 0.0f, 0.125f, 0.25f, 0.375f, 0.5f, 0.625f, 0.75f, 0.875f, 1.0f };
734
735 int numStops;
736
737 switch (rand->nextULessThan(3)) {
738 case 0: numStops = 2; break;
739 case 1: numStops = 7; break;
740 case 2: [[fallthrough]];
741 default: numStops = kMaxNumStops; break;
742 }
743
744 SkMatrix lmStorage;
745 SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage);
746
747 uint32_t flags = rand->nextBool() ? 0x0 : SkGradientShader::kInterpolateColorsInPremul_Flag;
748
749 sk_sp<SkShader> s;
750 sk_sp<PrecompileShader> o;
751
752 SkTileMode tm = random_tilemode(rand);
753
754 switch (type) {
755 case SkShaderBase::GradientType::kLinear:
756 s = SkGradientShader::MakeLinear(kPts,
757 colors, kOffsets, numStops, tm, flags, lmPtr);
758 o = PrecompileShaders::LinearGradient();
759 break;
760 case SkShaderBase::GradientType::kRadial:
761 s = SkGradientShader::MakeRadial(/* center= */ {0, 0}, /* radius= */ 100,
762 colors, kOffsets, numStops, tm, flags, lmPtr);
763 o = PrecompileShaders::RadialGradient();
764 break;
765 case SkShaderBase::GradientType::kSweep:
766 s = SkGradientShader::MakeSweep(/* cx= */ 0, /* cy= */ 0,
767 colors, kOffsets, numStops, tm,
768 /* startAngle= */ 0, /* endAngle= */ 359,
769 flags, lmPtr);
770 o = PrecompileShaders::SweepGradient();
771 break;
772 case SkShaderBase::GradientType::kConical:
773 s = SkGradientShader::MakeTwoPointConical(/* start= */ {100, 100},
774 /* startRadius= */ 100,
775 /* end= */ {-100, -100},
776 /* endRadius= */ 100,
777 colors, kOffsets, numStops, tm, flags, lmPtr);
778 o = PrecompileShaders::TwoPointConicalGradient();
779 break;
780 case SkShaderBase::GradientType::kNone:
781 SkDEBUGFAIL("Gradient shader says its type is none");
782 break;
783 }
784
785 return { s, o };
786 }
787
create_localmatrix_shader(SkRandom * rand,Recorder * recorder)788 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_localmatrix_shader(SkRandom* rand,
789 Recorder* recorder) {
790 auto [s, o] = create_random_shader(rand, recorder);
791 SkASSERT(!s == !o);
792
793 if (!s) {
794 return { nullptr, nullptr };
795 }
796
797 bool hasPerspective = rand->nextBool();
798
799 SkMatrix lmStorage;
800 random_local_matrix(rand, &lmStorage, hasPerspective ? LocalMatrixConstraint::kWithPerspective
801 : LocalMatrixConstraint::kNone);
802
803 return { s->makeWithLocalMatrix(lmStorage), o->makeWithLocalMatrix(hasPerspective) };
804 }
805
create_colorfilter_shader(SkRandom * rand,Recorder * recorder)806 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_colorfilter_shader(SkRandom* rand,
807 Recorder* recorder) {
808 auto [s, o] = create_random_shader(rand, recorder);
809 SkASSERT(!s == !o);
810
811 if (!s) {
812 return { nullptr, nullptr };
813 }
814
815 auto [cf, cfO] = create_random_colorfilter(rand);
816
817 return { s->makeWithColorFilter(std::move(cf)), o->makeWithColorFilter(std::move(cfO)) };
818 }
819
820 // TODO: With the new explicit PrecompileImageFilter API we need to test out complete DAGS of IFs
create_image_shader(SkRandom * rand,Recorder * recorder)821 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_image_shader(SkRandom* rand,
822 Recorder* recorder) {
823 SkTileMode tmX = random_tilemode(rand);
824 SkTileMode tmY = random_tilemode(rand);
825
826 SkMatrix lmStorage;
827 SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage);
828
829 sk_sp<SkShader> s;
830 sk_sp<PrecompileShader> o;
831
832 // TODO: the combination system accounts for cubic vs. non-cubic sampling and HW vs. non-HW
833 // tiling. We should test those combinations in the fuzzer.
834 if (rand->nextBool()) {
835 s = SkShaders::Image(make_image(rand, recorder),
836 tmX, tmY,
837 SkSamplingOptions(),
838 lmPtr);
839 o = PrecompileShaders::Image();
840 } else {
841 s = SkShaders::RawImage(make_image(rand, recorder),
842 tmX, tmY,
843 SkSamplingOptions(),
844 lmPtr);
845 o = PrecompileShaders::RawImage();
846 }
847
848 return { s, o };
849 }
850
create_yuv_image_shader(SkRandom * rand,Recorder * recorder)851 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_yuv_image_shader(SkRandom* rand,
852 Recorder* recorder) {
853 SkTileMode tmX = random_tilemode(rand);
854 SkTileMode tmY = random_tilemode(rand);
855
856 SkMatrix lmStorage;
857 SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage);
858
859 sk_sp<SkShader> s;
860 sk_sp<PrecompileShader> o;
861
862 SkSamplingOptions samplingOptions(SkFilterMode::kLinear);
863 if (rand->nextBool()) {
864 samplingOptions = SkCubicResampler::Mitchell();
865 }
866
867 sk_sp<SkImage> yuvImage = make_yuv_image(rand, recorder);
868 if (rand->nextBool()) {
869 s = SkImageShader::MakeSubset(std::move(yuvImage), SkRect::MakeXYWH(8, 8, 16, 16),
870 tmX, tmY, samplingOptions, lmPtr);
871 } else {
872 s = SkShaders::Image(std::move(yuvImage), tmX, tmY, samplingOptions, lmPtr);
873 }
874
875 o = PrecompileShaders::YUVImage();
876
877 return { s, o };
878 }
879
create_blend_shader(SkRandom * rand,Recorder * recorder)880 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_blend_shader(SkRandom* rand,
881 Recorder* recorder) {
882 // TODO: add explicit testing of the kClear, kDst and kSrc blend modes since they short
883 // circuit creation of a true blend shader (i.e., in SkShaders::Blend).
884 auto [blender, blenderO] = create_random_blender(rand);
885
886 auto [dstS, dstO] = create_random_shader(rand, recorder);
887 SkASSERT(!dstS == !dstO);
888 if (!dstS) {
889 return { nullptr, nullptr };
890 }
891
892 auto [srcS, srcO] = create_random_shader(rand, recorder);
893 SkASSERT(!srcS == !srcO);
894 if (!srcS) {
895 return { nullptr, nullptr };
896 }
897
898 auto s = SkShaders::Blend(std::move(blender), std::move(dstS), std::move(srcS));
899 auto o = PrecompileShaders::Blend(SkSpan<const sk_sp<PrecompileBlender>>({ blenderO }),
900 { dstO }, { srcO });
901
902 return { s, o };
903 }
904
create_workingCS_shader(SkRandom * rand,Recorder * recorder)905 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_workingCS_shader(SkRandom* rand,
906 Recorder* recorder) {
907 auto [wrappedS, wrappedO] = create_random_shader(rand, recorder);
908 SkASSERT(!wrappedS == !wrappedO);
909 if (!wrappedS) {
910 return { nullptr, nullptr };
911 }
912
913 sk_sp<SkColorSpace> cs = random_colorspace(rand);
914 sk_sp<SkShader> s = wrappedS->makeWithWorkingColorSpace(cs);
915 sk_sp<PrecompileShader> o = wrappedO->makeWithWorkingColorSpace(std::move(cs));
916
917 return { s, o };
918 }
919
create_shader(SkRandom * rand,Recorder * recorder,ShaderType shaderType)920 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_shader(SkRandom* rand,
921 Recorder* recorder,
922 ShaderType shaderType) {
923
924 switch (shaderType) {
925 case ShaderType::kNone:
926 return { nullptr, nullptr };
927 case ShaderType::kBlend:
928 return create_blend_shader(rand, recorder);
929 case ShaderType::kColorFilter:
930 return create_colorfilter_shader(rand, recorder);
931 case ShaderType::kCoordClamp:
932 return create_coord_clamp_shader(rand, recorder);
933 case ShaderType::kConicalGradient:
934 return create_gradient_shader(rand, SkShaderBase::GradientType::kConical);
935 // case ShaderType::kEmpty:
936 // return create_empty_shader(rand);
937 case ShaderType::kImage:
938 return create_image_shader(rand, recorder);
939 case ShaderType::kLinearGradient:
940 return create_gradient_shader(rand, SkShaderBase::GradientType::kLinear);
941 case ShaderType::kLocalMatrix:
942 return create_localmatrix_shader(rand, recorder);
943 case ShaderType::kPerlinNoise:
944 return create_perlin_noise_shader(rand);
945 case ShaderType::kPicture:
946 return create_picture_shader(rand);
947 case ShaderType::kRadialGradient:
948 return create_gradient_shader(rand, SkShaderBase::GradientType::kRadial);
949 case ShaderType::kRuntime:
950 return create_runtime_shader(rand);
951 case ShaderType::kSolidColor:
952 return create_solid_shader(rand);
953 case ShaderType::kSweepGradient:
954 return create_gradient_shader(rand, SkShaderBase::GradientType::kSweep);
955 case ShaderType::kYUVImage:
956 return create_yuv_image_shader(rand, recorder);
957 case ShaderType::kWorkingColorSpace:
958 return create_workingCS_shader(rand, recorder);
959 }
960
961 SkUNREACHABLE;
962 }
963
create_random_shader(SkRandom * rand,Recorder * recorder)964 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_random_shader(SkRandom* rand,
965 Recorder* recorder) {
966 return create_shader(rand, recorder, random_shadertype(rand));
967 }
968
create_clip_shader(SkRandom * rand,Recorder * recorder)969 std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_clip_shader(SkRandom* rand,
970 Recorder* recorder) {
971 // The clip shader has to be transparent to be at all interesting.
972 // TODO/Note: an opaque clipShader is eliminated from the SkPaint by the normal Skia API
973 // but I'm unsure if we should bother capturing that possibility in the precompile system.
974 switch (rand->nextULessThan(5)) {
975 case 0: return create_gradient_shader(rand, SkShaderBase::GradientType::kConical,
976 ColorConstraint::kTransparent);
977 case 1: return create_gradient_shader(rand, SkShaderBase::GradientType::kLinear,
978 ColorConstraint::kTransparent);
979 case 2: return create_gradient_shader(rand, SkShaderBase::GradientType::kRadial,
980 ColorConstraint::kTransparent);
981 case 3: return create_solid_shader(rand, ColorConstraint::kTransparent);
982 case 4: return create_gradient_shader(rand, SkShaderBase::GradientType::kSweep,
983 ColorConstraint::kTransparent);
984 }
985
986 SkUNREACHABLE;
987 }
988
989 //--------------------------------------------------------------------------------------------------
src_blender()990 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> src_blender() {
991 static SkRuntimeEffect* sSrcEffect = SkMakeRuntimeEffect(
992 SkRuntimeEffect::MakeForBlender,
993 "half4 main(half4 src, half4 dst) {"
994 "return src;"
995 "}"
996 );
997
998 sk_sp<SkBlender> b = sSrcEffect->makeBlender(/* uniforms= */ nullptr);
999 sk_sp<PrecompileBlender> o =
1000 PrecompileRuntimeEffects::MakePrecompileBlender(sk_ref_sp(sSrcEffect));
1001 return { std::move(b) , std::move(o) };
1002 }
1003
dest_blender()1004 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> dest_blender() {
1005 static SkRuntimeEffect* sDestEffect = SkMakeRuntimeEffect(
1006 SkRuntimeEffect::MakeForBlender,
1007 "half4 main(half4 src, half4 dst) {"
1008 "return dst;"
1009 "}"
1010 );
1011
1012 sk_sp<SkBlender> b = sDestEffect->makeBlender(/* uniforms= */ nullptr);
1013 sk_sp<PrecompileBlender> o =
1014 PrecompileRuntimeEffects::MakePrecompileBlender(sk_ref_sp(sDestEffect));
1015 return { std::move(b) , std::move(o) };
1016 }
1017
1018
combo_blender()1019 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> combo_blender() {
1020 static SkRuntimeEffect* sComboEffect = SkMakeRuntimeEffect(
1021 SkRuntimeEffect::MakeForBlender,
1022 "uniform float blendFrac;"
1023 "uniform blender a;"
1024 "uniform blender b;"
1025 "half4 main(half4 src, half4 dst) {"
1026 "return (blendFrac * a.eval(src, dst)) + ((1 - blendFrac) * b.eval(src, dst));"
1027 "}"
1028 );
1029
1030 auto [src, srcO] = src_blender();
1031 auto [dst, dstO] = dest_blender();
1032
1033 SkRuntimeEffect::ChildPtr children[] = { src, dst };
1034
1035 const float kUniforms[] = { 1.0f };
1036
1037 sk_sp<SkData> uniforms = SkData::MakeWithCopy(kUniforms, sizeof(kUniforms));
1038 sk_sp<SkBlender> b = sComboEffect->makeBlender(std::move(uniforms), children);
1039 sk_sp<PrecompileBlender> o = PrecompileRuntimeEffects::MakePrecompileBlender(
1040 sk_ref_sp(sComboEffect),
1041 { { srcO }, { dstO } });
1042 return { std::move(b) , std::move(o) };
1043 }
1044
create_bm_blender(SkRandom * rand,SkBlendMode bm)1045 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_bm_blender(SkRandom* rand,
1046 SkBlendMode bm) {
1047 return { SkBlender::Mode(bm), PrecompileBlenders::Mode(bm) };
1048 }
1049
create_arithmetic_blender()1050 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_arithmetic_blender() {
1051 sk_sp<SkBlender> b = SkBlenders::Arithmetic(/* k1= */ 0.5,
1052 /* k2= */ 0.5,
1053 /* k3= */ 0.5,
1054 /* k4= */ 0.5,
1055 /* enforcePremul= */ true);
1056 sk_sp<PrecompileBlender> o = PrecompileBlenders::Arithmetic();
1057
1058 return { std::move(b), std::move(o) };
1059 }
1060
create_rt_blender(SkRandom * rand)1061 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_rt_blender(SkRandom* rand) {
1062 int option = rand->nextULessThan(3);
1063
1064 switch (option) {
1065 case 0: return src_blender();
1066 case 1: return dest_blender();
1067 case 2: return combo_blender();
1068 }
1069
1070 return { nullptr, nullptr };
1071 }
1072
create_blender(SkRandom * rand,BlenderType type)1073 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_blender(SkRandom* rand,
1074 BlenderType type) {
1075 switch (type) {
1076 case BlenderType::kNone:
1077 return { nullptr, nullptr };
1078 case BlenderType::kPorterDuff:
1079 return create_bm_blender(rand, random_porter_duff_bm(rand));
1080 case BlenderType::kShaderBased:
1081 return create_bm_blender(rand, random_complex_bm(rand));
1082 case BlenderType::kArithmetic:
1083 return create_arithmetic_blender();
1084 case BlenderType::kRuntime:
1085 return create_rt_blender(rand);
1086 }
1087
1088 SkUNREACHABLE;
1089 }
1090
create_random_blender(SkRandom * rand)1091 std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_random_blender(SkRandom* rand) {
1092 return create_blender(rand, random_blendertype(rand));
1093 }
1094
1095 //--------------------------------------------------------------------------------------------------
1096 //--------------------------------------------------------------------------------------------------
double_colorfilter()1097 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> double_colorfilter() {
1098 static SkRuntimeEffect* sSrcEffect = SkMakeRuntimeEffect(
1099 SkRuntimeEffect::MakeForColorFilter,
1100 "half4 main(half4 c) {"
1101 "return 2*c;"
1102 "}"
1103 );
1104
1105 return { sSrcEffect->makeColorFilter(/* uniforms= */ nullptr),
1106 PrecompileRuntimeEffects::MakePrecompileColorFilter(sk_ref_sp(sSrcEffect)) };
1107 }
1108
half_colorfilter()1109 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> half_colorfilter() {
1110 static SkRuntimeEffect* sDestEffect = SkMakeRuntimeEffect(
1111 SkRuntimeEffect::MakeForColorFilter,
1112 "half4 main(half4 c) {"
1113 "return 0.5*c;"
1114 "}"
1115 );
1116
1117 return { sDestEffect->makeColorFilter(/* uniforms= */ nullptr),
1118 PrecompileRuntimeEffects::MakePrecompileColorFilter(sk_ref_sp(sDestEffect)) };
1119 }
1120
combo_colorfilter()1121 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> combo_colorfilter() {
1122 static SkRuntimeEffect* sComboEffect = SkMakeRuntimeEffect(
1123 SkRuntimeEffect::MakeForColorFilter,
1124 "uniform float blendFrac;"
1125 "uniform colorFilter a;"
1126 "uniform colorFilter b;"
1127 "half4 main(half4 c) {"
1128 "return (blendFrac * a.eval(c)) + ((1 - blendFrac) * b.eval(c));"
1129 "}"
1130 );
1131
1132 auto [src, srcO] = double_colorfilter();
1133 auto [dst, dstO] = half_colorfilter();
1134
1135 SkRuntimeEffect::ChildPtr children[] = { src, dst };
1136
1137 const float kUniforms[] = { 0.5f };
1138
1139 sk_sp<SkData> uniforms = SkData::MakeWithCopy(kUniforms, sizeof(kUniforms));
1140 sk_sp<SkColorFilter> cf = sComboEffect->makeColorFilter(std::move(uniforms), children);
1141 sk_sp<PrecompileColorFilter> o =
1142 PrecompileRuntimeEffects::MakePrecompileColorFilter(sk_ref_sp(sComboEffect),
1143 { { srcO }, { dstO } });
1144 return { std::move(cf) , std::move(o) };
1145 }
1146
create_rt_colorfilter(SkRandom * rand)1147 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_rt_colorfilter(
1148 SkRandom* rand) {
1149 int option = rand->nextULessThan(3);
1150
1151 switch (option) {
1152 case 0: return double_colorfilter();
1153 case 1: return half_colorfilter();
1154 case 2: return combo_colorfilter();
1155 }
1156
1157 return { nullptr, nullptr };
1158 }
1159
create_lerp_colorfilter(SkRandom * rand)1160 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_lerp_colorfilter(
1161 SkRandom* rand) {
1162
1163 auto [dst, dstO] = create_random_colorfilter(rand);
1164 auto [src, srcO] = create_random_colorfilter(rand);
1165 // SkColorFilters::Lerp optimizes away the case where src == dst. I don't know if it is worth
1166 // capturing it in the precompilation API
1167 while (src == dst) {
1168 std::tie(src, srcO) = create_random_colorfilter(rand);
1169 }
1170
1171 // TODO: SkColorFilters::Lerp will return a different colorFilter depending on the
1172 // weight value and the child color filters. I don't know if that is worth capturing
1173 // in the precompile API.
1174 sk_sp<SkColorFilter> cf = SkColorFilters::Lerp(0.5f, std::move(dst), std::move(src));
1175
1176 sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Lerp({ dstO }, { srcO });
1177
1178 return { cf, o };
1179 }
1180
create_lighting_colorfilter()1181 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_lighting_colorfilter() {
1182 // TODO: the lighting color filter factory special cases when nothing is added and converts it
1183 // to a blendmode color filter
1184 return { SkColorFilters::Lighting(SK_ColorGREEN, SK_ColorRED),
1185 PrecompileColorFilters::Lighting() };
1186 }
1187
create_blendmode_colorfilter(SkRandom * rand)1188 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_blendmode_colorfilter(
1189 SkRandom* rand) {
1190
1191 sk_sp<SkColorFilter> cf;
1192
1193 // SkColorFilters::Blend is clever and can weed out noop color filters. Loop until we get
1194 // a valid color filter.
1195 SkBlendMode blend;
1196 while (!cf) {
1197 blend = random_blend_mode(rand);
1198 cf = SkColorFilters::Blend(random_color4f(rand, ColorConstraint::kNone),
1199 random_colorspace(rand),
1200 blend);
1201 }
1202
1203 sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Blend({&blend, 1});
1204
1205 return { std::move(cf), std::move(o) };
1206 }
1207
create_matrix_colorfilter()1208 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_matrix_colorfilter() {
1209 sk_sp<SkColorFilter> cf = SkColorFilters::Matrix(
1210 SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace));
1211 sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Matrix();
1212
1213 return { std::move(cf), std::move(o) };
1214 }
1215
create_color_space_colorfilter(SkRandom * rand)1216 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_color_space_colorfilter(
1217 SkRandom* rand) {
1218 return { SkColorFilterPriv::MakeColorSpaceXform(random_colorspace(rand),
1219 random_colorspace(rand)),
1220 PrecompileColorFiltersPriv::ColorSpaceXform() };
1221 }
1222
create_linear_to_srgb_colorfilter()1223 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_linear_to_srgb_colorfilter() {
1224 return { SkColorFilters::LinearToSRGBGamma(), PrecompileColorFilters::LinearToSRGBGamma() };
1225 }
1226
create_srgb_to_linear_colorfilter()1227 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_srgb_to_linear_colorfilter() {
1228 return { SkColorFilters::SRGBToLinearGamma(), PrecompileColorFilters::SRGBToLinearGamma() };
1229 }
1230
create_high_contrast_colorfilter()1231 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_high_contrast_colorfilter() {
1232 SkHighContrastConfig config(/* grayscale= */ false,
1233 SkHighContrastConfig::InvertStyle::kInvertBrightness,
1234 /* contrast= */ 0.5f);
1235 return { SkHighContrastFilter::Make(config), PrecompileColorFilters::HighContrast() };
1236 }
1237
create_luma_colorfilter()1238 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_luma_colorfilter() {
1239 return { SkLumaColorFilter::Make(), PrecompileColorFilters::Luma() };
1240 }
1241
create_overdraw_colorfilter()1242 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_overdraw_colorfilter() {
1243 // Black to red heat map gradation
1244 static const SkColor kColors[SkOverdrawColorFilter::kNumColors] = {
1245 SK_ColorBLACK,
1246 SK_ColorBLUE,
1247 SK_ColorCYAN,
1248 SK_ColorGREEN,
1249 SK_ColorYELLOW,
1250 SK_ColorRED
1251 };
1252
1253 return { SkOverdrawColorFilter::MakeWithSkColors(kColors), PrecompileColorFilters::Overdraw() };
1254 }
1255
create_compose_colorfilter(SkRandom * rand)1256 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_compose_colorfilter(
1257 SkRandom* rand) {
1258 auto [outerCF, outerO] = create_random_colorfilter(rand);
1259 auto [innerCF, innerO] = create_random_colorfilter(rand);
1260
1261 // TODO: if outerCF is null, innerCF will be returned by Compose. We need a Precompile
1262 // list object that can encapsulate innerO if there are no combinations in outerO.
1263 return { SkColorFilters::Compose(std::move(outerCF), std::move(innerCF)),
1264 PrecompileColorFilters::Compose({ std::move(outerO) }, { std::move(innerO) }) };
1265 }
1266
create_gaussian_colorfilter()1267 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_gaussian_colorfilter() {
1268 return { SkColorFilterPriv::MakeGaussian(), PrecompileColorFiltersPriv::Gaussian() };
1269 }
1270
create_table_colorfilter()1271 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_table_colorfilter() {
1272 static constexpr uint8_t kTable[256] = { 0 };
1273
1274 return { SkColorFilters::Table(kTable), PrecompileColorFilters::Table() };
1275 }
1276
create_workingformat_colorfilter(SkRandom * rand)1277 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_workingformat_colorfilter(
1278 SkRandom* rand) {
1279 auto [childCF, childO] = create_random_colorfilter(rand);
1280
1281 if (!childCF) {
1282 return { nullptr, nullptr };
1283 }
1284
1285 SkASSERT(childCF && childO);
1286
1287 SkAlphaType unpremul = kUnpremul_SkAlphaType;
1288 sk_sp<SkColorFilter> cf = SkColorFilterPriv::WithWorkingFormat(std::move(childCF),
1289 &random_xfer_function(rand),
1290 &random_gamut(rand),
1291 &unpremul);
1292
1293 sk_sp<PrecompileColorFilter> o = PrecompileColorFiltersPriv::WithWorkingFormat(
1294 { std::move(childO) });
1295
1296 return { std::move(cf), std::move(o) };
1297 }
1298
create_hsla_matrix_colorfilter()1299 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_hsla_matrix_colorfilter() {
1300 sk_sp<SkColorFilter> cf = SkColorFilters::HSLAMatrix(
1301 SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace));
1302 sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::HSLAMatrix();
1303
1304 return { std::move(cf), std::move(o) };
1305 }
1306
create_colorfilter(SkRandom * rand,ColorFilterType type)1307 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_colorfilter(
1308 SkRandom* rand,
1309 ColorFilterType type) {
1310
1311 switch (type) {
1312 case ColorFilterType::kNone:
1313 return { nullptr, nullptr };
1314 case ColorFilterType::kBlendMode:
1315 return create_blendmode_colorfilter(rand);
1316 case ColorFilterType::kColorSpaceXform:
1317 return create_color_space_colorfilter(rand);
1318 case ColorFilterType::kCompose:
1319 return create_compose_colorfilter(rand);
1320 case ColorFilterType::kGaussian:
1321 return create_gaussian_colorfilter();
1322 case ColorFilterType::kHighContrast:
1323 return create_high_contrast_colorfilter();
1324 case ColorFilterType::kHSLAMatrix:
1325 return create_hsla_matrix_colorfilter();
1326 case ColorFilterType::kLerp:
1327 return create_lerp_colorfilter(rand);
1328 case ColorFilterType::kLighting:
1329 return create_lighting_colorfilter();
1330 case ColorFilterType::kLinearToSRGB:
1331 return create_linear_to_srgb_colorfilter();
1332 case ColorFilterType::kLuma:
1333 return create_luma_colorfilter();
1334 case ColorFilterType::kMatrix:
1335 return create_matrix_colorfilter();
1336 case ColorFilterType::kOverdraw:
1337 return create_overdraw_colorfilter();
1338 case ColorFilterType::kRuntime:
1339 return create_rt_colorfilter(rand);
1340 case ColorFilterType::kSRGBToLinear:
1341 return create_srgb_to_linear_colorfilter();
1342 case ColorFilterType::kTable:
1343 return create_table_colorfilter();
1344 case ColorFilterType::kWorkingFormat:
1345 return create_workingformat_colorfilter(rand);
1346 }
1347
1348 SkUNREACHABLE;
1349 }
1350
create_random_colorfilter(SkRandom * rand)1351 std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_random_colorfilter(
1352 SkRandom* rand) {
1353 return create_colorfilter(rand, random_colorfiltertype(rand));
1354 }
1355
1356 //--------------------------------------------------------------------------------------------------
1357 //--------------------------------------------------------------------------------------------------
arithmetic_imagefilter(SkRandom *)1358 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> arithmetic_imagefilter(
1359 SkRandom* /* rand */) {
1360
1361 sk_sp<SkImageFilter> arithmeticIF = SkImageFilters::Arithmetic(/* k1= */ 0.5f,
1362 /* k2= */ 0.5f,
1363 /* k3= */ 0.5f,
1364 /* k4= */ 0.5f,
1365 /* enforcePMColor= */ false,
1366 /* background= */ nullptr,
1367 /* foreground= */ nullptr);
1368 sk_sp<PrecompileImageFilter> option = PrecompileImageFilters::Arithmetic(
1369 /* background= */ nullptr,
1370 /* foreground= */ nullptr);
1371
1372 return { std::move(arithmeticIF), std::move(option) };
1373 }
1374
blendmode_imagefilter(SkRandom * rand)1375 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> blendmode_imagefilter(
1376 SkRandom* rand) {
1377
1378 SkBlendMode bm = random_blend_mode(rand);
1379 sk_sp<SkImageFilter> blendIF = SkImageFilters::Blend(bm,
1380 /* background= */ nullptr,
1381 /* foreground= */ nullptr);
1382 sk_sp<PrecompileImageFilter> blendO = PrecompileImageFilters::Blend(
1383 bm,
1384 /* background= */ nullptr,
1385 /* foreground= */ nullptr);
1386
1387 return { std::move(blendIF), std::move(blendO) };
1388 }
1389
runtime_blender_imagefilter(SkRandom * rand)1390 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> runtime_blender_imagefilter(
1391 SkRandom* rand) {
1392
1393 auto [blender, blenderO] = create_blender(rand, BlenderType::kRuntime);
1394 sk_sp<SkImageFilter> blenderIF = SkImageFilters::Blend(std::move(blender),
1395 /* background= */ nullptr,
1396 /* foreground= */ nullptr);
1397 sk_sp<PrecompileImageFilter> option = PrecompileImageFilters::Blend(std::move(blenderO),
1398 /* background= */ nullptr,
1399 /* foreground= */ nullptr);
1400
1401 return { std::move(blenderIF), std::move(option) };
1402 }
1403
blur_imagefilter(SkRandom * rand)1404 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> blur_imagefilter(
1405 SkRandom* rand) {
1406
1407 int option = rand->nextULessThan(3);
1408
1409 float sigma;
1410 switch (option) {
1411 case 0: sigma = 1.0f; break; // 1DBlur4
1412 case 1: sigma = 2.0f; break; // 1DBlur8
1413 case 2: [[fallthrough]];
1414 default: sigma = 5.0f; break; // 1DBlur16
1415 }
1416
1417 sk_sp<SkImageFilter> blurIF = SkImageFilters::Blur(sigma, sigma, /* input= */ nullptr);
1418 sk_sp<PrecompileImageFilter> blurO = PrecompileImageFilters::Blur(/* input= */ nullptr);
1419
1420 return { std::move(blurIF), std::move(blurO) };
1421 }
1422
displacement_imagefilter(Recorder * recorder,SkRandom * rand)1423 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> displacement_imagefilter(
1424 Recorder* recorder,
1425 SkRandom* rand) {
1426
1427 sk_sp<SkImage> checkerboard = ToolUtils::create_checkerboard_image(16, 16,
1428 SK_ColorWHITE,
1429 SK_ColorBLACK,
1430 /* checkSize= */ 4);
1431 checkerboard = SkImages::TextureFromImage(recorder, std::move(checkerboard), {false});
1432 SkASSERT(checkerboard);
1433
1434 sk_sp<SkImageFilter> imageIF(SkImageFilters::Image(std::move(checkerboard),
1435 SkFilterMode::kLinear));
1436
1437 sk_sp<SkImageFilter> displacementIF;
1438
1439 displacementIF = SkImageFilters::DisplacementMap(SkColorChannel::kR,
1440 SkColorChannel::kB,
1441 /* scale= */ 2.0f,
1442 /* displacement= */ std::move(imageIF),
1443 /* color= */ nullptr);
1444 sk_sp<PrecompileImageFilter> option =
1445 PrecompileImageFilters::DisplacementMap(/* input= */ nullptr);
1446
1447 return { std::move(displacementIF), std::move(option) };
1448 }
1449
colorfilter_imagefilter(SkRandom * rand)1450 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> colorfilter_imagefilter(
1451 SkRandom* rand) {
1452
1453 auto [cf, o] = create_random_colorfilter(rand);
1454
1455 sk_sp<SkImageFilter> inputIF;
1456 sk_sp<PrecompileImageFilter> inputO;
1457 if (rand->nextBool()) {
1458 // Exercise color filter collapsing in the factories
1459 auto [cf2, o2] = create_random_colorfilter(rand);
1460 inputIF = SkImageFilters::ColorFilter(std::move(cf2), /* input= */ nullptr);
1461 inputO = PrecompileImageFilters::ColorFilter(std::move(o2), /* input= */ nullptr);
1462 }
1463
1464 sk_sp<SkImageFilter> cfIF = SkImageFilters::ColorFilter(std::move(cf), std::move(inputIF));
1465 sk_sp<PrecompileImageFilter> cfIFO = PrecompileImageFilters::ColorFilter(std::move(o),
1466 std::move(inputO));
1467
1468 return { std::move(cfIF), std::move(cfIFO) };
1469 }
1470
lighting_imagefilter(SkRandom * rand)1471 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> lighting_imagefilter(
1472 SkRandom* rand) {
1473 static constexpr SkPoint3 kLocation{10.0f, 2.0f, 30.0f};
1474 static constexpr SkPoint3 kTarget{0, 0, 0};
1475 static constexpr SkPoint3 kDirection{0, 1, 0};
1476
1477 sk_sp<SkImageFilter> lightingIF;
1478
1479 int option = rand->nextULessThan(6);
1480 switch (option) {
1481 case 0:
1482 lightingIF = SkImageFilters::DistantLitDiffuse(kDirection, SK_ColorRED,
1483 /* surfaceScale= */ 1.0f,
1484 /* kd= */ 0.5f,
1485 /* input= */ nullptr);
1486 break;
1487 case 1:
1488 lightingIF = SkImageFilters::PointLitDiffuse(kLocation, SK_ColorGREEN,
1489 /* surfaceScale= */ 1.0f,
1490 /* kd= */ 0.5f,
1491 /* input= */ nullptr);
1492 break;
1493 case 2:
1494 lightingIF = SkImageFilters::SpotLitDiffuse(kLocation, kTarget,
1495 /* falloffExponent= */ 2.0f,
1496 /* cutoffAngle= */ 30.0f,
1497 SK_ColorBLUE,
1498 /* surfaceScale= */ 1.0f,
1499 /* kd= */ 0.5f,
1500 /* input= */ nullptr);
1501 break;
1502 case 3:
1503 lightingIF = SkImageFilters::DistantLitSpecular(kDirection, SK_ColorCYAN,
1504 /* surfaceScale= */ 1.0f,
1505 /* ks= */ 0.5f,
1506 /* shininess= */ 2.0f,
1507 /* input= */ nullptr);
1508 break;
1509 case 4:
1510 lightingIF = SkImageFilters::PointLitSpecular(kLocation, SK_ColorMAGENTA,
1511 /* surfaceScale= */ 1.0f,
1512 /* ks= */ 0.5f,
1513 /* shininess= */ 2.0f,
1514 /* input= */ nullptr);
1515 break;
1516 case 5:
1517 lightingIF = SkImageFilters::SpotLitSpecular(kLocation, kTarget,
1518 /* falloffExponent= */ 2.0f,
1519 /* cutoffAngle= */ 30.0f,
1520 SK_ColorYELLOW,
1521 /* surfaceScale= */ 1.0f,
1522 /* ks= */ 4.0f,
1523 /* shininess= */ 0.5f,
1524 /* input= */ nullptr);
1525 break;
1526 }
1527
1528 sk_sp<PrecompileImageFilter> lightingO = PrecompileImageFilters::Lighting(/* input= */ nullptr);
1529 return { std::move(lightingIF), std::move(lightingO) };
1530 }
1531
matrix_convolution_imagefilter(SkRandom * rand)1532 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> matrix_convolution_imagefilter(
1533 SkRandom* rand) {
1534
1535 int kernelSize = 1;
1536
1537 int option = rand->nextULessThan(3);
1538 switch (option) {
1539 case 0: kernelSize = 3; break;
1540 case 1: kernelSize = 7; break;
1541 case 2: kernelSize = 11; break;
1542 }
1543
1544 int center = (kernelSize * kernelSize - 1) / 2;
1545 std::vector<float> kernel(kernelSize * kernelSize, SkIntToScalar(1));
1546 kernel[center] = 2.0f - kernelSize * kernelSize;
1547
1548 sk_sp<SkImageFilter> matrixConvIF;
1549 matrixConvIF = SkImageFilters::MatrixConvolution({ kernelSize, kernelSize },
1550 /* kernel= */ kernel.data(),
1551 /* gain= */ 0.3f,
1552 /* bias= */ 100.0f,
1553 /* kernelOffset= */ { 1, 1 },
1554 SkTileMode::kMirror,
1555 /* convolveAlpha= */ false,
1556 /* input= */ nullptr);
1557 SkASSERT(matrixConvIF);
1558 sk_sp<PrecompileImageFilter> convOption = PrecompileImageFilters::MatrixConvolution(
1559 /* input= */ nullptr);
1560
1561 return { std::move(matrixConvIF), std::move(convOption) };
1562 }
1563
morphology_imagefilter(SkRandom * rand)1564 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> morphology_imagefilter(
1565 SkRandom* rand) {
1566 static constexpr float kRadX = 2.0f, kRadY = 4.0f;
1567
1568 sk_sp<SkImageFilter> morphologyIF;
1569
1570 if (rand->nextBool()) {
1571 morphologyIF = SkImageFilters::Erode(kRadX, kRadY, /* input= */ nullptr);
1572 } else {
1573 morphologyIF = SkImageFilters::Dilate(kRadX, kRadY, /* input= */ nullptr);
1574 }
1575 SkASSERT(morphologyIF);
1576 sk_sp<PrecompileImageFilter> option = PrecompileImageFilters::Morphology(/* input= */ nullptr);
1577
1578 return { std::move(morphologyIF), std::move(option) };
1579 }
1580
create_image_filter(Recorder * recorder,SkRandom * rand,ImageFilterType type)1581 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> create_image_filter(
1582 Recorder* recorder,
1583 SkRandom* rand,
1584 ImageFilterType type) {
1585
1586 switch (type) {
1587 case ImageFilterType::kNone:
1588 return {};
1589 case ImageFilterType::kArithmetic:
1590 return arithmetic_imagefilter(rand);
1591 case ImageFilterType::kBlendMode:
1592 return blendmode_imagefilter(rand);
1593 case ImageFilterType::kRuntimeBlender:
1594 return runtime_blender_imagefilter(rand);
1595 case ImageFilterType::kBlur:
1596 return blur_imagefilter(rand);
1597 case ImageFilterType::kColorFilter:
1598 return colorfilter_imagefilter(rand);
1599 case ImageFilterType::kDisplacement:
1600 return displacement_imagefilter(recorder, rand);
1601 case ImageFilterType::kLighting:
1602 return lighting_imagefilter(rand);
1603 case ImageFilterType::kMatrixConvolution:
1604 return matrix_convolution_imagefilter(rand);
1605 case ImageFilterType::kMorphology:
1606 return morphology_imagefilter(rand);
1607 }
1608
1609 SkUNREACHABLE;
1610 }
1611
create_random_image_filter(Recorder * recorder,SkRandom * rand)1612 std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> create_random_image_filter(
1613 Recorder* recorder,
1614 SkRandom* rand) {
1615 return create_image_filter(recorder, rand, random_imagefiltertype(rand));
1616 }
1617
create_blur_maskfilter(SkRandom * rand)1618 std::pair<sk_sp<SkMaskFilter>, sk_sp<PrecompileMaskFilter>> create_blur_maskfilter(SkRandom* rand) {
1619 SkBlurStyle style;
1620 switch (rand->nextULessThan(4)) {
1621 case 0: style = kNormal_SkBlurStyle; break;
1622 case 1: style = kSolid_SkBlurStyle; break;
1623 case 2: style = kOuter_SkBlurStyle; break;
1624 case 3: [[fallthrough]];
1625 default: style = kInner_SkBlurStyle; break;
1626 }
1627
1628 float sigma = 1.0f;
1629 switch (rand->nextULessThan(2)) {
1630 case 0: sigma = 1.0f; break;
1631 case 1: sigma = 2.0f; break;
1632 case 2: [[fallthrough]];
1633 default: sigma = 5.0f; break;
1634 }
1635
1636 bool respectCTM = rand->nextBool();
1637
1638 return { SkMaskFilter::MakeBlur(style, sigma, respectCTM), PrecompileMaskFilters::Blur() };
1639 }
1640
create_maskfilter(SkRandom * rand,MaskFilterType type)1641 std::pair<sk_sp<SkMaskFilter>, sk_sp<PrecompileMaskFilter>> create_maskfilter(SkRandom* rand,
1642 MaskFilterType type) {
1643 switch (type) {
1644 case MaskFilterType::kNone: return {nullptr, nullptr};
1645 case MaskFilterType::kBlur: return create_blur_maskfilter(rand);
1646 }
1647
1648 SkUNREACHABLE;
1649 }
1650
1651 //--------------------------------------------------------------------------------------------------
create_paint(SkRandom * rand,Recorder * recorder,ShaderType shaderType,BlenderType blenderType,ColorFilterType colorFilterType,MaskFilterType maskFilterType,ImageFilterType imageFilterType)1652 std::pair<SkPaint, PaintOptions> create_paint(SkRandom* rand,
1653 Recorder* recorder,
1654 ShaderType shaderType,
1655 BlenderType blenderType,
1656 ColorFilterType colorFilterType,
1657 MaskFilterType maskFilterType,
1658 ImageFilterType imageFilterType) {
1659 SkPaint paint;
1660 paint.setColor(random_color(rand, ColorConstraint::kOpaque));
1661
1662 PaintOptions paintOptions;
1663
1664 {
1665 auto [s, o] = create_shader(rand, recorder, shaderType);
1666 SkASSERT(!s == !o);
1667
1668 if (s) {
1669 paint.setShader(std::move(s));
1670 paintOptions.setShaders({o});
1671 }
1672 }
1673
1674 {
1675 auto [cf, o] = create_colorfilter(rand, colorFilterType);
1676 SkASSERT(!cf == !o);
1677
1678 if (cf) {
1679 paint.setColorFilter(std::move(cf));
1680 paintOptions.setColorFilters({o});
1681 }
1682 }
1683
1684 {
1685 auto [mf, o] = create_maskfilter(rand, maskFilterType);
1686 SkASSERT(!mf == !o);
1687
1688 if (mf) {
1689 paint.setMaskFilter(std::move(mf));
1690 paintOptions.setMaskFilters({o});
1691 }
1692 }
1693
1694 {
1695 auto [b, o] = create_blender(rand, blenderType);
1696 SkASSERT(!b == !o);
1697
1698 if (b) {
1699 paint.setBlender(std::move(b));
1700 paintOptions.setBlenders({o});
1701 }
1702 }
1703
1704 {
1705 auto [filter, o] = create_image_filter(recorder, rand, imageFilterType);
1706 SkASSERT(!filter == !o);
1707
1708 if (filter) {
1709 paint.setImageFilter(std::move(filter));
1710 paintOptions.setImageFilters({o});
1711 }
1712 }
1713
1714 if (rand->nextBool()) {
1715 paint.setDither(true);
1716 paintOptions.setDither(true);
1717 }
1718
1719 return { paint, paintOptions };
1720 }
1721
make_path()1722 SkPath make_path() {
1723 SkPathBuilder path;
1724 path.moveTo(0, 0);
1725 path.lineTo(8, 2);
1726 path.lineTo(16, 0);
1727 path.lineTo(14, 8);
1728 path.lineTo(16, 16);
1729 path.lineTo(8, 14);
1730 path.lineTo(0, 16);
1731 path.lineTo(2, 8);
1732 path.close();
1733 return path.detach();
1734 }
1735
1736 struct DrawData {
DrawData__anon74e4dc3c0111::DrawData1737 DrawData() {
1738 static constexpr int kMaskTextFontSize = 16;
1739 // A large font size can bump text to be drawn as a path.
1740 static constexpr int kPathTextFontSize = 300;
1741
1742 SkFont font(ToolUtils::DefaultPortableTypeface(), kMaskTextFontSize);
1743
1744 SkFont lcdFont(ToolUtils::DefaultPortableTypeface(), kMaskTextFontSize);
1745 lcdFont.setSubpixel(true);
1746 lcdFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
1747
1748 ToolUtils::EmojiTestSample emojiTestSample =
1749 ToolUtils::EmojiSample(ToolUtils::EmojiFontFormat::ColrV0);
1750 SkFont emojiFont(emojiTestSample.typeface);
1751
1752 SkFont pathFont(ToolUtils::DefaultPortableTypeface(), kPathTextFontSize);
1753
1754 const char text[] = "hambur1";
1755
1756 constexpr int kNumVerts = 4;
1757 constexpr SkPoint kPositions[kNumVerts] { { 0, 0 }, { 0, 16 }, { 16, 16 }, { 16, 0 } };
1758 constexpr SkColor kColors[kNumVerts] = { SK_ColorBLUE, SK_ColorGREEN,
1759 SK_ColorCYAN, SK_ColorYELLOW };
1760
1761 fPath = make_path();
1762 fBlob = SkTextBlob::MakeFromText(text, strlen(text), font);
1763 fLCDBlob = SkTextBlob::MakeFromText(text, strlen(text), lcdFont);
1764 fEmojiBlob = SkTextBlob::MakeFromText(emojiTestSample.sampleText,
1765 strlen(emojiTestSample.sampleText),
1766 emojiFont);
1767 fPathBlob = SkTextBlob::MakeFromText(text, strlen(text), pathFont);
1768
1769 fVertsWithColors = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, kNumVerts,
1770 kPositions, kPositions, kColors);
1771 fVertsWithOutColors = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, kNumVerts,
1772 kPositions, kPositions, /* colors= */ nullptr);
1773 }
1774
1775 SkPath fPath;
1776 sk_sp<SkTextBlob> fBlob;
1777 sk_sp<SkTextBlob> fLCDBlob;
1778 sk_sp<SkTextBlob> fEmojiBlob;
1779 sk_sp<SkTextBlob> fPathBlob;
1780 sk_sp<SkVertices> fVertsWithColors;
1781 sk_sp<SkVertices> fVertsWithOutColors;
1782 };
1783
simple_draws(SkCanvas * canvas,const SkPaint & paint)1784 void simple_draws(SkCanvas* canvas, const SkPaint& paint) {
1785 // TODO: add some drawLine calls
1786 canvas->drawRect(SkRect::MakeWH(16, 16), paint);
1787 canvas->drawRRect(SkRRect::MakeOval({0, 0, 16, 16}), paint);
1788 canvas->drawRRect(SkRRect::MakeRectXY({0, 0, 16, 16}, 4, 4), paint);
1789 canvas->drawArc({0, 0, 16, 16}, 0, 90, /* useCenter= */ false, paint);
1790 if (paint.getStyle() == SkPaint::kFill_Style) {
1791 canvas->drawArc({0, 0, 16, 16}, 0, 90, /* useCenter= */ true, paint);
1792 }
1793
1794 // TODO: add a case that uses the SkCanvas::experimental_DrawEdgeAAImageSet entry point
1795 if (!paint.getShader() &&
1796 !paint.getColorFilter() &&
1797 !paint.getImageFilter() &&
1798 paint.asBlendMode().has_value()) {
1799 // The SkPaint reconstructed inside the drawEdgeAAQuad call needs to match 'paint' for
1800 // the precompilation checks to work.
1801 canvas->experimental_DrawEdgeAAQuad(SkRect::MakeWH(16, 16),
1802 /* clip= */ nullptr,
1803 SkCanvas::kAll_QuadAAFlags,
1804 paint.getColor4f(),
1805 paint.asBlendMode().value());
1806 }
1807 }
1808
non_simple_draws(SkCanvas * canvas,const SkPaint & paint,const DrawData & drawData)1809 void non_simple_draws(SkCanvas* canvas, const SkPaint& paint, const DrawData& drawData) {
1810 // TODO: add strokeAndFill draws here as well as a stroked non-circular rrect draw
1811 canvas->drawPath(drawData.fPath, paint);
1812 canvas->drawTextBlob(drawData.fPathBlob, 0, 16, paint);
1813 if (paint.getStyle() == SkPaint::kStroke_Style) {
1814 canvas->drawArc({0, 0, 16, 16}, 0, 90, /* useCenter= */ true, paint);
1815 }
1816 }
1817
1818 #ifdef SK_DEBUG
dump_keys(PrecompileContext * precompileContext,const std::vector<skgpu::UniqueKey> & needleKeys,const std::vector<skgpu::UniqueKey> & hayStackKeys,const char * needleName,const char * haystackName)1819 void dump_keys(PrecompileContext* precompileContext,
1820 const std::vector<skgpu::UniqueKey>& needleKeys,
1821 const std::vector<skgpu::UniqueKey>& hayStackKeys,
1822 const char* needleName,
1823 const char* haystackName) {
1824
1825 SkDebugf("-------------------------- %zu %s pipelines\n", needleKeys.size(), needleName);
1826
1827 int count = 0;
1828 for (const skgpu::UniqueKey& k : needleKeys) {
1829 bool found = std::find(hayStackKeys.begin(), hayStackKeys.end(), k) != hayStackKeys.end();
1830
1831 GraphicsPipelineDesc originalPipelineDesc;
1832 RenderPassDesc originalRenderPassDesc;
1833 UniqueKeyUtils::ExtractKeyDescs(precompileContext, k,
1834 &originalPipelineDesc,
1835 &originalRenderPassDesc);
1836
1837 SkString label;
1838 label.appendf("--- %s key %d (%s in %s):\n",
1839 needleName, count++, found ? "found" : "not-found", haystackName);
1840 k.dump(label.c_str());
1841 UniqueKeyUtils::DumpDescs(precompileContext,
1842 originalPipelineDesc,
1843 originalRenderPassDesc);
1844 }
1845 }
1846 #endif
1847
check_draw(skiatest::Reporter * reporter,Context * context,PrecompileContext * precompileContext,skiatest::graphite::GraphiteTestContext * testContext,Recorder * recorder,const SkPaint & paint,DrawTypeFlags dt,ClipType clip,sk_sp<SkShader> clipShader)1848 void check_draw(skiatest::Reporter* reporter,
1849 Context* context,
1850 PrecompileContext* precompileContext,
1851 skiatest::graphite::GraphiteTestContext* testContext,
1852 Recorder* recorder,
1853 const SkPaint& paint,
1854 DrawTypeFlags dt,
1855 ClipType clip,
1856 sk_sp<SkShader> clipShader) {
1857 static const DrawData kDrawData;
1858
1859 std::vector<skgpu::UniqueKey> precompileKeys, drawKeys;
1860
1861 UniqueKeyUtils::FetchUniqueKeys(precompileContext, &precompileKeys);
1862
1863 precompileContext->priv().globalCache()->resetGraphicsPipelines();
1864
1865 {
1866 // TODO: vary the colorType of the target surface too
1867 SkImageInfo ii = SkImageInfo::Make(16, 16,
1868 kBGRA_8888_SkColorType,
1869 kPremul_SkAlphaType);
1870
1871 SkSurfaceProps props;
1872
1873 if (dt == DrawTypeFlags::kBitmapText_LCD || dt == DrawTypeFlags::kSDFText_LCD) {
1874 props = SkSurfaceProps(/* flags= */ 0x0, SkPixelGeometry::kRGB_H_SkPixelGeometry);
1875 }
1876
1877 sk_sp<SkSurface> surf = SkSurfaces::RenderTarget(recorder, ii,
1878 skgpu::Mipmapped::kNo,
1879 &props);
1880 SkCanvas* canvas = surf->getCanvas();
1881
1882 switch (clip) {
1883 case ClipType::kNone:
1884 break;
1885 case ClipType::kShader:
1886 SkASSERT(clipShader);
1887 canvas->clipShader(clipShader, SkClipOp::kIntersect);
1888 break;
1889 case ClipType::kShader_Diff:
1890 SkASSERT(clipShader);
1891 canvas->clipShader(clipShader, SkClipOp::kDifference);
1892 break;
1893 }
1894
1895 switch (dt) {
1896 case DrawTypeFlags::kBitmapText_Mask:
1897 canvas->drawTextBlob(kDrawData.fBlob, 0, 16, paint);
1898 break;
1899 case DrawTypeFlags::kBitmapText_LCD:
1900 canvas->drawTextBlob(kDrawData.fLCDBlob, 0, 16, paint);
1901 break;
1902 case DrawTypeFlags::kBitmapText_Color:
1903 canvas->drawTextBlob(kDrawData.fEmojiBlob, 0, 16, paint);
1904 break;
1905 case DrawTypeFlags::kSDFText: {
1906 SkMatrix perspective;
1907 perspective.setPerspX(0.01f);
1908 perspective.setPerspY(0.001f);
1909 canvas->save();
1910 canvas->concat(perspective);
1911 canvas->drawTextBlob(kDrawData.fBlob, 0, 16, paint);
1912 canvas->restore();
1913 } break;
1914 case DrawTypeFlags::kSDFText_LCD: {
1915 SkMatrix perspective;
1916 perspective.setPerspX(0.01f);
1917 perspective.setPerspY(0.001f);
1918 canvas->save();
1919 canvas->concat(perspective);
1920 canvas->drawTextBlob(kDrawData.fLCDBlob, 0, 16, paint);
1921 canvas->restore();
1922 } break;
1923 case DrawTypeFlags::kDrawVertices:
1924 canvas->drawVertices(kDrawData.fVertsWithColors, SkBlendMode::kDst, paint);
1925 canvas->drawVertices(kDrawData.fVertsWithOutColors, SkBlendMode::kDst, paint);
1926 break;
1927 case DrawTypeFlags::kSimpleShape:
1928 simple_draws(canvas, paint);
1929 break;
1930 case DrawTypeFlags::kNonSimpleShape:
1931 non_simple_draws(canvas, paint, kDrawData);
1932 break;
1933 default:
1934 SkASSERT(false);
1935 break;
1936 }
1937
1938 std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
1939 context->insertRecording({ recording.get() });
1940 testContext->syncedSubmit(context);
1941 }
1942
1943 UniqueKeyUtils::FetchUniqueKeys(precompileContext, &drawKeys);
1944
1945 // Actually using the SkPaint with the specified type of draw shouldn't have added
1946 // any additional pipelines
1947 int missingPipelines = 0;
1948 for (const skgpu::UniqueKey& k : drawKeys) {
1949 bool found =
1950 std::find(precompileKeys.begin(), precompileKeys.end(), k) != precompileKeys.end();
1951 if (!found) {
1952 ++missingPipelines;
1953 }
1954 }
1955
1956 REPORTER_ASSERT(reporter, missingPipelines == 0,
1957 "precompile pipelines: %zu draw pipelines: %zu - %d missing from precompile",
1958 precompileKeys.size(), drawKeys.size(), missingPipelines);
1959 #ifdef SK_DEBUG
1960 if (missingPipelines) {
1961 dump_keys(precompileContext, drawKeys, precompileKeys, "draw", "precompile");
1962 dump_keys(precompileContext, precompileKeys, drawKeys, "precompile", "draw");
1963 }
1964 #endif // SK_DEBUG
1965
1966 }
1967
create_key_context(Context * context,RuntimeEffectDictionary * rtDict)1968 KeyContext create_key_context(Context* context, RuntimeEffectDictionary* rtDict) {
1969 ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
1970
1971 SkColorInfo destColorInfo = SkColorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType,
1972 SkColorSpace::MakeSRGB());
1973 return KeyContext(context->priv().caps(),
1974 dict,
1975 rtDict,
1976 destColorInfo);
1977 }
1978
1979 // This subtest compares the output of ExtractPaintData (applied to an SkPaint) and
1980 // PaintOptions::buildCombinations (applied to a matching PaintOptions). The actual check
1981 // performed is that the UniquePaintParamsID created by ExtractPaintData is contained in the
1982 // set of IDs generated by buildCombinations.
1983 [[maybe_unused]]
extract_vs_build_subtest(skiatest::Reporter * reporter,Context * context,skiatest::graphite::GraphiteTestContext *,const KeyContext & precompileKeyContext,Recorder * recorder,const SkPaint & paint,const PaintOptions & paintOptions,ShaderType s,BlenderType bm,ColorFilterType cf,MaskFilterType mf,ImageFilterType imageFilter,ClipType clip,sk_sp<SkShader> clipShader,DrawTypeFlags dt,uint32_t seed,SkRandom * rand,bool verbose)1984 void extract_vs_build_subtest(skiatest::Reporter* reporter,
1985 Context* context,
1986 skiatest::graphite::GraphiteTestContext* /* testContext */,
1987 const KeyContext& precompileKeyContext,
1988 Recorder* recorder,
1989 const SkPaint& paint,
1990 const PaintOptions& paintOptions,
1991 ShaderType s,
1992 BlenderType bm,
1993 ColorFilterType cf,
1994 MaskFilterType mf,
1995 ImageFilterType imageFilter,
1996 ClipType clip,
1997 sk_sp<SkShader> clipShader,
1998 DrawTypeFlags dt,
1999 uint32_t seed,
2000 SkRandom* rand,
2001 bool verbose) {
2002
2003 ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
2004
2005 PaintParamsKeyBuilder builder(dict);
2006 PipelineDataGatherer paramsGatherer(Layout::kMetal);
2007 PipelineDataGatherer precompileGatherer(Layout::kMetal);
2008
2009 for (bool withPrimitiveBlender: {false, true}) {
2010
2011 sk_sp<SkBlender> primitiveBlender;
2012 if (withPrimitiveBlender) {
2013 if (dt != DrawTypeFlags::kDrawVertices) {
2014 // Only drawVertices calls need a primitive blender
2015 continue;
2016 }
2017
2018 primitiveBlender = SkBlender::Mode(SkBlendMode::kSrcOver);
2019 }
2020
2021 constexpr Coverage coverageOptions[3] = {
2022 Coverage::kNone, Coverage::kSingleChannel, Coverage::kLCD
2023 };
2024 Coverage coverage = coverageOptions[rand->nextULessThan(3)];
2025
2026 DstReadRequirement dstReadReq = DstReadRequirement::kNone;
2027 const SkBlenderBase* blender = as_BB(paint.getBlender());
2028 if (blender) {
2029 dstReadReq = GetDstReadRequirement(recorder->priv().caps(),
2030 blender->asBlendMode(),
2031 coverage);
2032 }
2033
2034 // In the normal API this modification happens in SkDevice::clipShader()
2035 // All clipShaders get wrapped in a CTMShader
2036 sk_sp<SkShader> modifiedClipShader = clipShader
2037 ? as_SB(clipShader)->makeWithCTM(SkMatrix::I())
2038 : nullptr;
2039 if (clip == ClipType::kShader_Diff && modifiedClipShader) {
2040 // The CTMShader gets further wrapped in a ColorFilterShader for kDifference clips
2041 modifiedClipShader = modifiedClipShader->makeWithColorFilter(
2042 SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));
2043 }
2044
2045 UniquePaintParamsID paintID =
2046 ExtractPaintData(recorder,
2047 ¶msGatherer,
2048 &builder,
2049 Layout::kMetal,
2050 {},
2051 PaintParams(paint,
2052 primitiveBlender,
2053 {}, // TODO (jvanverth): add analytic clip to test
2054 std::move(modifiedClipShader),
2055 dstReadReq,
2056 /* skipColorXform= */ false),
2057 {},
2058 precompileKeyContext.dstColorInfo());
2059
2060 RenderPassDesc unusedRenderPassDesc;
2061
2062 std::vector<UniquePaintParamsID> precompileIDs;
2063 paintOptions.priv().buildCombinations(precompileKeyContext,
2064 &precompileGatherer,
2065 DrawTypeFlags::kNone,
2066 withPrimitiveBlender,
2067 coverage,
2068 unusedRenderPassDesc,
2069 [&precompileIDs](UniquePaintParamsID id,
2070 DrawTypeFlags,
2071 bool /* withPrimitiveBlender */,
2072 Coverage,
2073 const RenderPassDesc&) {
2074 precompileIDs.push_back(id);
2075 });
2076
2077 if (verbose) {
2078 SkDebugf("Precompilation generated %zu unique keys\n", precompileIDs.size());
2079 }
2080
2081 // Although we've gathered both sets of uniforms (i.e., from the paint
2082 // params and the precompilation paths) we can't compare the two since the
2083 // precompilation path may have generated multiple sets
2084 // and the last one created may not be the one that matches the paint
2085 // params' set. Additionally, for runtime effects we just skip gathering
2086 // the uniforms in the precompilation path.
2087
2088 // The specific key generated by ExtractPaintData should be one of the
2089 // combinations generated by the combination system.
2090 auto result = std::find(precompileIDs.begin(), precompileIDs.end(), paintID);
2091
2092 if (result == precompileIDs.end()) {
2093 log_run("Failure on case", seed, s, bm, cf, mf, imageFilter, clip, dt);
2094 }
2095
2096 #ifdef SK_DEBUG
2097 if (result == precompileIDs.end()) {
2098 SkDebugf("From paint: ");
2099 dict->dump(paintID);
2100
2101 SkDebugf("From combination builder [%d]:", static_cast<int>(precompileIDs.size()));
2102 for (auto iter: precompileIDs) {
2103 dict->dump(iter);
2104 }
2105 }
2106 #endif
2107
2108 REPORTER_ASSERT(reporter, result != precompileIDs.end());
2109 }
2110 }
2111
2112 // This subtest verifies that, given an equivalent SkPaint and PaintOptions, the
2113 // Precompile system will, at least, generate all the pipelines a real draw would generate.
precompile_vs_real_draws_subtest(skiatest::Reporter * reporter,Context * context,PrecompileContext * precompileContext,skiatest::graphite::GraphiteTestContext * testContext,Recorder * recorder,const SkPaint & paint,const PaintOptions & paintOptions,ClipType clip,sk_sp<SkShader> clipShader,DrawTypeFlags dt,bool)2114 void precompile_vs_real_draws_subtest(skiatest::Reporter* reporter,
2115 Context* context,
2116 PrecompileContext* precompileContext,
2117 skiatest::graphite::GraphiteTestContext* testContext,
2118 Recorder* recorder,
2119 const SkPaint& paint,
2120 const PaintOptions& paintOptions,
2121 ClipType clip,
2122 sk_sp<SkShader> clipShader,
2123 DrawTypeFlags dt,
2124 bool /* verbose */) {
2125 GlobalCache* globalCache = precompileContext->priv().globalCache();
2126
2127 globalCache->resetGraphicsPipelines();
2128
2129 const skgpu::graphite::Caps* caps = context->priv().caps();
2130
2131 const SkColorType kColorType = kBGRA_8888_SkColorType;
2132
2133 static const RenderPassProperties kDepth_Stencil_4 { DepthStencilFlags::kDepthStencil,
2134 kColorType,
2135 /* requiresMSAA= */ true };
2136 static const RenderPassProperties kDepth_1 { DepthStencilFlags::kDepth,
2137 kColorType,
2138 /* requiresMSAA= */ false };
2139
2140 TextureInfo textureInfo = caps->getDefaultSampledTextureInfo(kColorType,
2141 skgpu::Mipmapped::kNo,
2142 skgpu::Protected::kNo,
2143 skgpu::Renderable::kYes);
2144
2145 TextureInfo msaaTex = caps->getDefaultMSAATextureInfo(textureInfo, Discardable::kYes);
2146
2147 bool vello = false;
2148 #ifdef SK_ENABLE_VELLO_SHADERS
2149 vello = caps->computeSupport();
2150 #endif
2151
2152 // Using Vello skips using MSAA for complex paths. Additionally, Intel Macs avoid MSAA
2153 // in favor of path rendering.
2154 const RenderPassProperties* pathProperties = (msaaTex.numSamples() > 1 && !vello)
2155 ? &kDepth_Stencil_4
2156 : &kDepth_1;
2157
2158 int before = globalCache->numGraphicsPipelines();
2159 Precompile(precompileContext, paintOptions, dt,
2160 dt == kNonSimpleShape ? SkSpan(pathProperties, 1) : SkSpan(&kDepth_1, 1));
2161 if (gNeedSKPPaintOption) {
2162 // The skp draws a rect w/ a default SkPaint
2163 PaintOptions skpPaintOptions;
2164 Precompile(precompileContext, skpPaintOptions, DrawTypeFlags::kSimpleShape,
2165 { kDepth_1 });
2166 }
2167 int after = globalCache->numGraphicsPipelines();
2168
2169 REPORTER_ASSERT(reporter, before == 0);
2170 REPORTER_ASSERT(reporter, after > before);
2171
2172 check_draw(reporter,
2173 context,
2174 precompileContext,
2175 testContext,
2176 recorder,
2177 paint,
2178 dt,
2179 clip,
2180 clipShader);
2181 }
2182
run_test(skiatest::Reporter * reporter,Context * context,PrecompileContext * precompileContext,skiatest::graphite::GraphiteTestContext * testContext,const KeyContext & precompileKeyContext,ShaderType s,BlenderType bm,ColorFilterType cf,MaskFilterType mf,ImageFilterType imageFilter,ClipType clip,DrawTypeFlags dt,uint32_t seed,bool verbose)2183 void run_test(skiatest::Reporter* reporter,
2184 Context* context,
2185 PrecompileContext* precompileContext,
2186 skiatest::graphite::GraphiteTestContext* testContext,
2187 const KeyContext& precompileKeyContext,
2188 ShaderType s,
2189 BlenderType bm,
2190 ColorFilterType cf,
2191 MaskFilterType mf,
2192 ImageFilterType imageFilter,
2193 ClipType clip,
2194 DrawTypeFlags dt,
2195 uint32_t seed,
2196 bool verbose) {
2197 SkRandom rand(seed);
2198
2199 std::unique_ptr<Recorder> recorder = context->makeRecorder();
2200
2201 sk_sp<SkShader> clipShader;
2202 sk_sp<PrecompileShader> clipShaderOption;
2203
2204 if (clip == ClipType::kShader || clip == ClipType::kShader_Diff) {
2205 std::tie(clipShader, clipShaderOption) = create_clip_shader(&rand, recorder.get());
2206 SkASSERT(!clipShader == !clipShaderOption);
2207 }
2208
2209 gNeedSKPPaintOption = false;
2210 auto [paint, paintOptions] = create_paint(&rand, recorder.get(), s, bm, cf, mf, imageFilter);
2211
2212 // The PaintOptions' clipShader can be handled here while the SkPaint's clipShader handling
2213 // must be performed later (in ExtractPaintData or when an SkCanvas is accessible for
2214 // a SkCanvas::clipShader call).
2215 paintOptions.priv().setClipShaders({clipShaderOption});
2216
2217 extract_vs_build_subtest(reporter, context, testContext, precompileKeyContext, recorder.get(),
2218 paint, paintOptions, s, bm, cf, mf, imageFilter, clip, clipShader, dt,
2219 seed, &rand, verbose);
2220 precompile_vs_real_draws_subtest(reporter, context, precompileContext,
2221 testContext, recorder.get(),
2222 paint, paintOptions, clip, clipShader, dt, verbose);
2223 }
2224
2225 } // anonymous namespace
2226
DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(PaintParamsKeyTestReduced,reporter,context,testContext,true,CtsEnforcement::kNever)2227 DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(PaintParamsKeyTestReduced,
2228 reporter,
2229 context,
2230 testContext,
2231 true,
2232 CtsEnforcement::kNever) {
2233 std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
2234 std::unique_ptr<RuntimeEffectDictionary> rtDict = std::make_unique<RuntimeEffectDictionary>();
2235
2236 #if 1
2237 //----------------------
2238 uint32_t seed = std::time(nullptr) % std::numeric_limits<uint32_t>::max();
2239 SkRandom rand(seed);
2240 ShaderType shaderType = random_shadertype(&rand);
2241 BlenderType blenderType = random_blendertype(&rand);
2242 ColorFilterType colorFilterType = random_colorfiltertype(&rand);
2243 MaskFilterType maskFilterType = random_maskfiltertype(&rand);
2244 ImageFilterType imageFilterType = ImageFilterType::kNone; // random_imagefiltertype(&rand);
2245 ClipType clipType = random_cliptype(&rand);
2246 DrawTypeFlags drawTypeFlags = random_drawtype(&rand);
2247 //----------------------
2248 #else
2249 //------------------------
2250 uint32_t seed = 1721227069;
2251 ShaderType shaderType = ShaderType::kLocalMatrix;
2252 BlenderType blenderType = BlenderType::kArithmetic;
2253 ColorFilterType colorFilterType = ColorFilterType::kRuntime;
2254 MaskFilterType maskFilterType = MaskFilterType::kNone;
2255 ImageFilterType imageFilterType = ImageFilterType::kDisplacement;
2256 ClipType clipType = ClipType::kNone;
2257 DrawTypeFlags drawTypeFlags = DrawTypeFlags::kText;
2258 //-----------------------
2259 #endif
2260
2261 SkString logMsg("Running ");
2262 logMsg += BackendApiToStr(context->backend());
2263
2264 log_run(logMsg.c_str(), seed, shaderType, blenderType, colorFilterType, maskFilterType,
2265 imageFilterType, clipType, drawTypeFlags);
2266
2267 run_test(reporter,
2268 context,
2269 precompileContext.get(),
2270 testContext,
2271 create_key_context(context, rtDict.get()),
2272 shaderType,
2273 blenderType,
2274 colorFilterType,
2275 maskFilterType,
2276 imageFilterType,
2277 clipType,
2278 drawTypeFlags,
2279 seed,
2280 /* verbose= */ true);
2281 }
2282
2283 // This is intended to be a smoke test for the agreement between the two ways of creating a
2284 // PaintParamsKey:
2285 // via ExtractPaintData (i.e., from an SkPaint)
2286 // and via the pre-compilation system
2287 //
2288 // TODO: keep this as a smoke test but add a fuzzer that reuses all the helpers
2289 // TODO(b/306174708): enable in SkQP (if it's feasible)
DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(PaintParamsKeyTest,reporter,context,testContext,true,CtsEnforcement::kNever)2290 DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(PaintParamsKeyTest,
2291 reporter,
2292 context,
2293 testContext,
2294 true,
2295 CtsEnforcement::kNever) {
2296 std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
2297 std::unique_ptr<RuntimeEffectDictionary> rtDict = std::make_unique<RuntimeEffectDictionary>();
2298
2299 KeyContext precompileKeyContext(create_key_context(context, rtDict.get()));
2300
2301 ShaderType shaders[] = {
2302 ShaderType::kBlend,
2303 ShaderType::kImage,
2304 ShaderType::kRadialGradient,
2305 ShaderType::kSolidColor,
2306 #if EXPANDED_SET
2307 ShaderType::kNone,
2308 ShaderType::kColorFilter,
2309 ShaderType::kCoordClamp,
2310 ShaderType::kConicalGradient,
2311 ShaderType::kLinearGradient,
2312 ShaderType::kLocalMatrix,
2313 ShaderType::kPerlinNoise,
2314 ShaderType::kPicture,
2315 ShaderType::kRuntime,
2316 ShaderType::kSweepGradient,
2317 ShaderType::kYUVImage,
2318 ShaderType::kWorkingColorSpace,
2319 #endif
2320 };
2321
2322 BlenderType blenders[] = {
2323 BlenderType::kPorterDuff,
2324 BlenderType::kShaderBased,
2325 BlenderType::kRuntime,
2326 #if EXPANDED_SET
2327 BlenderType::kNone,
2328 BlenderType::kArithmetic,
2329 #endif
2330 };
2331
2332 ColorFilterType colorFilters[] = {
2333 ColorFilterType::kNone,
2334 ColorFilterType::kBlendMode,
2335 ColorFilterType::kMatrix,
2336 #if EXPANDED_SET
2337 ColorFilterType::kColorSpaceXform,
2338 ColorFilterType::kCompose,
2339 ColorFilterType::kGaussian,
2340 ColorFilterType::kHighContrast,
2341 ColorFilterType::kHSLAMatrix,
2342 ColorFilterType::kLerp,
2343 ColorFilterType::kLighting,
2344 ColorFilterType::kLinearToSRGB,
2345 ColorFilterType::kLuma,
2346 ColorFilterType::kOverdraw,
2347 ColorFilterType::kRuntime,
2348 ColorFilterType::kSRGBToLinear,
2349 ColorFilterType::kTable,
2350 ColorFilterType::kWorkingFormat,
2351 #endif
2352 };
2353
2354 MaskFilterType maskFilters[] = {
2355 MaskFilterType::kNone,
2356 #if EXPANDED_SET
2357 MaskFilterType::kBlur,
2358 #endif
2359 };
2360
2361 ImageFilterType imageFilters[] = {
2362 ImageFilterType::kNone,
2363 #if EXPANDED_SET
2364 ImageFilterType::kArithmetic,
2365 ImageFilterType::kBlendMode,
2366 ImageFilterType::kRuntimeBlender,
2367 ImageFilterType::kBlur,
2368 ImageFilterType::kColorFilter,
2369 ImageFilterType::kDisplacement,
2370 ImageFilterType::kLighting,
2371 ImageFilterType::kMatrixConvolution,
2372 ImageFilterType::kMorphology,
2373 #endif
2374 };
2375
2376 ClipType clips[] = {
2377 ClipType::kNone,
2378 #if EXPANDED_SET
2379 ClipType::kShader, // w/ a SkClipOp::kIntersect
2380 ClipType::kShader_Diff, // w/ a SkClipOp::kDifference
2381 #endif
2382 };
2383
2384 static const DrawTypeFlags kDrawTypeFlags[] = {
2385 DrawTypeFlags::kBitmapText_Mask,
2386 DrawTypeFlags::kBitmapText_LCD,
2387 DrawTypeFlags::kBitmapText_Color,
2388 DrawTypeFlags::kSDFText,
2389 DrawTypeFlags::kSDFText_LCD,
2390 DrawTypeFlags::kDrawVertices,
2391 DrawTypeFlags::kSimpleShape,
2392 DrawTypeFlags::kNonSimpleShape,
2393 };
2394
2395 #if EXPANDED_SET
2396 size_t kExpected = std::size(shaders) * std::size(blenders) * std::size(colorFilters) *
2397 std::size(maskFilters) * std::size(imageFilters) * std::size(clips) *
2398 std::size(kDrawTypeFlags);
2399 int current = 0;
2400 #endif
2401
2402 for (auto shader : shaders) {
2403 for (auto blender : blenders) {
2404 for (auto cf : colorFilters) {
2405 for (auto mf : maskFilters) {
2406 for (auto imageFilter : imageFilters) {
2407 for (auto clip : clips) {
2408 for (DrawTypeFlags dt : kDrawTypeFlags) {
2409 #if EXPANDED_SET
2410 SkDebugf("%d/%zu\n", current, kExpected);
2411 ++current;
2412 #endif
2413
2414 run_test(reporter, context, precompileContext.get(),
2415 testContext, precompileKeyContext,
2416 shader, blender, cf, mf, imageFilter, clip, dt,
2417 kDefaultSeed, /* verbose= */ false);
2418 }
2419 }
2420 }
2421 }
2422 }
2423 }
2424 }
2425
2426 #if EXPANDED_SET
2427 SkASSERT(current == (int) kExpected);
2428 #endif
2429 }
2430
2431 #endif // SK_GRAPHITE
2432