• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/gpu/graphite/FactoryFunctions.h"
9 
10 #include "src/core/SkRuntimeEffectPriv.h"
11 #include "src/gpu/graphite/KeyContext.h"
12 #include "src/gpu/graphite/KeyHelpers.h"
13 #include "src/gpu/graphite/PaintParamsKey.h"
14 #include "src/gpu/graphite/Precompile.h"
15 #include "src/gpu/graphite/PrecompileBasePriv.h"
16 #include "src/shaders/SkShaderBase.h"
17 
18 namespace skgpu::graphite {
19 
20 //--------------------------------------------------------------------------------------------------
21 class PrecompileBlendModeBlender : public PrecompileBlender {
22 public:
PrecompileBlendModeBlender(SkBlendMode blendMode)23     PrecompileBlendModeBlender(SkBlendMode blendMode) : fBlendMode(blendMode) {}
24 
asBlendMode() const25     std::optional<SkBlendMode> asBlendMode() const final { return fBlendMode; }
26 
27 private:
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const28     void addToKey(const KeyContext& keyContext,
29                   int desiredCombination,
30                   PaintParamsKeyBuilder* builder) const override {
31         SkASSERT(desiredCombination == 0); // The blend mode blender only ever has one combination
32 
33         // The blend mode is used in this BeginBlock! It is used to choose between fixed function
34         // and shader-based blending
35         BlendModeBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, fBlendMode);
36         builder->endBlock();
37     }
38 
39 
40     SkBlendMode fBlendMode;
41 };
42 
Mode(SkBlendMode blendMode)43 sk_sp<PrecompileBlender> PrecompileBlender::Mode(SkBlendMode blendMode) {
44     return sk_make_sp<PrecompileBlendModeBlender>(blendMode);
45 }
46 
47 //--------------------------------------------------------------------------------------------------
48 class PrecompileColorShader : public PrecompileShader {
49 public:
PrecompileColorShader()50     PrecompileColorShader() {}
51 
52 private:
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const53     void addToKey(const KeyContext& keyContext,
54                   int desiredCombination,
55                   PaintParamsKeyBuilder* builder) const override {
56 
57         SkASSERT(desiredCombination == 0); // The color shader only ever has one combination
58 
59         constexpr SkPMColor4f kUnusedColor = { 1, 0, 0, 1 };
60 
61         SolidColorShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr,
62                                           kUnusedColor); // color isn't used w/o a gatherer
63         builder->endBlock();
64     }
65 
66 };
67 
Color()68 sk_sp<PrecompileShader> PrecompileShaders::Color() {
69     return sk_make_sp<PrecompileColorShader>();
70 }
71 
72 //--------------------------------------------------------------------------------------------------
73 class PrecompileBlendShader : public PrecompileShader {
74 public:
PrecompileBlendShader(SkSpan<const sk_sp<PrecompileBlender>> runtimeBlendEffects,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs,bool needsPorterDuffBased,bool needsSeparableMode)75     PrecompileBlendShader(SkSpan<const sk_sp<PrecompileBlender>> runtimeBlendEffects,
76                           SkSpan<const sk_sp<PrecompileShader>> dsts,
77                           SkSpan<const sk_sp<PrecompileShader>> srcs,
78                           bool needsPorterDuffBased,
79                           bool needsSeparableMode)
80             : fRuntimeBlendEffects(runtimeBlendEffects.begin(), runtimeBlendEffects.end())
81             , fDstOptions(dsts.begin(), dsts.end())
82             , fSrcOptions(srcs.begin(), srcs.end()) {
83 
84         fNumBlenderCombos = 0;
85         for (auto rt : fRuntimeBlendEffects) {
86             fNumBlenderCombos += rt->numCombinations();
87         }
88         if (needsPorterDuffBased) {
89             ++fNumBlenderCombos;
90         }
91         if (needsSeparableMode) {
92             ++fNumBlenderCombos;
93         }
94 
95         SkASSERT(fNumBlenderCombos >= 1);
96 
97         fNumDstCombos = 0;
98         for (auto d : fDstOptions) {
99             fNumDstCombos += d->numCombinations();
100         }
101 
102         fNumSrcCombos = 0;
103         for (auto s : fSrcOptions) {
104             fNumSrcCombos += s->numCombinations();
105         }
106 
107         if (needsPorterDuffBased) {
108             fPorterDuffIndex = 0;
109             if (needsSeparableMode) {
110                 fSeparableModeIndex = 1;
111                 if (!fRuntimeBlendEffects.empty()) {
112                     fBlenderIndex = 2;
113                 }
114             } else if (!fRuntimeBlendEffects.empty()) {
115                 fBlenderIndex = 1;
116             }
117         } else if (needsSeparableMode) {
118             fSeparableModeIndex = 0;
119             if (!fRuntimeBlendEffects.empty()) {
120                 fBlenderIndex = 1;
121             }
122         } else {
123             SkASSERT(!fRuntimeBlendEffects.empty());
124             fBlenderIndex = 0;
125         }
126     }
127 
128 private:
numChildCombinations() const129     int numChildCombinations() const override {
130         return fNumBlenderCombos * fNumDstCombos * fNumSrcCombos;
131     }
132 
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const133     void addToKey(const KeyContext& keyContext,
134                   int desiredCombination,
135                   PaintParamsKeyBuilder* builder) const override {
136         SkASSERT(desiredCombination < this->numCombinations());
137 
138         const int desiredDstCombination = desiredCombination % fNumDstCombos;
139         int remainingCombinations = desiredCombination / fNumDstCombos;
140 
141         const int desiredSrcCombination = remainingCombinations % fNumSrcCombos;
142         remainingCombinations /= fNumSrcCombos;
143 
144         int desiredBlendCombination = remainingCombinations;
145         SkASSERT(desiredBlendCombination < fNumBlenderCombos);
146 
147         if (desiredBlendCombination == fPorterDuffIndex) {
148             PorterDuffBlendShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr,
149                                                    {}); // Porter/Duff coeffs aren't used
150         } else if (desiredBlendCombination == fSeparableModeIndex) {
151             BlendShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr,
152                                          { SkBlendMode::kOverlay }); // the blendmode is unused
153         } else {
154             // TODO: share this with the copy over in SkComposeShader.cpp. For now, the block ID is
155             // determined by a hash of the code so both copies will generate the same key and
156             // the SkPaint vs. PaintOptions key match testing will trigger if they get out of
157             // sync.
158             static SkRuntimeEffect* sBlendEffect = SkMakeRuntimeEffect(
159                     SkRuntimeEffect::MakeForShader,
160                     "uniform blender b;"
161                     "uniform shader d, s;"
162                     "half4 main(float2 xy) {"
163                         "return b.eval(s.eval(xy), d.eval(xy));"
164                     "}"
165             );
166 
167             RuntimeEffectBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr,
168                                            { sk_ref_sp(sBlendEffect) });
169 
170             SkASSERT(desiredBlendCombination >= fBlenderIndex);
171             desiredBlendCombination -= fBlenderIndex;
172 
173             AddToKey<PrecompileBlender>(keyContext, builder, fRuntimeBlendEffects,
174                                         desiredBlendCombination);
175         }
176 
177         AddToKey<PrecompileShader>(keyContext, builder, fDstOptions, desiredDstCombination);
178         AddToKey<PrecompileShader>(keyContext, builder, fSrcOptions, desiredSrcCombination);
179 
180         builder->endBlock();
181     }
182 
183     std::vector<sk_sp<PrecompileBlender>> fRuntimeBlendEffects;
184     std::vector<sk_sp<PrecompileShader>> fDstOptions;
185     std::vector<sk_sp<PrecompileShader>> fSrcOptions;
186 
187     int fNumBlenderCombos;
188     int fNumDstCombos;
189     int fNumSrcCombos;
190 
191     int fPorterDuffIndex = -1;
192     int fSeparableModeIndex = -1;
193     int fBlenderIndex = -1;
194 };
195 
Blend(SkSpan<const sk_sp<PrecompileBlender>> blenders,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs)196 sk_sp<PrecompileShader> PrecompileShaders::Blend(
197         SkSpan<const sk_sp<PrecompileBlender>> blenders,
198         SkSpan<const sk_sp<PrecompileShader>> dsts,
199         SkSpan<const sk_sp<PrecompileShader>> srcs) {
200     std::vector<sk_sp<PrecompileBlender>> tmp;
201     tmp.reserve(blenders.size());
202 
203     bool needsPorterDuffBased = false;
204     bool needsBlendModeBased = false;
205 
206     for (auto b : blenders) {
207         if (!b) {
208             needsPorterDuffBased = true; // fall back to kSrcOver
209         } else if (b->asBlendMode().has_value()) {
210             SkBlendMode bm = b->asBlendMode().value();
211 
212             if (bm <= SkBlendMode::kLastCoeffMode) {
213                 needsPorterDuffBased = true;
214             } else {
215                 needsBlendModeBased = true;
216             }
217         } else {
218             tmp.push_back(b);
219         }
220     }
221 
222     if (!needsPorterDuffBased && !needsBlendModeBased && tmp.empty()) {
223         needsPorterDuffBased = true; // fallback to kSrcOver
224     }
225 
226     return sk_make_sp<PrecompileBlendShader>(SkSpan<const sk_sp<PrecompileBlender>>(tmp),
227                                              dsts, srcs,
228                                              needsPorterDuffBased, needsBlendModeBased);
229 }
230 
Blend(SkSpan<SkBlendMode> blendModes,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs)231 sk_sp<PrecompileShader> PrecompileShaders::Blend(
232         SkSpan<SkBlendMode> blendModes,
233         SkSpan<const sk_sp<PrecompileShader>> dsts,
234         SkSpan<const sk_sp<PrecompileShader>> srcs) {
235 
236     bool needsPorterDuffBased = false;
237     bool needsBlendModeBased = false;
238 
239     for (SkBlendMode bm : blendModes) {
240         SkSpan<const float> porterDuffConstants = skgpu::GetPorterDuffBlendConstants(bm);
241         if (!porterDuffConstants.empty()) {
242             needsPorterDuffBased = true;
243         } else {
244             needsBlendModeBased = true;
245         }
246     }
247 
248     if (!needsPorterDuffBased && !needsBlendModeBased) {
249         needsPorterDuffBased = true; // fallback to kSrcOver
250     }
251 
252     return sk_make_sp<PrecompileBlendShader>(SkSpan<const sk_sp<PrecompileBlender>>(),
253                                              dsts, srcs,
254                                              needsPorterDuffBased, needsBlendModeBased);
255 }
256 
257 //--------------------------------------------------------------------------------------------------
258 class PrecompileImageShader : public PrecompileShader {
259 public:
PrecompileImageShader()260     PrecompileImageShader() {}
261 
262 private:
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const263     void addToKey(const KeyContext& keyContext,
264                   int desiredCombination,
265                   PaintParamsKeyBuilder* builder) const override {
266         SkASSERT(desiredCombination == 0);
267 
268         ImageShaderBlock::BeginBlock(keyContext, builder,
269                                      /* gatherer= */ nullptr, /* imgData= */ nullptr);
270         builder->endBlock();
271     }
272 };
273 
Image()274 sk_sp<PrecompileShader> PrecompileShaders::Image() {
275     return sk_make_sp<PrecompileImageShader>();
276 }
277 
278 //--------------------------------------------------------------------------------------------------
279 class PrecompileGradientShader : public PrecompileShader {
280 public:
PrecompileGradientShader(SkShaderBase::GradientType type)281     PrecompileGradientShader(SkShaderBase::GradientType type) : fType(type) {}
282 
283 private:
284     /*
285      * The gradients currently have two specializations based on the number of stops.
286      */
287     inline static constexpr int kNumStopVariants = 2;
288     inline static constexpr int kStopVariants[kNumStopVariants] = { 4, 8 };
289 
numIntrinsicCombinations() const290     int numIntrinsicCombinations() const override {
291         return kNumStopVariants;
292     }
293 
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const294     void addToKey(const KeyContext& keyContext,
295                   int desiredCombination,
296                   PaintParamsKeyBuilder* builder) const override {
297         const int intrinsicCombination = desiredCombination / this->numChildCombinations();
298         SkDEBUGCODE(int childCombination = desiredCombination % this->numChildCombinations();)
299         SkASSERT(intrinsicCombination < kNumStopVariants);
300         SkASSERT(childCombination == 0);
301 
302         // Only the type and number of stops are accessed when there is no gatherer
303         GradientShaderBlocks::GradientData gradData(fType, kStopVariants[intrinsicCombination]);
304 
305         // TODO: we may need SkLocalMatrixShader-wrapped versions too
306         ColorFilterShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr);
307             GradientShaderBlocks::BeginBlock(keyContext, builder,
308                                              /* gatherer= */ nullptr, gradData);
309             builder->endBlock();
310 
311             ColorSpaceTransformBlock::BeginBlock(keyContext, builder,
312                                                  /* gatherer= */ nullptr, /* data= */ nullptr);
313             builder->endBlock();
314         builder->endBlock();
315     }
316 
317     SkShaderBase::GradientType fType;
318 };
319 
LinearGradient()320 sk_sp<PrecompileShader> PrecompileShaders::LinearGradient() {
321     return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kLinear);
322 }
323 
RadialGradient()324 sk_sp<PrecompileShader> PrecompileShaders::RadialGradient() {
325     return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kRadial);
326 }
327 
SweepGradient()328 sk_sp<PrecompileShader> PrecompileShaders::SweepGradient() {
329     return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kSweep);
330 }
331 
TwoPointConicalGradient()332 sk_sp<PrecompileShader> PrecompileShaders::TwoPointConicalGradient() {
333     return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kConical);
334 }
335 
336 //--------------------------------------------------------------------------------------------------
337 class PrecompileLocalMatrixShader : public PrecompileShader {
338 public:
PrecompileLocalMatrixShader(sk_sp<PrecompileShader> wrapped)339     PrecompileLocalMatrixShader(sk_sp<PrecompileShader> wrapped) : fWrapped(std::move(wrapped)) {}
340 
341 private:
isALocalMatrixShader() const342     bool isALocalMatrixShader() const override { return true; }
343 
numChildCombinations() const344     int numChildCombinations() const override {
345         return fWrapped->numChildCombinations();
346     }
347 
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const348     void addToKey(const KeyContext& keyContext,
349                   int desiredCombination,
350                   PaintParamsKeyBuilder* builder) const override {
351         SkASSERT(desiredCombination < fWrapped->numCombinations());
352 
353         LocalMatrixShaderBlock::BeginBlock(keyContext, builder,
354                                            /* gatherer= */ nullptr, /* lmShaderData= */ nullptr);
355 
356         fWrapped->priv().addToKey(keyContext, desiredCombination, builder);
357 
358         builder->endBlock();
359     }
360 
361     sk_sp<PrecompileShader> fWrapped;
362 };
363 
LocalMatrix(sk_sp<PrecompileShader> wrapped)364 sk_sp<PrecompileShader> PrecompileShaders::LocalMatrix(sk_sp<PrecompileShader> wrapped) {
365     return sk_make_sp<PrecompileLocalMatrixShader>(std::move(wrapped));
366 }
367 
368 //--------------------------------------------------------------------------------------------------
369 class PrecompileColorFilterShader : public PrecompileShader {
370 public:
PrecompileColorFilterShader(sk_sp<PrecompileShader> shader,sk_sp<PrecompileColorFilter> cf)371     PrecompileColorFilterShader(sk_sp<PrecompileShader> shader, sk_sp<PrecompileColorFilter> cf)
372             : fShader(std::move(shader))
373             , fColorFilter(std::move(cf)) {}
374 
375 private:
numChildCombinations() const376     int numChildCombinations() const override {
377         const int numShaderCombos = fShader->numCombinations();
378         const int numColorFilterCombos = fColorFilter->numCombinations();
379 
380         return numShaderCombos * numColorFilterCombos;
381     }
382 
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const383     void addToKey(const KeyContext& keyContext,
384                   int desiredCombination,
385                   PaintParamsKeyBuilder* builder) const override {
386 
387         SkASSERT(desiredCombination < this->numCombinations());
388 
389         const int numShaderCombos = fShader->numCombinations();
390         SkDEBUGCODE(int numColorFilterCombos = fColorFilter->numCombinations();)
391 
392         int desiredShaderCombination = desiredCombination % numShaderCombos;
393         int desiredColorFilterCombination = desiredCombination / numShaderCombos;
394         SkASSERT(desiredColorFilterCombination < numColorFilterCombos);
395 
396         ColorFilterShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr);
397 
398         fShader->priv().addToKey(keyContext, desiredShaderCombination, builder);
399         fColorFilter->priv().addToKey(keyContext, desiredColorFilterCombination, builder);
400 
401         builder->endBlock();
402     }
403 
404     sk_sp<PrecompileShader> fShader;
405     sk_sp<PrecompileColorFilter> fColorFilter;
406 };
407 
ColorFilter(sk_sp<PrecompileShader> shader,sk_sp<PrecompileColorFilter> cf)408 sk_sp<PrecompileShader> PrecompileShaders::ColorFilter(sk_sp<PrecompileShader> shader,
409                                                        sk_sp<PrecompileColorFilter> cf) {
410     return sk_make_sp<PrecompileColorFilterShader>(std::move(shader), std::move(cf));
411 }
412 
413 //--------------------------------------------------------------------------------------------------
414 class PrecompileBlurMaskFilter : public PrecompileMaskFilter {
415 public:
PrecompileBlurMaskFilter()416     PrecompileBlurMaskFilter() {}
417 
418 private:
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const419     void addToKey(const KeyContext& keyContext,
420                   int desiredCombination,
421                   PaintParamsKeyBuilder* builder) const override {
422         SkASSERT(desiredCombination == 0);
423 
424         // TODO: need to add a BlurMaskFilter Block. This is somewhat blocked on figuring out
425         // what we're going to do with the Blur system.
426     }
427 };
428 
Blur()429 sk_sp<PrecompileMaskFilter> PrecompileMaskFilters::Blur() {
430     return sk_make_sp<PrecompileBlurMaskFilter>();
431 }
432 
433 //--------------------------------------------------------------------------------------------------
434 class PrecompileBlendColorFilter : public PrecompileColorFilter {
435 public:
PrecompileBlendColorFilter()436     PrecompileBlendColorFilter() {}
437 
438 private:
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const439     void addToKey(const KeyContext& keyContext,
440                   int desiredCombination,
441                   PaintParamsKeyBuilder* builder) const override {
442         SkASSERT(desiredCombination == 0);
443 
444         BlendColorFilterBlock::BeginBlock(keyContext,
445                                           builder,
446                                           /* gatherer= */ nullptr,
447                                           /* blendCFData= */ nullptr);
448         builder->endBlock();
449     }
450 };
451 
Blend()452 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Blend() {
453     return sk_make_sp<PrecompileBlendColorFilter>();
454 }
455 
456 //--------------------------------------------------------------------------------------------------
457 class PrecompileMatrixColorFilter : public PrecompileColorFilter {
458 public:
PrecompileMatrixColorFilter()459     PrecompileMatrixColorFilter() {}
460 
461 private:
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const462     void addToKey(const KeyContext& keyContext,
463                   int desiredCombination,
464                   PaintParamsKeyBuilder* builder) const override {
465         SkASSERT(desiredCombination == 0);
466 
467         MatrixColorFilterBlock::BeginBlock(keyContext, builder,
468                                            /* gatherer= */ nullptr,
469                                            /* matrixCFData= */ nullptr);
470         builder->endBlock();
471     }
472 };
473 
Matrix()474 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Matrix() {
475     return sk_make_sp<PrecompileMatrixColorFilter>();
476 }
477 
HSLAMatrix()478 sk_sp<PrecompileColorFilter> PrecompileColorFilters::HSLAMatrix() {
479     return sk_make_sp<PrecompileMatrixColorFilter>();
480 }
481 
482 //--------------------------------------------------------------------------------------------------
483 // TODO: need to figure out how we're going to decompose ImageFilters
Blur()484 sk_sp<PrecompileImageFilter> PrecompileImageFilters::Blur() {
485     return nullptr; // sk_make_sp<PrecompileImageFilter>();
486 }
487 
Image()488 sk_sp<PrecompileImageFilter> PrecompileImageFilters::Image() {
489     return nullptr; // sk_make_sp<PrecompileImageFilter>();
490 }
491 
492 //--------------------------------------------------------------------------------------------------
PrecompileChildPtr(sk_sp<PrecompileShader> s)493 PrecompileChildPtr::PrecompileChildPtr(sk_sp<PrecompileShader> s) : fChild(std::move(s)) {}
PrecompileChildPtr(sk_sp<PrecompileColorFilter> cf)494 PrecompileChildPtr::PrecompileChildPtr(sk_sp<PrecompileColorFilter> cf)
495         : fChild(std::move(cf)) {
496 }
PrecompileChildPtr(sk_sp<PrecompileBlender> b)497 PrecompileChildPtr::PrecompileChildPtr(sk_sp<PrecompileBlender> b) : fChild(std::move(b)) {}
498 
499 namespace {
500 
501 #ifdef SK_DEBUG
502 
precompilebase_is_valid_as_child(const PrecompileBase * child)503 bool precompilebase_is_valid_as_child(const PrecompileBase *child) {
504     if (!child) {
505         return true;
506     }
507 
508     switch (child->type()) {
509         case PrecompileBase::Type::kShader:
510         case PrecompileBase::Type::kColorFilter:
511         case PrecompileBase::Type::kBlender:
512             return true;
513         default:
514             return false;
515     }
516 }
517 
518 #endif // SK_DEBUG
519 
520 } // anonymous namespace
521 
PrecompileChildPtr(sk_sp<PrecompileBase> child)522 PrecompileChildPtr::PrecompileChildPtr(sk_sp<PrecompileBase> child)
523         : fChild(std::move(child)) {
524     SkASSERT(precompilebase_is_valid_as_child(fChild.get()));
525 }
526 
type() const527 std::optional<SkRuntimeEffect::ChildType> PrecompileChildPtr::type() const {
528     if (fChild) {
529         switch (fChild->type()) {
530             case PrecompileBase::Type::kShader:
531                 return SkRuntimeEffect::ChildType::kShader;
532             case PrecompileBase::Type::kColorFilter:
533                 return SkRuntimeEffect::ChildType::kColorFilter;
534             case PrecompileBase::Type::kBlender:
535                 return SkRuntimeEffect::ChildType::kBlender;
536             default:
537                 break;
538         }
539     }
540     return std::nullopt;
541 }
542 
shader() const543 PrecompileShader* PrecompileChildPtr::shader() const {
544     return (fChild && fChild->type() == PrecompileBase::Type::kShader)
545            ? static_cast<PrecompileShader*>(fChild.get())
546            : nullptr;
547 }
548 
colorFilter() const549 PrecompileColorFilter* PrecompileChildPtr::colorFilter() const {
550     return (fChild && fChild->type() == PrecompileBase::Type::kColorFilter)
551            ? static_cast<PrecompileColorFilter*>(fChild.get())
552            : nullptr;
553 }
554 
blender() const555 PrecompileBlender* PrecompileChildPtr::blender() const {
556     return (fChild && fChild->type() == PrecompileBase::Type::kBlender)
557            ? static_cast<PrecompileBlender*>(fChild.get())
558            : nullptr;
559 }
560 
561 //--------------------------------------------------------------------------------------------------
562 namespace {
563 
num_options_in_set(const std::vector<PrecompileChildPtr> & optionSet)564 int num_options_in_set(const std::vector<PrecompileChildPtr>& optionSet) {
565     int numOptions = 1;
566     for (const PrecompileChildPtr& childOption : optionSet) {
567         // A missing child will fall back to a passthrough object
568         if (childOption.base()) {
569             numOptions *= childOption.base()->numCombinations();
570         }
571     }
572 
573     return numOptions;
574 }
575 
576 // This is the precompile correlate to SkRuntimeEffect.cpp's add_children_to_key
add_children_to_key(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder,const std::vector<PrecompileChildPtr> & optionSet,SkSpan<const SkRuntimeEffect::Child> childInfo)577 void add_children_to_key(const KeyContext& keyContext,
578                          int desiredCombination,
579                          PaintParamsKeyBuilder* builder,
580                          const std::vector<PrecompileChildPtr>& optionSet,
581                          SkSpan<const SkRuntimeEffect::Child> childInfo) {
582     using ChildType = SkRuntimeEffect::ChildType;
583 
584     SkASSERT(optionSet.size() == childInfo.size());
585 
586     int remainingCombinations = desiredCombination;
587 
588     for (size_t index = 0; index < optionSet.size(); ++index) {
589         const PrecompileChildPtr& childOption = optionSet[index];
590 
591         const int numChildCombos = childOption.base() ? childOption.base()->numCombinations()
592                                                       : 1;
593         const int curCombo = remainingCombinations % numChildCombos;
594         remainingCombinations /= numChildCombos;
595 
596         std::optional<ChildType> type = childOption.type();
597         if (type == ChildType::kShader) {
598             childOption.shader()->priv().addToKey(keyContext, curCombo, builder);
599         } else if (type == ChildType::kColorFilter) {
600             childOption.colorFilter()->priv().addToKey(keyContext, curCombo, builder);
601         } else if (type == ChildType::kBlender) {
602             childOption.blender()->priv().addToKey(keyContext, curCombo, builder);
603         } else {
604             SkASSERT(curCombo == 0);
605 
606             // We don't have a child effect. Substitute in a no-op effect.
607             switch (childInfo[index].type) {
608                 case ChildType::kShader:
609                 case ChildType::kColorFilter:
610                     // A "passthrough" shader returns the input color as-is.
611                     PassthroughShaderBlock::BeginBlock(keyContext, builder,
612                                                        /* gatherer= */ nullptr);
613                     builder->endBlock();
614                     break;
615 
616                 case ChildType::kBlender:
617                     // A "passthrough" blender performs `blend_src_over(src, dest)`.
618                     PassthroughBlenderBlock::BeginBlock(keyContext, builder,
619                                                         /* gatherer= */ nullptr);
620                     builder->endBlock();
621                     break;
622             }
623         }
624     }
625 }
626 
627 } // anonymous namespace
628 
629 template<typename T>
630 class PrecompileRTEffect : public T {
631 public:
PrecompileRTEffect(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)632     PrecompileRTEffect(sk_sp<SkRuntimeEffect> effect,
633                        SkSpan<const PrecompileChildOptions> childOptions)
634             : fEffect(std::move(effect)) {
635         fChildOptions.reserve(childOptions.size());
636         for (PrecompileChildOptions c : childOptions) {
637             fChildOptions.push_back({ c.begin(), c.end() });
638         }
639     }
640 
641 private:
numChildCombinations() const642     int numChildCombinations() const override {
643         int numOptions = 0;
644         for (const std::vector<PrecompileChildPtr>& optionSet : fChildOptions) {
645             numOptions += num_options_in_set(optionSet);
646         }
647 
648         return numOptions ? numOptions : 1;
649     }
650 
addToKey(const KeyContext & keyContext,int desiredCombination,PaintParamsKeyBuilder * builder) const651     void addToKey(const KeyContext& keyContext,
652                   int desiredCombination,
653                   PaintParamsKeyBuilder* builder) const override {
654 
655         SkASSERT(desiredCombination < this->numCombinations());
656 
657         SkSpan<const SkRuntimeEffect::Child> childInfo = fEffect->children();
658 
659         RuntimeEffectBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, { fEffect });
660 
661         for (const std::vector<PrecompileChildPtr>& optionSet : fChildOptions) {
662             int numOptionsInSet = num_options_in_set(optionSet);
663 
664             if (desiredCombination < numOptionsInSet) {
665                 add_children_to_key(keyContext, desiredCombination, builder, optionSet, childInfo);
666                 break;
667             }
668 
669             desiredCombination -= numOptionsInSet;
670         }
671 
672         builder->endBlock();
673     }
674 
675     sk_sp<SkRuntimeEffect> fEffect;
676     std::vector<std::vector<PrecompileChildPtr>> fChildOptions;
677 };
678 
MakePrecompileShader(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)679 sk_sp<PrecompileShader> MakePrecompileShader(
680         sk_sp<SkRuntimeEffect> effect,
681         SkSpan<const PrecompileChildOptions> childOptions) {
682     // TODO: check that 'effect' has the kAllowShader_Flag bit set and:
683     //  for each entry in childOptions:
684     //    all the SkPrecompileChildPtrs have the same type as the corresponding child in the effect
685     return sk_make_sp<PrecompileRTEffect<PrecompileShader>>(std::move(effect), childOptions);
686 }
687 
MakePrecompileColorFilter(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)688 sk_sp<PrecompileColorFilter> MakePrecompileColorFilter(
689         sk_sp<SkRuntimeEffect> effect,
690         SkSpan<const PrecompileChildOptions> childOptions) {
691     // TODO: check that 'effect' has the kAllowColorFilter_Flag bit set and:
692     //  for each entry in childOptions:
693     //    all the SkPrecompileChildPtrs have the same type as the corresponding child in the effect
694     return sk_make_sp<PrecompileRTEffect<PrecompileColorFilter>>(std::move(effect),
695                                                                  childOptions);
696 }
697 
MakePrecompileBlender(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)698 sk_sp<PrecompileBlender> MakePrecompileBlender(
699         sk_sp<SkRuntimeEffect> effect,
700         SkSpan<const PrecompileChildOptions> childOptions) {
701     // TODO: check that 'effect' has the kAllowBlender_Flag bit set and:
702     //  for each entry in childOptions:
703     //    all the SkPrecompileChildPtrs have the same type as the corresponding child in the effect
704     return sk_make_sp<PrecompileRTEffect<PrecompileBlender>>(std::move(effect), childOptions);
705 }
706 
707 } // namespace skgpu::graphite
708 
709 //--------------------------------------------------------------------------------------------------
710