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