xref: /aosp_15_r20/external/skia/tests/graphite/precompile/CombinationBuilderTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 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/SkColorSpace.h"
13 #include "include/effects/SkRuntimeEffect.h"
14 #include "include/gpu/graphite/precompile/PrecompileBlender.h"
15 #include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
16 #include "include/gpu/graphite/precompile/PrecompileRuntimeEffect.h"
17 #include "include/gpu/graphite/precompile/PrecompileShader.h"
18 #include "src/gpu/graphite/ContextPriv.h"
19 #include "src/gpu/graphite/KeyContext.h"
20 #include "src/gpu/graphite/PipelineData.h"
21 #include "src/gpu/graphite/PrecompileInternal.h"
22 #include "src/gpu/graphite/RenderPassDesc.h"
23 #include "src/gpu/graphite/Renderer.h"
24 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
25 #include "src/gpu/graphite/precompile/PaintOptionsPriv.h"
26 
27 #include <array>
28 
29 using namespace::skgpu::graphite;
30 
31 namespace {
32 
33 // colorfilters
34 static constexpr int kExpectedBlendCFCombos = 15;
35 static constexpr int kExpectedColorSpaceCFCombos = 1;
36 static constexpr int kExpectedHighContrastCFCombos = 1;
37 static constexpr int kExpectedLightingCFCombos = 1;
38 static constexpr int kExpectedLumaCFCombos = 1;
39 static constexpr int kExpectedMatrixCFCombos = 1;
40 static constexpr int kExpectedOverdrawCFCombos = 1;
41 static constexpr int kExpectedTableCFCombos = 1;
42 
43 // shaders
44 static constexpr int kExpectedGradientCombos = 3;
45 static constexpr int kExpectedImageCombos = 6;
46 static constexpr int kExpectedPerlinNoiseCombos = 1;
47 static constexpr int kExpectedPictureCombos = 12;
48 static constexpr int kExpectedRawImageCombos = 3;
49 static constexpr int kExpectedSolidColorCombos = 1;
50 
51 
52 // A default kSrcOver blend mode will be supplied if no other blend options are added
no_blend_mode_option_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const RenderPassDesc & renderPassDesc,skiatest::Reporter * reporter)53 void no_blend_mode_option_test(const KeyContext& keyContext,
54                                PipelineDataGatherer* gatherer,
55                                const RenderPassDesc& renderPassDesc,
56                                skiatest::Reporter* reporter) {
57     PaintOptions paintOptions;
58     paintOptions.setShaders({ PrecompileShaders::Color() });
59 
60     REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == 1);
61 
62     std::vector<UniquePaintParamsID> precompileIDs;
63     paintOptions.priv().buildCombinations(keyContext,
64                                           gatherer,
65                                           DrawTypeFlags::kNone,
66                                           /* withPrimitiveBlender= */ false,
67                                           Coverage::kNone,
68                                           renderPassDesc,
69                                           [&precompileIDs](UniquePaintParamsID id,
70                                                            DrawTypeFlags,
71                                                            bool /* withPrimitiveBlender */,
72                                                            Coverage,
73                                                            const RenderPassDesc&) {
74                                                                precompileIDs.push_back(id);
75                                                            });
76 
77     SkASSERT(precompileIDs.size() == 1);
78 }
79 
80 // This test checks that the 'PaintOptions::numCombinations' method and the number actually
81 // generated by 'buildCombinations' agree with the expected number of combinations.
run_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const RenderPassDesc & renderPassDesc,skiatest::Reporter * reporter,const PaintOptions & paintOptions,int expectedNumOptions)82 void run_test(const KeyContext& keyContext,
83               PipelineDataGatherer* gatherer,
84               const RenderPassDesc& renderPassDesc,
85               skiatest::Reporter* reporter,
86               const PaintOptions& paintOptions,
87               int expectedNumOptions) {
88     REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == expectedNumOptions,
89                     "expected %d, but was %d",
90                     expectedNumOptions, paintOptions.priv().numCombinations());
91 
92     std::vector<UniquePaintParamsID> precompileIDs;
93     paintOptions.priv().buildCombinations(keyContext,
94                                           gatherer,
95                                           DrawTypeFlags::kNone,
96                                           /* withPrimitiveBlender= */ false,
97                                           Coverage::kNone,
98                                           renderPassDesc,
99                                           [&precompileIDs](UniquePaintParamsID id,
100                                                            DrawTypeFlags,
101                                                            bool /* withPrimitiveBlender */,
102                                                            Coverage,
103                                                            const RenderPassDesc&) {
104                                               precompileIDs.push_back(id);
105                                           });
106 
107     SkASSERT(static_cast<int>(precompileIDs.size()) == expectedNumOptions);
108 }
109 
big_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const RenderPassDesc & renderPassDesc,skiatest::Reporter * reporter)110 void big_test(const KeyContext& keyContext,
111               PipelineDataGatherer* gatherer,
112               const RenderPassDesc& renderPassDesc,
113               skiatest::Reporter* reporter) {
114 
115     static constexpr int kNumExpected = 444;
116     // paintOptions (444 = 4*111)
117     //  |- (111 = 3+108) sweepGrad_0 (3) |
118     //  |                blendShader_0 (108 = 1*4*27)
119     //  |                 |- 0: (1)       kSrc (1)
120     //  |                 |- 1: (4=3+1)   (dsts) linearGrad_0 (3) | solid_0 (1)
121     //  |                 |- 2: (27=3+24) (srcs) linearGrad_1 (3) |
122     //  |                                        blendShader_1 (24=1*4*6)
123     //  |                                         |- 0: (1) kDst (1)
124     //  |                                         |- 1: (4=3+1) (dsts) radGrad_0 (3) | solid_1 (1)
125     //  |                                         |- 2: (6) (srcs) imageShader_0 (6)
126     //  |
127     //  |- (4) 4-built-in-blend-modes
128 
129     PaintOptions paintOptions;
130 
131     // first, shaders. First top-level option (sweepGrad_0)
132     sk_sp<PrecompileShader> sweepGrad_0 = PrecompileShaders::SweepGradient();
133 
134     std::array<SkBlendMode, 1> blendModes{ SkBlendMode::kSrc };
135 
136     std::vector<SkBlendMode> moreBlendModes{ SkBlendMode::kDst };
137 
138     // Second top-level option (blendShader_0)
139     auto blendShader_0 = PrecompileShaders::Blend(
140                                 SkSpan<const SkBlendMode>(blendModes),          // std::array
141                                 {                                               // initializer_list
142                                     PrecompileShaders::LinearGradient(),
143                                     PrecompileShaders::Color()
144                                 },
145                                 {
146                                     PrecompileShaders::LinearGradient(),
147                                     PrecompileShaders::Blend(
148                                             SkSpan<const SkBlendMode>(moreBlendModes),// std::vector
149                                             {
150                                                 PrecompileShaders::RadialGradient(),
151                                                 PrecompileShaders::Color()
152                                             },
153                                             {
154                                                 PrecompileShaders::Image()
155                                             })
156                                 });
157 
158     paintOptions.setShaders({ sweepGrad_0, blendShader_0 });
159 
160     static const SkBlendMode kEvenMoreBlendModes[] = {
161         SkBlendMode::kSrcOver,
162         SkBlendMode::kSrc,
163         SkBlendMode::kDstOver,
164         SkBlendMode::kDst
165     };
166 
167     // now, blend modes
168     paintOptions.setBlendModes(kEvenMoreBlendModes);                             // c array
169 
170     REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == kNumExpected,
171                     "Actual # of combinations %d", paintOptions.priv().numCombinations());
172 
173     std::vector<UniquePaintParamsID> precompileIDs;
174     paintOptions.priv().buildCombinations(keyContext,
175                                           gatherer,
176                                           DrawTypeFlags::kNone,
177                                           /* withPrimitiveBlender= */ false,
178                                           Coverage::kNone,
179                                           renderPassDesc,
180                                           [&precompileIDs](UniquePaintParamsID id,
181                                                            DrawTypeFlags,
182                                                            bool /* withPrimitiveBlender */,
183                                                            Coverage,
184                                                            const RenderPassDesc&) {
185                                                                precompileIDs.push_back(id);
186                                                            });
187 
188     SkASSERT(precompileIDs.size() == kNumExpected);
189 }
190 
191 template <typename T>
create_runtime_combos(skiatest::Reporter * reporter,SkRuntimeEffect::Result effectFactory (SkString),sk_sp<T> precompileFactory (sk_sp<SkRuntimeEffect>,SkSpan<const PrecompileChildOptions> childOptions),const char * redCode,const char * greenCode,const char * blueCode,const char * combineCode)192 std::vector<sk_sp<T>> create_runtime_combos(
193         skiatest::Reporter* reporter,
194         SkRuntimeEffect::Result effectFactory(SkString),
195         sk_sp<T> precompileFactory(sk_sp<SkRuntimeEffect>,
196                                    SkSpan<const PrecompileChildOptions> childOptions),
197         const char* redCode,
198         const char* greenCode,
199         const char* blueCode,
200         const char* combineCode) {
201     auto [redEffect, error1] = effectFactory(SkString(redCode));
202     REPORTER_ASSERT(reporter, redEffect, "%s", error1.c_str());
203     auto [greenEffect, error2] = effectFactory(SkString(greenCode));
204     REPORTER_ASSERT(reporter, greenEffect, "%s", error2.c_str());
205     auto [blueEffect, error3] = effectFactory(SkString(blueCode));
206     REPORTER_ASSERT(reporter, blueEffect, "%s", error3.c_str());
207     auto [combineEffect, error4] = effectFactory(SkString(combineCode));
208     REPORTER_ASSERT(reporter, combineEffect, "%s", error4.c_str());
209 
210     sk_sp<T> red = precompileFactory(redEffect, {});
211     REPORTER_ASSERT(reporter, red);
212 
213     sk_sp<T> green = precompileFactory(greenEffect, {});
214     REPORTER_ASSERT(reporter, green);
215 
216     sk_sp<T> blue = precompileFactory(blueEffect, {});
217     REPORTER_ASSERT(reporter, blue);
218 
219     sk_sp<T> combine = precompileFactory(combineEffect, { { red, green },
220                                                           { blue, sk_sp<T>(nullptr) } });
221     REPORTER_ASSERT(reporter, combine);
222 
223     return { combine };
224 }
225 
runtime_effect_test(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const RenderPassDesc & renderPassDesc,skiatest::Reporter * reporter)226 void runtime_effect_test(const KeyContext& keyContext,
227                          PipelineDataGatherer* gatherer,
228                          const RenderPassDesc& renderPassDesc,
229                          skiatest::Reporter* reporter) {
230     // paintOptions (64 = 4*4*4)
231     //  |- combineShader (4)
232     //  |       0: redShader  | greenShader
233     //  |       1: blueShader | nullptr
234     //  |
235     //  |- combineColorFilter (4)
236     //  |       0: redColorFilter  | greenColorFilter
237     //  |       1: blueColorFilter | nullptr
238     //  |
239     //  |- combineBlender (4)
240     //  |       0: redBlender  | greenBlender
241     //  |       1: blueBlender | nullptr
242 
243     PaintOptions paintOptions;
244 
245     // shaders
246     {
247         static const char* kRedS = R"(
248             half4 main(vec2 fragcoord) { return half4(.5, 0, 0, .5); }
249         )";
250         static const char* kGreenS = R"(
251             half4 main(vec2 fragcoord) { return half4(0, .5, 0, .5); }
252         )";
253         static const char* kBlueS = R"(
254             half4 main(vec2 fragcoord) { return half4(0, 0, .5, .5); }
255         )";
256 
257         static const char* kCombineS = R"(
258             uniform shader first;
259             uniform shader second;
260             half4 main(vec2 fragcoords) {
261                 return first.eval(fragcoords) + second.eval(fragcoords);
262             }
263         )";
264 
265         std::vector<sk_sp<PrecompileShader>> combinations =
266             create_runtime_combos<PrecompileShader>(reporter,
267                                                     SkRuntimeEffect::MakeForShader,
268                                                     PrecompileRuntimeEffects::MakePrecompileShader,
269                                                     kRedS,
270                                                     kGreenS,
271                                                     kBlueS,
272                                                     kCombineS);
273         paintOptions.setShaders(combinations);
274     }
275 
276     // color filters
277     {
278         static const char* kRedCF = R"(
279             half4 main(half4 color) { return half4(.5, 0, 0, .5); }
280         )";
281         static const char* kGreenCF = R"(
282             half4 main(half4 color) { return half4(0, .5, 0, .5); }
283         )";
284         static const char* kBlueCF = R"(
285             half4 main(half4 color) { return half4(0, 0, .5, .5); }
286         )";
287 
288         static const char* kCombineCF = R"(
289             uniform colorFilter first;
290             uniform colorFilter second;
291             half4 main(half4 color) { return first.eval(color) + second.eval(color); }
292         )";
293 
294         std::vector<sk_sp<PrecompileColorFilter>> combinations =
295             create_runtime_combos<PrecompileColorFilter>(
296                     reporter,
297                     SkRuntimeEffect::MakeForColorFilter,
298                     PrecompileRuntimeEffects::MakePrecompileColorFilter,
299                     kRedCF,
300                     kGreenCF,
301                     kBlueCF,
302                     kCombineCF);
303         paintOptions.setColorFilters(combinations);
304     }
305 
306     // blenders
307     {
308         static const char* kRedB = R"(
309             half4 main(half4 src, half4 dst) { return half4(.5, 0, 0, .5); }
310         )";
311         static const char* kGreenB = R"(
312             half4 main(half4 src, half4 dst) { return half4(0, .5, 0, .5); }
313         )";
314         static const char* kBlueB = R"(
315             half4 main(half4 src, half4 dst) { return half4(0, 0, .5, .5); }
316         )";
317 
318         static const char* kCombineB = R"(
319             uniform blender first;
320             uniform blender second;
321             half4 main(half4 src, half4 dst) {
322                 return first.eval(src, dst) + second.eval(src, dst);
323             }
324         )";
325 
326         std::vector<sk_sp<PrecompileBlender>> combinations =
327             create_runtime_combos<PrecompileBlender>(
328                     reporter,
329                     SkRuntimeEffect::MakeForBlender,
330                     PrecompileRuntimeEffects::MakePrecompileBlender,
331                     kRedB,
332                     kGreenB,
333                     kBlueB,
334                     kCombineB);
335         paintOptions.setBlenders(combinations);
336     }
337 
338     REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == 64);
339 
340     std::vector<UniquePaintParamsID> precompileIDs;
341     paintOptions.priv().buildCombinations(keyContext,
342                                           gatherer,
343                                           DrawTypeFlags::kNone,
344                                           /* withPrimitiveBlender= */ false,
345                                           Coverage::kNone,
346                                           renderPassDesc,
347                                           [&precompileIDs](UniquePaintParamsID id,
348                                                            DrawTypeFlags,
349                                                            bool /* withPrimitiveBlender */,
350                                                            Coverage,
351                                                            const RenderPassDesc&) {
352                                                                precompileIDs.push_back(id);
353                                                            });
354 
355     SkASSERT(precompileIDs.size() == 64);
356 }
357 
358 // Exercise all the PrecompileBlenders factories
blend_subtest(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const RenderPassDesc & renderPassDesc,skiatest::Reporter * reporter)359 void blend_subtest(const KeyContext& keyContext,
360                    PipelineDataGatherer* gatherer,
361                    const RenderPassDesc& renderPassDesc,
362                    skiatest::Reporter* reporter) {
363     // The BlendMode PrecompileBlender only ever has 1 combination
364     {
365         PaintOptions paintOptions;
366         paintOptions.setBlenders({ PrecompileBlenders::Mode(SkBlendMode::kColorDodge) });
367 
368         run_test(keyContext, gatherer, renderPassDesc,
369                  reporter, paintOptions, /* expectedNumOptions= */ 1);
370     }
371 
372     // Specifying the BlendMode PrecompileBlender by SkBlendMode should also only ever
373     // yield 1 combination.
374     {
375         PaintOptions paintOptions;
376         paintOptions.setBlendModes({ SkBlendMode::kSrcOver });
377 
378         run_test(keyContext, gatherer, renderPassDesc,
379                  reporter, paintOptions, /* expectedNumOptions= */ 1);
380     }
381 
382     // The Arithmetic PrecompileBlender only ever has 1 combination
383     {
384         PaintOptions paintOptions;
385         paintOptions.setBlenders({ PrecompileBlenders::Arithmetic() });
386 
387         run_test(keyContext, gatherer, renderPassDesc,
388                  reporter, paintOptions, /* expectedNumOptions= */ 1);
389     }
390 }
391 
392 // Exercise all the PrecompileShaders factories
shader_subtest(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const RenderPassDesc & renderPassDesc,skiatest::Reporter * reporter)393 void shader_subtest(const KeyContext& keyContext,
394                     PipelineDataGatherer* gatherer,
395                     const RenderPassDesc& renderPassDesc,
396                     skiatest::Reporter* reporter) {
397     {
398         PaintOptions paintOptions;
399         paintOptions.setShaders({ PrecompileShaders::Empty() });
400 
401         run_test(keyContext, gatherer, renderPassDesc,
402                  reporter, paintOptions, /* expectedNumOptions= */ 1);
403     }
404 
405     // The solid color shader only ever generates one combination. Because it is constant
406     // everywhere it can cause other shaders to be elided (e.g., the LocalMatrix shader -
407     // see the LocalMatrix test(s) below).
408     {
409         PaintOptions paintOptions;
410         paintOptions.setShaders({ PrecompileShaders::Color() });
411 
412         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
413                  /* expectedNumOptions= */ kExpectedSolidColorCombos);
414     }
415 
416     // In general, the blend shader generates the product of the options in each of its slots.
417     // The rules for how many combinations the SkBlendModes yield are:
418     //   all Porter-Duff SkBlendModes collapse to one option (see GetPorterDuffBlendConstants))
419     //   all HSCL SkBlendModes collapse to another option
420     //   all other SkBlendModes produce unique options
421     {
422         const SkBlendMode kBlendModes[] = {
423                 SkBlendMode::kSrcOut,    // Porter-Duff
424                 SkBlendMode::kSrcOver,   // Porter-Duff
425                 SkBlendMode::kHue,       // HSLC
426                 SkBlendMode::kColor,     // HSLC
427                 SkBlendMode::kScreen,    // Fixed Screen
428                 SkBlendMode::kDarken,    // fixed Darken
429         };
430         PaintOptions paintOptions;
431         paintOptions.setShaders(
432                 { PrecompileShaders::Blend(SkSpan<const SkBlendMode>(kBlendModes),
433                                            { PrecompileShaders::Color() },
434                                            { PrecompileShaders::MakeFractalNoise() }) });
435 
436         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
437                  /* expectedNumOptions= */ 4 *  // Porter-Duff, HSLC, Screen, Darken
438                                            kExpectedSolidColorCombos *
439                                            kExpectedPerlinNoiseCombos);
440     }
441 
442     // The ImageShaders have 6 combinations (3 sampling/tiling x 2 alpha/non-alpha)
443     // The CoordClamp shader doesn't add any additional combinations to its wrapped shader.
444     {
445         PaintOptions paintOptions;
446         paintOptions.setShaders({ PrecompileShaders::CoordClamp({ PrecompileShaders::Image() }) });
447 
448         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
449                  /* expectedNumOptions= */ kExpectedImageCombos);
450     }
451 
452     // RawImageShaders only have 3 combinations (since they never incorporate alpha)
453     {
454         PaintOptions paintOptions;
455         paintOptions.setShaders({ PrecompileShaders::RawImage() });
456 
457         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
458                  /* expectedNumOptions= */ kExpectedRawImageCombos);
459     }
460 
461     // Each Perlin noise shader only has one combination
462     {
463         PaintOptions paintOptions;
464         paintOptions.setShaders({ PrecompileShaders::MakeFractalNoise(),
465                                   PrecompileShaders::MakeTurbulence() });
466 
467         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
468                  /* expectedNumOptions= */ kExpectedPerlinNoiseCombos + kExpectedPerlinNoiseCombos);
469     }
470 
471     // Each gradient shader generates 3 combinations
472     {
473         PaintOptions paintOptions;
474         paintOptions.setShaders({ PrecompileShaders::LinearGradient(),
475                                   PrecompileShaders::RadialGradient(),
476                                   PrecompileShaders::TwoPointConicalGradient(),
477                                   PrecompileShaders::SweepGradient() });
478 
479         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
480                  /* expectedNumOptions= */ kExpectedGradientCombos + kExpectedGradientCombos +
481                                            kExpectedGradientCombos + kExpectedGradientCombos);
482     }
483 
484     // Each picture shader generates 12 combinations:
485     //    2 (pictureShader LM) x 6 (imageShader variations)
486     {
487         PaintOptions paintOptions;
488         paintOptions.setShaders({ PrecompileShaders::Picture() });
489 
490         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
491                  /* expectedNumOptions= */ kExpectedPictureCombos);
492     }
493 
494     // In general, the local matrix shader just generates however many options its wrapped
495     // shader generates.
496     {
497         PaintOptions paintOptions;
498         paintOptions.setShaders(
499                 { PrecompileShaders::LocalMatrix({ PrecompileShaders::LinearGradient() }) });
500 
501         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
502                  /* expectedNumOptions= */ kExpectedGradientCombos);
503     }
504 
505     // The ColorFilter shader just creates the cross product of its child options
506     {
507         PaintOptions paintOptions;
508         paintOptions.setShaders(
509                 { PrecompileShaders::ColorFilter({ PrecompileShaders::LinearGradient() },
510                                                  { PrecompileColorFilters::Blend() }) });
511 
512         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
513                  /* expectedNumOptions= */ kExpectedGradientCombos * kExpectedBlendCFCombos);
514     }
515 
516     {
517         PaintOptions paintOptions;
518         paintOptions.setShaders(
519                 { PrecompileShaders::WorkingColorSpace({ PrecompileShaders::LinearGradient() },
520                                                        { SkColorSpace::MakeSRGBLinear() }) });
521 
522         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
523                  /* expectedNumOptions= */ kExpectedGradientCombos *
524                                            1 /* only one colorSpace */);
525     }
526 }
527 
528 // Exercise all the PrecompileColorFilters factories. The impact of colorfilters on the number
529 // of combinations is very predictable. Except for the Compose and Lerp color filters, all the
530 // color filters only ever have one combination. The Compose and Lerp color filters also just
531 // simply generate the cross product of their children.
colorfilter_subtest(const KeyContext & keyContext,PipelineDataGatherer * gatherer,const RenderPassDesc & renderPassDesc,skiatest::Reporter * reporter)532 void colorfilter_subtest(const KeyContext& keyContext,
533                          PipelineDataGatherer* gatherer,
534                          const RenderPassDesc& renderPassDesc,
535                          skiatest::Reporter* reporter) {
536 
537 
538     {
539         // The compose colorfilter just generates the cross product of its children.
540         static constexpr int kExpectedNumOptions =
541                 kExpectedTableCFCombos * (kExpectedHighContrastCFCombos + kExpectedLumaCFCombos) +
542                 kExpectedLightingCFCombos * (kExpectedHighContrastCFCombos + kExpectedLumaCFCombos);
543 
544         PaintOptions paintOptions;
545         paintOptions.setColorFilters(
546             { PrecompileColorFilters::Compose(
547                     { PrecompileColorFilters::Table(), PrecompileColorFilters::Lighting() },
548                     { PrecompileColorFilters::HighContrast(), PrecompileColorFilters::Luma() }) });
549 
550         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions, kExpectedNumOptions);
551     }
552 
553     {
554         PaintOptions paintOptions;
555         paintOptions.setColorFilters({ PrecompileColorFilters::Blend() });
556 
557         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
558                  kExpectedBlendCFCombos);
559     }
560 
561     {
562         PaintOptions paintOptions;
563         paintOptions.setColorFilters({ PrecompileColorFilters::Matrix(),
564                                        PrecompileColorFilters::HSLAMatrix() });
565 
566         // HSLAMatrix and Matrix map to the same color filter
567         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
568                  kExpectedMatrixCFCombos + kExpectedMatrixCFCombos);
569     }
570 
571     {
572         PaintOptions paintOptions;
573         paintOptions.setColorFilters({ PrecompileColorFilters::LinearToSRGBGamma(),
574                                        PrecompileColorFilters::SRGBToLinearGamma() });
575 
576         // LinearToSRGB and SRGBToLinear both map to the colorspace colorfilter
577         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions,
578                  kExpectedColorSpaceCFCombos + kExpectedColorSpaceCFCombos);
579     }
580 
581     {
582         // The lerp colorfilter just generates the cross product of its children.
583         static constexpr int kExpectedNumOptions =
584                 kExpectedMatrixCFCombos * (kExpectedBlendCFCombos + kExpectedOverdrawCFCombos) +
585                 kExpectedLumaCFCombos * (kExpectedBlendCFCombos + kExpectedOverdrawCFCombos);
586 
587         PaintOptions paintOptions;
588         paintOptions.setColorFilters(
589             { PrecompileColorFilters::Lerp(
590                     { PrecompileColorFilters::Matrix(), PrecompileColorFilters::Luma() },
591                     { PrecompileColorFilters::Blend(), PrecompileColorFilters::Overdraw() }) });
592 
593         run_test(keyContext, gatherer, renderPassDesc, reporter, paintOptions, kExpectedNumOptions);
594     }
595 }
596 
597 } // anonymous namespace
598 
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(CombinationBuilderTest,reporter,context,CtsEnforcement::kNextRelease)599 DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(CombinationBuilderTest, reporter, context,
600                                    CtsEnforcement::kNextRelease) {
601     ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
602 
603     auto rtEffectDict = std::make_unique<RuntimeEffectDictionary>();
604 
605     SkColorInfo ci(kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
606     KeyContext keyContext(context->priv().caps(), dict, rtEffectDict.get(), ci);
607 
608     PipelineDataGatherer gatherer(Layout::kMetal);
609 
610     RenderPassDesc unusedRenderPassDesc;
611 
612     // The default PaintOptions should create a single combination with a solid color shader and
613     // kSrcOver blending
614     {
615         PaintOptions paintOptions;
616 
617         run_test(keyContext, &gatherer, unusedRenderPassDesc, reporter, paintOptions,
618                  /* expectedNumOptions= */ 1);
619     }
620 
621     blend_subtest(keyContext, &gatherer, unusedRenderPassDesc, reporter);
622     shader_subtest(keyContext, &gatherer, unusedRenderPassDesc, reporter);
623     colorfilter_subtest(keyContext, &gatherer, unusedRenderPassDesc, reporter);
624 
625     no_blend_mode_option_test(keyContext, &gatherer, unusedRenderPassDesc, reporter);
626     big_test(keyContext, &gatherer, unusedRenderPassDesc, reporter);
627     runtime_effect_test(keyContext, &gatherer, unusedRenderPassDesc, reporter);
628 }
629 
630 #endif // SK_GRAPHITE
631