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