• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 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 "include/gpu/graphite/precompile/PrecompileShader.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "include/gpu/graphite/precompile/PrecompileBlender.h"
13 #include "src/core/SkColorSpacePriv.h"
14 #include "src/core/SkKnownRuntimeEffects.h"
15 #include "src/gpu/Blend.h"
16 #include "src/gpu/graphite/BuiltInCodeSnippetID.h"
17 #include "src/gpu/graphite/FactoryFunctionsPriv.h"
18 #include "src/gpu/graphite/KeyContext.h"
19 #include "src/gpu/graphite/KeyHelpers.h"
20 #include "src/gpu/graphite/PaintParams.h"
21 #include "src/gpu/graphite/PaintParamsKey.h"
22 #include "src/gpu/graphite/PrecompileInternal.h"
23 #include "src/gpu/graphite/ReadSwizzle.h"
24 #include "src/gpu/graphite/precompile/PrecompileBaseComplete.h"
25 #include "src/gpu/graphite/precompile/PrecompileBasePriv.h"
26 #include "src/gpu/graphite/precompile/PrecompileBlenderPriv.h"
27 #include "src/gpu/graphite/precompile/PrecompileShaderPriv.h"
28 
29 namespace skgpu::graphite {
30 
31 PrecompileShader::~PrecompileShader() = default;
32 
makeWithLocalMatrix()33 sk_sp<PrecompileShader> PrecompileShader::makeWithLocalMatrix() {
34     if (this->priv().isALocalMatrixShader()) {
35         // SkShader::makeWithLocalMatrix collapses chains of localMatrix shaders so we need to
36         // follow suit here
37         return sk_ref_sp(this);
38     }
39 
40     return PrecompileShaders::LocalMatrix({ sk_ref_sp(this) });
41 }
42 
makeWithColorFilter(sk_sp<PrecompileColorFilter> cf)43 sk_sp<PrecompileShader> PrecompileShader::makeWithColorFilter(sk_sp<PrecompileColorFilter> cf) {
44     if (!cf) {
45         return sk_ref_sp(this);
46     }
47 
48     return PrecompileShaders::ColorFilter({ sk_ref_sp(this) }, { std::move(cf) });
49 }
50 
makeWithWorkingColorSpace(sk_sp<SkColorSpace> cs)51 sk_sp<PrecompileShader> PrecompileShader::makeWithWorkingColorSpace(sk_sp<SkColorSpace> cs) {
52     if (!cs) {
53         return sk_ref_sp(this);
54     }
55 
56     return PrecompileShaders::WorkingColorSpace({ sk_ref_sp(this) }, { std::move(cs) });
57 }
58 
59 //--------------------------------------------------------------------------------------------------
60 class PrecompileEmptyShader final : public PrecompileShader {
61 private:
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const62     void addToKey(const KeyContext& keyContext,
63                   PaintParamsKeyBuilder* builder,
64                   PipelineDataGatherer* gatherer,
65                   int desiredCombination) const override {
66 
67         SkASSERT(desiredCombination == 0); // The empty shader only ever has one combination
68 
69         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
70     }
71 };
72 
Empty()73 sk_sp<PrecompileShader> PrecompileShaders::Empty() {
74     return sk_make_sp<PrecompileEmptyShader>();
75 }
76 
77 //--------------------------------------------------------------------------------------------------
78 class PrecompileColorShader final : public PrecompileShader {
79 private:
isConstant(int desiredCombination) const80     bool isConstant(int desiredCombination) const override {
81         SkASSERT(desiredCombination == 0); // The color shader only ever has one combination
82         return true;
83     }
84 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const85     void addToKey(const KeyContext& keyContext,
86                   PaintParamsKeyBuilder* builder,
87                   PipelineDataGatherer* gatherer,
88                   int desiredCombination) const override {
89 
90         SkASSERT(desiredCombination == 0); // The color shader only ever has one combination
91 
92         // The white PMColor is just a placeholder for the actual paint params color
93         SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, SK_PMColor4fWHITE);
94     }
95 };
96 
Color()97 sk_sp<PrecompileShader> PrecompileShaders::Color() {
98     return sk_make_sp<PrecompileColorShader>();
99 }
100 
101 // The colorSpace is safe to ignore - it is just applied to the color and doesn't modify the
102 // generated program.
Color(sk_sp<SkColorSpace>)103 sk_sp<PrecompileShader> PrecompileShaders::Color(sk_sp<SkColorSpace>) {
104     return sk_make_sp<PrecompileColorShader>();
105 }
106 
107 //--------------------------------------------------------------------------------------------------
108 class PrecompileBlendShader final : public PrecompileShader {
109 public:
PrecompileBlendShader(SkSpan<const sk_sp<PrecompileBlender>> runtimeBlendEffects,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs,bool needsPorterDuffBased,bool needsSeparableMode)110     PrecompileBlendShader(SkSpan<const sk_sp<PrecompileBlender>> runtimeBlendEffects,
111                           SkSpan<const sk_sp<PrecompileShader>> dsts,
112                           SkSpan<const sk_sp<PrecompileShader>> srcs,
113                           bool needsPorterDuffBased,
114                           bool needsSeparableMode)
115             : fRuntimeBlendEffects(runtimeBlendEffects.begin(), runtimeBlendEffects.end())
116             , fDstOptions(dsts.begin(), dsts.end())
117             , fSrcOptions(srcs.begin(), srcs.end()) {
118 
119         fNumBlenderCombos = 0;
120         for (const auto& rt : fRuntimeBlendEffects) {
121             fNumBlenderCombos += rt->priv().numCombinations();
122         }
123         if (needsPorterDuffBased) {
124             ++fNumBlenderCombos;
125         }
126         if (needsSeparableMode) {
127             ++fNumBlenderCombos;
128         }
129 
130         SkASSERT(fNumBlenderCombos >= 1);
131 
132         fNumDstCombos = 0;
133         for (const auto& d : fDstOptions) {
134             fNumDstCombos += d->priv().numCombinations();
135         }
136 
137         fNumSrcCombos = 0;
138         for (const auto& s : fSrcOptions) {
139             fNumSrcCombos += s->priv().numCombinations();
140         }
141 
142         if (needsPorterDuffBased) {
143             fPorterDuffIndex = 0;
144             if (needsSeparableMode) {
145                 fSeparableModeIndex = 1;
146                 if (!fRuntimeBlendEffects.empty()) {
147                     fBlenderIndex = 2;
148                 }
149             } else if (!fRuntimeBlendEffects.empty()) {
150                 fBlenderIndex = 1;
151             }
152         } else if (needsSeparableMode) {
153             fSeparableModeIndex = 0;
154             if (!fRuntimeBlendEffects.empty()) {
155                 fBlenderIndex = 1;
156             }
157         } else {
158             SkASSERT(!fRuntimeBlendEffects.empty());
159             fBlenderIndex = 0;
160         }
161     }
162 
163 private:
numChildCombinations() const164     int numChildCombinations() const override {
165         return fNumBlenderCombos * fNumDstCombos * fNumSrcCombos;
166     }
167 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const168     void addToKey(const KeyContext& keyContext,
169                   PaintParamsKeyBuilder* builder,
170                   PipelineDataGatherer* gatherer,
171                   int desiredCombination) const override {
172         SkASSERT(desiredCombination < this->numCombinations());
173 
174         const int desiredDstCombination = desiredCombination % fNumDstCombos;
175         int remainingCombinations = desiredCombination / fNumDstCombos;
176 
177         const int desiredSrcCombination = remainingCombinations % fNumSrcCombos;
178         remainingCombinations /= fNumSrcCombos;
179 
180         int desiredBlendCombination = remainingCombinations;
181         SkASSERT(desiredBlendCombination < fNumBlenderCombos);
182 
183         if (desiredBlendCombination == fPorterDuffIndex ||
184             desiredBlendCombination == fSeparableModeIndex) {
185             BlendShaderBlock::BeginBlock(keyContext, builder, gatherer);
186 
187         } else {
188             const SkRuntimeEffect* blendEffect =
189                     GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kBlend);
190 
191             RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
192                                            { sk_ref_sp(blendEffect) });
193             SkASSERT(desiredBlendCombination >= fBlenderIndex);
194             desiredBlendCombination -= fBlenderIndex;
195         }
196 
197         AddToKey<PrecompileShader>(keyContext, builder, gatherer, fSrcOptions,
198                                    desiredSrcCombination);
199         AddToKey<PrecompileShader>(keyContext, builder, gatherer, fDstOptions,
200                                    desiredDstCombination);
201 
202         if (desiredBlendCombination == fPorterDuffIndex) {
203             CoeffBlenderBlock::AddBlock(keyContext, builder, gatherer,
204                                         { 0.0f, 0.0f, 0.0f, 0.0f }); // coeffs aren't used
205         } else if (desiredBlendCombination == fSeparableModeIndex) {
206             BlendModeBlenderBlock::AddBlock(keyContext, builder, gatherer,
207                                             SkBlendMode::kOverlay); // the blendmode is unused
208         } else {
209             AddToKey<PrecompileBlender>(keyContext, builder, gatherer, fRuntimeBlendEffects,
210                                         desiredBlendCombination);
211         }
212 
213         builder->endBlock();  // BlendShaderBlock or RuntimeEffectBlock
214     }
215 
216     std::vector<sk_sp<PrecompileBlender>> fRuntimeBlendEffects;
217     std::vector<sk_sp<PrecompileShader>> fDstOptions;
218     std::vector<sk_sp<PrecompileShader>> fSrcOptions;
219 
220     int fNumBlenderCombos;
221     int fNumDstCombos;
222     int fNumSrcCombos;
223 
224     int fPorterDuffIndex = -1;
225     int fSeparableModeIndex = -1;
226     int fBlenderIndex = -1;
227 };
228 
Blend(SkSpan<const sk_sp<PrecompileBlender>> blenders,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs)229 sk_sp<PrecompileShader> PrecompileShaders::Blend(
230         SkSpan<const sk_sp<PrecompileBlender>> blenders,
231         SkSpan<const sk_sp<PrecompileShader>> dsts,
232         SkSpan<const sk_sp<PrecompileShader>> srcs) {
233     std::vector<sk_sp<PrecompileBlender>> tmp;
234     tmp.reserve(blenders.size());
235 
236     bool needsPorterDuffBased = false;
237     bool needsBlendModeBased = false;
238 
239     for (const auto& b : blenders) {
240         if (!b) {
241             needsPorterDuffBased = true; // fall back to kSrcOver
242         } else if (b->priv().asBlendMode().has_value()) {
243             SkBlendMode bm = b->priv().asBlendMode().value();
244 
245             SkSpan<const float> coeffs = skgpu::GetPorterDuffBlendConstants(bm);
246             if (!coeffs.empty()) {
247                 needsPorterDuffBased = true;
248             } else {
249                 needsBlendModeBased = true;
250             }
251         } else {
252             tmp.push_back(b);
253         }
254     }
255 
256     if (!needsPorterDuffBased && !needsBlendModeBased && tmp.empty()) {
257         needsPorterDuffBased = true; // fallback to kSrcOver
258     }
259 
260     return sk_make_sp<PrecompileBlendShader>(SkSpan<const sk_sp<PrecompileBlender>>(tmp),
261                                              dsts, srcs,
262                                              needsPorterDuffBased, needsBlendModeBased);
263 }
264 
Blend(SkSpan<const SkBlendMode> blendModes,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs)265 sk_sp<PrecompileShader> PrecompileShaders::Blend(
266         SkSpan<const SkBlendMode> blendModes,
267         SkSpan<const sk_sp<PrecompileShader>> dsts,
268         SkSpan<const sk_sp<PrecompileShader>> srcs) {
269 
270     bool needsPorterDuffBased = false;
271     bool needsBlendModeBased = false;
272 
273     for (SkBlendMode bm : blendModes) {
274         SkSpan<const float> porterDuffConstants = skgpu::GetPorterDuffBlendConstants(bm);
275         if (!porterDuffConstants.empty()) {
276             needsPorterDuffBased = true;
277         } else {
278             needsBlendModeBased = true;
279         }
280     }
281 
282     if (!needsPorterDuffBased && !needsBlendModeBased) {
283         needsPorterDuffBased = true; // fallback to kSrcOver
284     }
285 
286     return sk_make_sp<PrecompileBlendShader>(SkSpan<const sk_sp<PrecompileBlender>>(),
287                                              dsts, srcs,
288                                              needsPorterDuffBased, needsBlendModeBased);
289 }
290 
291 //--------------------------------------------------------------------------------------------------
292 class PrecompileCoordClampShader final : public PrecompileShader {
293 public:
PrecompileCoordClampShader(SkSpan<const sk_sp<PrecompileShader>> shaders)294     PrecompileCoordClampShader(SkSpan<const sk_sp<PrecompileShader>> shaders)
295             : fShaders(shaders.begin(), shaders.end()) {
296         fNumShaderCombos = 0;
297         for (const auto& s : fShaders) {
298             fNumShaderCombos += s->priv().numCombinations();
299         }
300     }
301 
302 private:
numChildCombinations() const303     int numChildCombinations() const override {
304         return fNumShaderCombos;
305     }
306 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const307     void addToKey(const KeyContext& keyContext,
308                   PaintParamsKeyBuilder* builder,
309                   PipelineDataGatherer* gatherer,
310                   int desiredCombination) const override {
311         SkASSERT(desiredCombination < fNumShaderCombos);
312 
313         constexpr SkRect kIgnored { 0, 0, 256, 256 }; // ignored bc we're precompiling
314 
315         // TODO: update CoordClampShaderBlock so this is optional
316         CoordClampShaderBlock::CoordClampData data(kIgnored);
317 
318         CoordClampShaderBlock::BeginBlock(keyContext, builder, gatherer, data);
319             AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders, desiredCombination);
320         builder->endBlock();
321     }
322 
323     std::vector<sk_sp<PrecompileShader>> fShaders;
324     int fNumShaderCombos;
325 };
326 
CoordClamp(SkSpan<const sk_sp<PrecompileShader>> input)327 sk_sp<PrecompileShader> PrecompileShaders::CoordClamp(SkSpan<const sk_sp<PrecompileShader>> input) {
328     return sk_make_sp<PrecompileCoordClampShader>(input);
329 }
330 
331 //--------------------------------------------------------------------------------------------------
332 // TODO: Investigate the YUV-image use case
333 class PrecompileImageShader final : public PrecompileShader {
334 public:
PrecompileImageShader(SkEnumBitMask<PrecompileImageShaderFlags> flags)335     PrecompileImageShader(SkEnumBitMask<PrecompileImageShaderFlags> flags) : fFlags(flags) {}
336 
337 private:
338     // The ImageShader has 3 potential sampling/tiling variants: hardware-tiled, shader-tiled and
339     // cubic sampling (which always uses shader-tiling)
340     inline static constexpr int kNumSamplingTilingCombos = 3;
341     inline static constexpr int kCubicSampled = 2;
342     inline static constexpr int kHWTiled      = 1;
343     inline static constexpr int kShaderTiled  = 0;
344 
345     // There are also 2 potential alpha combinations: alpha-only and not-alpha-only
346     inline static constexpr int kNumAlphaCombinations = 2;
347     inline static constexpr int kAlphaOnly    = 1;
348     inline static constexpr int kNonAlphaOnly = 0;
349 
numIntrinsicCombinations() const350     int numIntrinsicCombinations() const override {
351         int numSamplingTilingCombos =
352                 (fFlags & PrecompileImageShaderFlags::kExcludeCubic) ? 2 : kNumSamplingTilingCombos;
353 
354         if (fFlags & PrecompileImageShaderFlags::kExcludeAlpha) {
355             // RawImageShaders don't blend alpha-only images w/ the paint color
356             return numSamplingTilingCombos;
357         }
358         return numSamplingTilingCombos * kNumAlphaCombinations;
359     }
360 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const361     void addToKey(const KeyContext& keyContext,
362                   PaintParamsKeyBuilder* builder,
363                   PipelineDataGatherer* gatherer,
364                   int desiredCombination) const override {
365         SkASSERT(desiredCombination < this->numIntrinsicCombinations());
366 
367         int desiredAlphaCombo, desiredSamplingTilingCombo;
368 
369         if (fFlags & PrecompileImageShaderFlags::kExcludeAlpha) {
370             desiredAlphaCombo = kNonAlphaOnly;
371             desiredSamplingTilingCombo = desiredCombination;
372         } else {
373             desiredAlphaCombo = desiredCombination % kNumAlphaCombinations;
374             desiredSamplingTilingCombo = desiredCombination / kNumAlphaCombinations;
375         }
376         SkDEBUGCODE(int numSamplingTilingCombos =
377             (fFlags & PrecompileImageShaderFlags::kExcludeCubic) ? 2 : kNumSamplingTilingCombos;)
378         SkASSERT(desiredSamplingTilingCombo < numSamplingTilingCombos);
379 
380         static constexpr SkSamplingOptions kDefaultCubicSampling(SkCubicResampler::Mitchell());
381         static constexpr SkSamplingOptions kDefaultSampling;
382         constexpr ReadSwizzle kIgnoredSwizzle = ReadSwizzle::kRGBA;
383 
384         // ImageShaderBlock will use hardware tiling when the subset covers the entire image, so we
385         // create subset + image size combinations where subset == imgSize (for a shader that uses
386         // hardware tiling) and subset < imgSize (for a shader that does shader-based tiling).
387         static constexpr SkRect kSubset = SkRect::MakeWH(1.0f, 1.0f);
388         static constexpr SkISize kHWTileableSize = SkISize::Make(1, 1);
389         static constexpr SkISize kShaderTileableSize = SkISize::Make(2, 2);
390 
391         ImageShaderBlock::ImageData imgData(
392                 desiredSamplingTilingCombo == kCubicSampled ? kDefaultCubicSampling
393                                                             : kDefaultSampling,
394                 SkTileMode::kClamp, SkTileMode::kClamp,
395                 desiredSamplingTilingCombo == kHWTiled ? kHWTileableSize : kShaderTileableSize,
396                 kSubset, kIgnoredSwizzle);
397 
398         if (desiredAlphaCombo == kAlphaOnly) {
399             SkASSERT(!(fFlags & PrecompileImageShaderFlags::kExcludeAlpha));
400 
401             Blend(keyContext, builder, gatherer,
402                   /* addBlendToKey= */ [&] () -> void {
403                       AddKnownModeBlend(keyContext, builder, gatherer, SkBlendMode::kDstIn);
404                   },
405                   /* addSrcToKey= */ [&] () -> void {
406                       ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
407                   },
408                   /* addDstToKey= */ [&]() -> void {
409                       RGBPaintColorBlock::AddBlock(keyContext, builder, gatherer);
410                   });
411         } else {
412             ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
413         }
414     }
415 
416     SkEnumBitMask<PrecompileImageShaderFlags> fFlags;
417 };
418 
Image()419 sk_sp<PrecompileShader> PrecompileShaders::Image() {
420     return PrecompileShaders::LocalMatrix(
421             { sk_make_sp<PrecompileImageShader>(PrecompileImageShaderFlags::kNone) });
422 }
423 
RawImage()424 sk_sp<PrecompileShader> PrecompileShaders::RawImage() {
425     // Raw images do not perform color space conversion, but in Graphite, this is represented as
426     // an identity color space xform, not as a distinct shader
427     return PrecompileShaders::LocalMatrix(
428             { sk_make_sp<PrecompileImageShader>(PrecompileImageShaderFlags::kExcludeAlpha) });
429 }
430 
Image(SkEnumBitMask<PrecompileImageShaderFlags> flags)431 sk_sp<PrecompileShader> PrecompileShadersPriv::Image(
432         SkEnumBitMask<PrecompileImageShaderFlags> flags) {
433     return PrecompileShaders::LocalMatrix({ sk_make_sp<PrecompileImageShader>(flags) });
434 }
435 
RawImage(SkEnumBitMask<PrecompileImageShaderFlags> flags)436 sk_sp<PrecompileShader> PrecompileShadersPriv::RawImage(
437         SkEnumBitMask<PrecompileImageShaderFlags> flags) {
438     return PrecompileShaders::LocalMatrix(
439             { sk_make_sp<PrecompileImageShader>(flags |
440                                                 PrecompileImageShaderFlags::kExcludeAlpha) });
441 }
442 
443 //--------------------------------------------------------------------------------------------------
444 class PrecompilePerlinNoiseShader final : public PrecompileShader {
445 public:
PrecompilePerlinNoiseShader()446     PrecompilePerlinNoiseShader() {}
447 
448 private:
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const449     void addToKey(const KeyContext& keyContext,
450                   PaintParamsKeyBuilder* builder,
451                   PipelineDataGatherer* gatherer,
452                   int desiredCombination) const override {
453 
454         SkASSERT(desiredCombination == 0); // The Perlin noise shader only ever has one combination
455 
456         // TODO: update PerlinNoiseShaderBlock so the NoiseData is optional
457         static const PerlinNoiseShaderBlock::PerlinNoiseData kIgnoredNoiseData(
458                 PerlinNoiseShaderBlock::Type::kFractalNoise, { 0.0f, 0.0f }, 2, {1, 1});
459 
460         PerlinNoiseShaderBlock::AddBlock(keyContext, builder, gatherer, kIgnoredNoiseData);
461     }
462 
463 };
464 
MakeFractalNoise()465 sk_sp<PrecompileShader> PrecompileShaders::MakeFractalNoise() {
466     return sk_make_sp<PrecompilePerlinNoiseShader>();
467 }
468 
MakeTurbulence()469 sk_sp<PrecompileShader> PrecompileShaders::MakeTurbulence() {
470     return sk_make_sp<PrecompilePerlinNoiseShader>();
471 }
472 
473 //--------------------------------------------------------------------------------------------------
474 class PrecompileGradientShader final : public PrecompileShader {
475 public:
PrecompileGradientShader(SkShaderBase::GradientType type)476     PrecompileGradientShader(SkShaderBase::GradientType type) : fType(type) {}
477 
478 private:
479     /*
480      * The gradients currently have two specializations based on the number of stops.
481      */
482     inline static constexpr int kNumStopVariants = 2;
483     inline static constexpr int kStopVariants[kNumStopVariants] = { 4, 8 };
484 
numIntrinsicCombinations() const485     int numIntrinsicCombinations() const override {
486         return kNumStopVariants;
487     }
488 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const489     void addToKey(const KeyContext& keyContext,
490                   PaintParamsKeyBuilder* builder,
491                   PipelineDataGatherer* gatherer,
492                   int desiredCombination) const override {
493         const int intrinsicCombination = desiredCombination / this->numChildCombinations();
494         SkDEBUGCODE(int childCombination = desiredCombination % this->numChildCombinations();)
495         SkASSERT(intrinsicCombination < kNumStopVariants);
496         SkASSERT(childCombination == 0);
497 
498         GradientShaderBlocks::GradientData gradData(fType, kStopVariants[intrinsicCombination]);
499 
500         constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType;
501         ColorSpaceTransformBlock::ColorSpaceTransformData csData(sk_srgb_singleton(), kAlphaType,
502                                                                  sk_srgb_singleton(), kAlphaType);
503 
504         Compose(keyContext, builder, gatherer,
505                 /* addInnerToKey= */ [&]() -> void {
506                     GradientShaderBlocks::AddBlock(keyContext, builder, gatherer, gradData);
507                 },
508                 /* addOuterToKey= */  [&]() -> void {
509                     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData);
510                 });
511     }
512 
513     SkShaderBase::GradientType fType;
514 };
515 
LinearGradient()516 sk_sp<PrecompileShader> PrecompileShaders::LinearGradient() {
517     sk_sp<PrecompileShader> s =
518             sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kLinear);
519     return PrecompileShaders::LocalMatrix({ std::move(s) });
520 }
521 
RadialGradient()522 sk_sp<PrecompileShader> PrecompileShaders::RadialGradient() {
523     sk_sp<PrecompileShader> s =
524             sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kRadial);
525     return PrecompileShaders::LocalMatrix({ std::move(s) });
526 }
527 
SweepGradient()528 sk_sp<PrecompileShader> PrecompileShaders::SweepGradient() {
529     sk_sp<PrecompileShader> s =
530             sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kSweep);
531     return PrecompileShaders::LocalMatrix({ std::move(s) });
532 }
533 
TwoPointConicalGradient()534 sk_sp<PrecompileShader> PrecompileShaders::TwoPointConicalGradient() {
535     sk_sp<PrecompileShader> s =
536             sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kConical);
537     return PrecompileShaders::LocalMatrix({ std::move(s) });
538 }
539 
540 //--------------------------------------------------------------------------------------------------
541 // The PictureShader ultimately turns into an SkImageShader optionally wrapped in a
542 // LocalMatrixShader. The PrecompileImageShader already captures that use case so just reuse it.
543 // Note that this means each precompile PictureShader will add 24 combinations:
544 //    2 (pictureshader LM) x 2 (imageShader LM) x 6 (imageShader variations)
Picture()545 sk_sp<PrecompileShader> PrecompileShaders::Picture() {
546     // Note: We don't need to consider the PrecompileYUVImageShader since the image
547     // being drawn was created internally by Skia (as non-YUV).
548     return PrecompileShadersPriv::LocalMatrixBothVariants({ PrecompileShaders::Image() });
549 }
550 
Picture(bool withLM)551 sk_sp<PrecompileShader> PrecompileShadersPriv::Picture(bool withLM) {
552     sk_sp<PrecompileShader> s = PrecompileShaders::Image();
553     if (withLM) {
554         return PrecompileShaders::LocalMatrix({ std::move(s) });
555     }
556     return s;
557 }
558 
559 //--------------------------------------------------------------------------------------------------
560 // In the main Skia API the SkLocalMatrixShader is optimized away when the LM is the identity
561 // or omitted. The PrecompileLocalMatrixShader captures this by adding two intrinsic options.
562 // One with the LMShader wrapping the child and one without the LMShader.
563 class PrecompileLocalMatrixShader final : public PrecompileShader {
564 public:
565     enum class Flags {
566         kNone,
567         kIncludeWithOutVariant,
568     };
569 
PrecompileLocalMatrixShader(SkSpan<const sk_sp<PrecompileShader>> wrapped,Flags flags=Flags::kNone)570     PrecompileLocalMatrixShader(SkSpan<const sk_sp<PrecompileShader>> wrapped,
571                                 Flags flags = Flags::kNone)
572             : fWrapped(wrapped.begin(), wrapped.end())
573             , fFlags(flags) {
574         fNumWrappedCombos = 0;
575         for (const auto& s : fWrapped) {
576             fNumWrappedCombos += s->priv().numCombinations();
577         }
578     }
579 
isConstant(int desiredCombination) const580     bool isConstant(int desiredCombination) const override {
581         SkASSERT(desiredCombination < this->numCombinations());
582 
583         /*
584          * Regardless of whether the LocalMatrixShader elides itself or not, we always want
585          * the Constant-ness of the wrapped shader.
586          */
587         int desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations;
588         SkASSERT(desiredWrappedCombination < fNumWrappedCombos);
589 
590         std::pair<sk_sp<PrecompileShader>, int> wrapped =
591                 PrecompileBase::SelectOption(SkSpan(fWrapped), desiredWrappedCombination);
592         if (wrapped.first) {
593             return wrapped.first->priv().isConstant(wrapped.second);
594         }
595 
596         return false;
597     }
598 
599 private:
600     // The LocalMatrixShader has two potential variants: with and without the LocalMatrixShader
601     inline static constexpr int kNumIntrinsicCombinations = 2;
602     inline static constexpr int kWithLocalMatrix    = 1;
603     inline static constexpr int kWithoutLocalMatrix = 0;
604 
isALocalMatrixShader() const605     bool isALocalMatrixShader() const override { return true; }
606 
numIntrinsicCombinations() const607     int numIntrinsicCombinations() const override {
608         if (fFlags != Flags::kIncludeWithOutVariant) {
609             return 1;   // just kWithLocalMatrix
610         }
611         return kNumIntrinsicCombinations;
612     }
613 
numChildCombinations() const614     int numChildCombinations() const override { return fNumWrappedCombos; }
615 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const616     void addToKey(const KeyContext& keyContext,
617                   PaintParamsKeyBuilder* builder,
618                   PipelineDataGatherer* gatherer,
619                   int desiredCombination) const override {
620         SkASSERT(desiredCombination < this->numCombinations());
621 
622         int desiredLMCombination, desiredWrappedCombination;
623 
624         if (fFlags != Flags::kIncludeWithOutVariant) {
625             desiredLMCombination = kWithLocalMatrix;
626             desiredWrappedCombination = desiredCombination;
627         } else {
628             desiredLMCombination = desiredCombination % kNumIntrinsicCombinations;
629             desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations;
630         }
631         SkASSERT(desiredWrappedCombination < fNumWrappedCombos);
632 
633         if (desiredLMCombination == kWithLocalMatrix) {
634             LocalMatrixShaderBlock::LMShaderData kIgnoredLMShaderData(SkMatrix::I());
635 
636             LocalMatrixShaderBlock::BeginBlock(keyContext, builder, gatherer, kIgnoredLMShaderData);
637         }
638 
639             AddToKey<PrecompileShader>(keyContext, builder, gatherer, fWrapped,
640                                        desiredWrappedCombination);
641 
642         if (desiredLMCombination == kWithLocalMatrix) {
643             builder->endBlock();
644         }
645     }
646 
647     std::vector<sk_sp<PrecompileShader>> fWrapped;
648     int fNumWrappedCombos;
649     Flags fFlags;
650 };
651 
LocalMatrix(SkSpan<const sk_sp<PrecompileShader>> wrapped)652 sk_sp<PrecompileShader> PrecompileShaders::LocalMatrix(
653         SkSpan<const sk_sp<PrecompileShader>> wrapped) {
654     return sk_make_sp<PrecompileLocalMatrixShader>(std::move(wrapped));
655 }
656 
LocalMatrixBothVariants(SkSpan<const sk_sp<PrecompileShader>> wrapped)657 sk_sp<PrecompileShader> PrecompileShadersPriv::LocalMatrixBothVariants(
658         SkSpan<const sk_sp<PrecompileShader>> wrapped) {
659     return sk_make_sp<PrecompileLocalMatrixShader>(
660             std::move(wrapped),
661             PrecompileLocalMatrixShader::Flags::kIncludeWithOutVariant);
662 }
663 
664 //--------------------------------------------------------------------------------------------------
665 class PrecompileColorFilterShader final : public PrecompileShader {
666 public:
PrecompileColorFilterShader(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters)667     PrecompileColorFilterShader(SkSpan<const sk_sp<PrecompileShader>> shaders,
668                                 SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters)
669             : fShaders(shaders.begin(), shaders.end())
670             , fColorFilters(colorFilters.begin(), colorFilters.end()) {
671         fNumShaderCombos = 0;
672         for (const auto& s : fShaders) {
673             fNumShaderCombos += s->priv().numCombinations();
674         }
675         fNumColorFilterCombos = 0;
676         for (const auto& cf : fColorFilters) {
677             fNumColorFilterCombos += cf->priv().numCombinations();
678         }
679     }
680 
681 private:
numChildCombinations() const682     int numChildCombinations() const override { return fNumShaderCombos * fNumColorFilterCombos; }
683 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const684     void addToKey(const KeyContext& keyContext,
685                   PaintParamsKeyBuilder* builder,
686                   PipelineDataGatherer* gatherer,
687                   int desiredCombination) const override {
688         SkASSERT(desiredCombination < this->numCombinations());
689 
690         int desiredShaderCombination = desiredCombination % fNumShaderCombos;
691         int desiredColorFilterCombination = desiredCombination / fNumShaderCombos;
692         SkASSERT(desiredColorFilterCombination < fNumColorFilterCombos);
693 
694         Compose(keyContext, builder, gatherer,
695                 /* addInnerToKey= */ [&]() -> void {
696                     AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders,
697                                                desiredShaderCombination);
698                 },
699                 /* addOuterToKey= */ [&]() -> void {
700                     AddToKey<PrecompileColorFilter>(keyContext, builder, gatherer, fColorFilters,
701                                                     desiredColorFilterCombination);
702                 });
703     }
704 
705     std::vector<sk_sp<PrecompileShader>>      fShaders;
706     std::vector<sk_sp<PrecompileColorFilter>> fColorFilters;
707     int fNumShaderCombos;
708     int fNumColorFilterCombos;
709 };
710 
ColorFilter(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters)711 sk_sp<PrecompileShader> PrecompileShaders::ColorFilter(
712         SkSpan<const sk_sp<PrecompileShader>> shaders,
713         SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters) {
714     return sk_make_sp<PrecompileColorFilterShader>(std::move(shaders), std::move(colorFilters));
715 }
716 
717 //--------------------------------------------------------------------------------------------------
718 class PrecompileWorkingColorSpaceShader final : public PrecompileShader {
719 public:
PrecompileWorkingColorSpaceShader(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<SkColorSpace>> colorSpaces)720     PrecompileWorkingColorSpaceShader(SkSpan<const sk_sp<PrecompileShader>> shaders,
721                                       SkSpan<const sk_sp<SkColorSpace>> colorSpaces)
722             : fShaders(shaders.begin(), shaders.end())
723             , fColorSpaces(colorSpaces.begin(), colorSpaces.end()) {
724         fNumShaderCombos = 0;
725         for (const auto& s : fShaders) {
726             fNumShaderCombos += s->priv().numCombinations();
727         }
728     }
729 
730 private:
numChildCombinations() const731     int numChildCombinations() const override { return fNumShaderCombos * fColorSpaces.size(); }
732 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const733     void addToKey(const KeyContext& keyContext,
734                   PaintParamsKeyBuilder* builder,
735                   PipelineDataGatherer* gatherer,
736                   int desiredCombination) const override {
737         SkASSERT(desiredCombination < this->numCombinations());
738 
739         int desiredShaderCombination = desiredCombination % fNumShaderCombos;
740         int desiredColorSpaceCombination = desiredCombination / fNumShaderCombos;
741         SkASSERT(desiredColorSpaceCombination < (int) fColorSpaces.size());
742 
743         const SkColorInfo& dstInfo = keyContext.dstColorInfo();
744         const SkAlphaType dstAT = dstInfo.alphaType();
745         sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace();
746         if (!dstCS) {
747             dstCS = SkColorSpace::MakeSRGB();
748         }
749 
750         sk_sp<SkColorSpace> workingCS = fColorSpaces[desiredColorSpaceCombination];
751         SkColorInfo workingInfo(dstInfo.colorType(), dstAT, workingCS);
752         KeyContextWithColorInfo workingContext(keyContext, workingInfo);
753 
754         Compose(keyContext, builder, gatherer,
755                 /* addInnerToKey= */ [&]() -> void {
756                     AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders,
757                                                desiredShaderCombination);
758                 },
759                 /* addOuterToKey= */ [&]() -> void {
760                     ColorSpaceTransformBlock::ColorSpaceTransformData data(
761                             workingCS.get(), dstAT, dstCS.get(), dstAT);
762                     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
763                 });
764     }
765 
766     std::vector<sk_sp<PrecompileShader>> fShaders;
767     std::vector<sk_sp<SkColorSpace>>     fColorSpaces;
768     int fNumShaderCombos;
769 };
770 
WorkingColorSpace(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<SkColorSpace>> colorSpaces)771 sk_sp<PrecompileShader> PrecompileShaders::WorkingColorSpace(
772         SkSpan<const sk_sp<PrecompileShader>> shaders,
773         SkSpan<const sk_sp<SkColorSpace>> colorSpaces) {
774     return sk_make_sp<PrecompileWorkingColorSpaceShader>(std::move(shaders),
775                                                          std::move(colorSpaces));
776 }
777 
778 //--------------------------------------------------------------------------------------------------
779 // In Graphite this acts as a non-elidable LocalMatrixShader
780 class PrecompileCTMShader final : public PrecompileShader {
781 public:
PrecompileCTMShader(SkSpan<const sk_sp<PrecompileShader>> wrapped)782     PrecompileCTMShader(SkSpan<const sk_sp<PrecompileShader>> wrapped)
783             : fWrapped(wrapped.begin(), wrapped.end()) {
784         fNumWrappedCombos = 0;
785         for (const auto& s : fWrapped) {
786             fNumWrappedCombos += s->priv().numCombinations();
787         }
788     }
789 
isConstant(int desiredCombination) const790     bool isConstant(int desiredCombination) const override {
791         SkASSERT(desiredCombination < fNumWrappedCombos);
792 
793         std::pair<sk_sp<PrecompileShader>, int> wrapped =
794                 PrecompileBase::SelectOption(SkSpan(fWrapped), desiredCombination);
795         if (wrapped.first) {
796             return wrapped.first->priv().isConstant(wrapped.second);
797         }
798 
799         return false;
800     }
801 
802 private:
numChildCombinations() const803     int numChildCombinations() const override { return fNumWrappedCombos; }
804 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const805     void addToKey(const KeyContext& keyContext,
806                   PaintParamsKeyBuilder* builder,
807                   PipelineDataGatherer* gatherer,
808                   int desiredCombination) const override {
809         SkASSERT(desiredCombination < fNumWrappedCombos);
810 
811         LocalMatrixShaderBlock::LMShaderData kIgnoredLMShaderData(SkMatrix::I());
812 
813         LocalMatrixShaderBlock::BeginBlock(keyContext, builder, gatherer, kIgnoredLMShaderData);
814 
815             AddToKey<PrecompileShader>(keyContext, builder, gatherer, fWrapped, desiredCombination);
816 
817         builder->endBlock();
818     }
819 
820     std::vector<sk_sp<PrecompileShader>> fWrapped;
821     int fNumWrappedCombos;
822 };
823 
CTM(SkSpan<const sk_sp<PrecompileShader>> wrapped)824 sk_sp<PrecompileShader> PrecompileShadersPriv::CTM(SkSpan<const sk_sp<PrecompileShader>> wrapped) {
825     return sk_make_sp<PrecompileCTMShader>(std::move(wrapped));
826 }
827 
828 //--------------------------------------------------------------------------------------------------
829 class PrecompileBlurShader final : public PrecompileShader {
830 public:
PrecompileBlurShader(sk_sp<PrecompileShader> wrapped)831     PrecompileBlurShader(sk_sp<PrecompileShader> wrapped)
832             : fWrapped(std::move(wrapped)) {
833         fNumWrappedCombos = fWrapped->priv().numCombinations();
834     }
835 
836 private:
837     // 6 known 1D blur effects + 6 known 2D blur effects
838     inline static constexpr int kNumIntrinsicCombinations = 12;
839 
numIntrinsicCombinations() const840     int numIntrinsicCombinations() const override { return kNumIntrinsicCombinations; }
841 
numChildCombinations() const842     int numChildCombinations() const override { return fNumWrappedCombos; }
843 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const844     void addToKey(const KeyContext& keyContext,
845                   PaintParamsKeyBuilder* builder,
846                   PipelineDataGatherer* gatherer,
847                   int desiredCombination) const override {
848         SkASSERT(desiredCombination < this->numCombinations());
849 
850         using namespace SkKnownRuntimeEffects;
851 
852         int desiredBlurCombination = desiredCombination % kNumIntrinsicCombinations;
853         int desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations;
854         SkASSERT(desiredWrappedCombination < fNumWrappedCombos);
855 
856         static const StableKey kIDs[kNumIntrinsicCombinations] = {
857                 StableKey::k1DBlur4,  StableKey::k1DBlur8,  StableKey::k1DBlur12,
858                 StableKey::k1DBlur16, StableKey::k1DBlur20, StableKey::k1DBlur28,
859 
860                 StableKey::k2DBlur4,  StableKey::k2DBlur8,  StableKey::k2DBlur12,
861                 StableKey::k2DBlur16, StableKey::k2DBlur20, StableKey::k2DBlur28,
862         };
863 
864         const SkRuntimeEffect* fEffect = GetKnownRuntimeEffect(kIDs[desiredBlurCombination]);
865 
866         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
867 
868         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(fEffect) });
869             fWrapped->priv().addToKey(childContext, builder, gatherer, desiredWrappedCombination);
870         builder->endBlock();
871     }
872 
873     sk_sp<PrecompileShader> fWrapped;
874     int fNumWrappedCombos;
875 };
876 
Blur(sk_sp<PrecompileShader> wrapped)877 sk_sp<PrecompileShader> PrecompileShadersPriv::Blur(sk_sp<PrecompileShader> wrapped) {
878     return sk_make_sp<PrecompileBlurShader>(std::move(wrapped));
879 }
880 
881 //--------------------------------------------------------------------------------------------------
882 class PrecompileMatrixConvolutionShader final : public PrecompileShader {
883 public:
PrecompileMatrixConvolutionShader(sk_sp<PrecompileShader> wrapped)884     PrecompileMatrixConvolutionShader(sk_sp<PrecompileShader> wrapped)
885             : fWrapped(std::move(wrapped)) {
886         fNumWrappedCombos = fWrapped->priv().numCombinations();
887 
888         // When the matrix convolution ImageFilter uses a texture we know it will only ever
889         // be SkFilterMode::kNearest and SkTileMode::kClamp.
890         // TODO: add a PrecompileImageShaderFlags to further limit the raw image shader
891         // combinations. Right now we're getting two combinations for the raw shader
892         // (sk_image_shader and sk_hw_image_shader).
893         fRawImageShader =
894                 PrecompileShadersPriv::RawImage(PrecompileImageShaderFlags::kExcludeCubic);
895         fNumRawImageShaderCombos = fRawImageShader->priv().numCombinations();
896     }
897 
898 private:
numIntrinsicCombinations() const899     int numIntrinsicCombinations() const override {
900         // The uniform version only has one option but the two texture-based versions will
901         // have as many combinations as the raw image shader.
902         return 1 + 2 * fNumRawImageShaderCombos;
903     }
904 
numChildCombinations() const905     int numChildCombinations() const override { return fNumWrappedCombos; }
906 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const907     void addToKey(const KeyContext& keyContext,
908                   PaintParamsKeyBuilder* builder,
909                   PipelineDataGatherer* gatherer,
910                   int desiredCombination) const override {
911 
912         int desiredTextureCombination = 0;
913 
914         const int desiredWrappedCombination = desiredCombination % fNumWrappedCombos;
915         int remainingCombinations = desiredCombination / fNumWrappedCombos;
916 
917         SkKnownRuntimeEffects::StableKey stableKey = SkKnownRuntimeEffects::StableKey::kInvalid;
918         if (remainingCombinations == 0) {
919             stableKey = SkKnownRuntimeEffects::StableKey::kMatrixConvUniforms;
920         } else {
921             static constexpr SkKnownRuntimeEffects::StableKey kTextureBasedStableKeys[] = {
922                     SkKnownRuntimeEffects::StableKey::kMatrixConvTexSm,
923                     SkKnownRuntimeEffects::StableKey::kMatrixConvTexLg,
924             };
925 
926             --remainingCombinations;
927             stableKey = kTextureBasedStableKeys[remainingCombinations % 2];
928             desiredTextureCombination = remainingCombinations / 2;
929             SkASSERT(desiredTextureCombination < fNumRawImageShaderCombos);
930         }
931 
932         const SkRuntimeEffect* fEffect = GetKnownRuntimeEffect(stableKey);
933 
934         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
935 
936         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(fEffect) });
937             fWrapped->priv().addToKey(childContext, builder, gatherer, desiredWrappedCombination);
938             if (stableKey != SkKnownRuntimeEffects::StableKey::kMatrixConvUniforms) {
939                 fRawImageShader->priv().addToKey(childContext, builder, gatherer,
940                                                  desiredTextureCombination);
941             }
942         builder->endBlock();
943     }
944 
945     sk_sp<PrecompileShader> fWrapped;
946     int fNumWrappedCombos;
947     sk_sp<PrecompileShader> fRawImageShader;
948     int fNumRawImageShaderCombos;
949 };
950 
MatrixConvolution(sk_sp<skgpu::graphite::PrecompileShader> wrapped)951 sk_sp<PrecompileShader> PrecompileShadersPriv::MatrixConvolution(
952         sk_sp<skgpu::graphite::PrecompileShader> wrapped) {
953     return sk_make_sp<PrecompileMatrixConvolutionShader>(std::move(wrapped));
954 }
955 
956 //--------------------------------------------------------------------------------------------------
957 class PrecompileMorphologyShader final : public PrecompileShader {
958 public:
PrecompileMorphologyShader(sk_sp<PrecompileShader> wrapped,SkKnownRuntimeEffects::StableKey stableKey)959     PrecompileMorphologyShader(sk_sp<PrecompileShader> wrapped,
960                                SkKnownRuntimeEffects::StableKey stableKey)
961             : fWrapped(std::move(wrapped))
962             , fStableKey(stableKey) {
963         fNumWrappedCombos = fWrapped->priv().numCombinations();
964         SkASSERT(stableKey == SkKnownRuntimeEffects::StableKey::kLinearMorphology ||
965                  stableKey == SkKnownRuntimeEffects::StableKey::kSparseMorphology);
966     }
967 
968 private:
numChildCombinations() const969     int numChildCombinations() const override { return fNumWrappedCombos; }
970 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const971     void addToKey(const KeyContext& keyContext,
972                   PaintParamsKeyBuilder* builder,
973                   PipelineDataGatherer* gatherer,
974                   int desiredCombination) const override {
975         SkASSERT(desiredCombination < fNumWrappedCombos);
976 
977         const SkRuntimeEffect* effect = GetKnownRuntimeEffect(fStableKey);
978 
979         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
980 
981         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(effect) });
982             fWrapped->priv().addToKey(childContext, builder, gatherer, desiredCombination);
983         builder->endBlock();
984     }
985 
986     sk_sp<PrecompileShader> fWrapped;
987     int fNumWrappedCombos;
988     SkKnownRuntimeEffects::StableKey fStableKey;
989 };
990 
LinearMorphology(sk_sp<PrecompileShader> wrapped)991 sk_sp<PrecompileShader> PrecompileShadersPriv::LinearMorphology(sk_sp<PrecompileShader> wrapped) {
992     return sk_make_sp<PrecompileMorphologyShader>(
993             std::move(wrapped),
994             SkKnownRuntimeEffects::StableKey::kLinearMorphology);
995 }
996 
SparseMorphology(sk_sp<PrecompileShader> wrapped)997 sk_sp<PrecompileShader> PrecompileShadersPriv::SparseMorphology(sk_sp<PrecompileShader> wrapped) {
998     return sk_make_sp<PrecompileMorphologyShader>(
999             std::move(wrapped),
1000             SkKnownRuntimeEffects::StableKey::kSparseMorphology);
1001 }
1002 
1003 //--------------------------------------------------------------------------------------------------
1004 class PrecompileDisplacementShader final : public PrecompileShader {
1005 public:
PrecompileDisplacementShader(sk_sp<PrecompileShader> displacement,sk_sp<PrecompileShader> color)1006     PrecompileDisplacementShader(sk_sp<PrecompileShader> displacement,
1007                                  sk_sp<PrecompileShader> color)
1008             : fDisplacement(std::move(displacement))
1009             , fColor(std::move(color)) {
1010         fNumDisplacementCombos = fDisplacement->priv().numCombinations();
1011         fNumColorCombos = fColor->priv().numCombinations();
1012     }
1013 
1014 private:
numChildCombinations() const1015     int numChildCombinations() const override { return fNumDisplacementCombos * fNumColorCombos; }
1016 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const1017     void addToKey(const KeyContext& keyContext,
1018                   PaintParamsKeyBuilder* builder,
1019                   PipelineDataGatherer* gatherer,
1020                   int desiredCombination) const override {
1021         SkASSERT(desiredCombination < this->numChildCombinations());
1022 
1023         const int desiredDisplacementCombination = desiredCombination % fNumDisplacementCombos;
1024         const int desiredColorCombination = desiredCombination / fNumDisplacementCombos;
1025         SkASSERT(desiredColorCombination < fNumColorCombos);
1026 
1027         const SkRuntimeEffect* fEffect =
1028                 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kDisplacement);
1029 
1030         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1031 
1032         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(fEffect) });
1033             fDisplacement->priv().addToKey(childContext, builder, gatherer,
1034                                            desiredDisplacementCombination);
1035             fColor->priv().addToKey(childContext, builder, gatherer,
1036                                     desiredColorCombination);
1037         builder->endBlock();
1038     }
1039 
1040     sk_sp<PrecompileShader> fDisplacement;
1041     int fNumDisplacementCombos;
1042     sk_sp<PrecompileShader> fColor;
1043     int fNumColorCombos;
1044 };
1045 
1046 //--------------------------------------------------------------------------------------------------
Displacement(sk_sp<PrecompileShader> displacement,sk_sp<PrecompileShader> color)1047 sk_sp<PrecompileShader> PrecompileShadersPriv::Displacement(sk_sp<PrecompileShader> displacement,
1048                                                             sk_sp<PrecompileShader> color) {
1049     return sk_make_sp<PrecompileDisplacementShader>(std::move(displacement), std::move(color));
1050 }
1051 
1052 //--------------------------------------------------------------------------------------------------
1053 class PrecompileLightingShader final : public PrecompileShader {
1054 public:
PrecompileLightingShader(sk_sp<PrecompileShader> wrapped)1055     PrecompileLightingShader(sk_sp<PrecompileShader> wrapped)
1056             : fWrapped(std::move(wrapped)) {
1057         fNumWrappedCombos = fWrapped->priv().numCombinations();
1058     }
1059 
1060 private:
numChildCombinations() const1061     int numChildCombinations() const override { return fNumWrappedCombos; }
1062 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const1063     void addToKey(const KeyContext& keyContext,
1064                   PaintParamsKeyBuilder* builder,
1065                   PipelineDataGatherer* gatherer,
1066                   int desiredCombination) const override {
1067         SkASSERT(desiredCombination < fNumWrappedCombos);
1068 
1069         const SkRuntimeEffect* normalEffect =
1070                 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kNormal);
1071         const SkRuntimeEffect* lightingEffect =
1072                 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kLighting);
1073 
1074         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1075 
1076         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
1077                                        { sk_ref_sp(lightingEffect) });
1078             RuntimeEffectBlock::BeginBlock(childContext, builder, gatherer,
1079                                            { sk_ref_sp(normalEffect) });
1080                 fWrapped->priv().addToKey(childContext, builder, gatherer, desiredCombination);
1081             builder->endBlock();
1082         builder->endBlock();
1083     }
1084 
1085     sk_sp<PrecompileShader> fWrapped;
1086     int fNumWrappedCombos;
1087 };
1088 
Lighting(sk_sp<PrecompileShader> wrapped)1089 sk_sp<PrecompileShader> PrecompileShadersPriv::Lighting(sk_sp<PrecompileShader> wrapped) {
1090     return sk_make_sp<PrecompileLightingShader>(std::move(wrapped));
1091 }
1092 
1093 //--------------------------------------------------------------------------------------------------
1094 
1095 } // namespace skgpu::graphite
1096