• 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 "include/gpu/graphite/precompile/PrecompileColorFilter.h"
14 #include "src/core/SkColorSpacePriv.h"
15 #include "src/core/SkImageInfoPriv.h"
16 #include "src/core/SkKnownRuntimeEffects.h"
17 #include "src/gpu/Blend.h"
18 #include "src/gpu/graphite/BuiltInCodeSnippetID.h"
19 #include "src/gpu/graphite/KeyContext.h"
20 #include "src/gpu/graphite/KeyHelpers.h"
21 #include "src/gpu/graphite/PaintParams.h"
22 #include "src/gpu/graphite/PaintParamsKey.h"
23 #include "src/gpu/graphite/PrecompileInternal.h"
24 #include "src/gpu/graphite/ReadSwizzle.h"
25 #include "src/gpu/graphite/RecorderPriv.h"
26 #include "src/gpu/graphite/precompile/PrecompileBaseComplete.h"
27 #include "src/gpu/graphite/precompile/PrecompileBasePriv.h"
28 #include "src/gpu/graphite/precompile/PrecompileBlenderPriv.h"
29 #include "src/gpu/graphite/precompile/PrecompileShaderPriv.h"
30 #include "src/gpu/graphite/precompile/PrecompileShadersPriv.h"
31 #include "src/shaders/gradients/SkLinearGradient.h"
32 
33 namespace skgpu::graphite {
34 
35 PrecompileShader::~PrecompileShader() = default;
36 
makeWithColorFilter(sk_sp<PrecompileColorFilter> cf) const37 sk_sp<PrecompileShader> PrecompileShader::makeWithColorFilter(
38         sk_sp<PrecompileColorFilter> cf) const {
39     if (!cf) {
40         return sk_ref_sp(this);
41     }
42 
43     return PrecompileShaders::ColorFilter({ sk_ref_sp(this) }, { std::move(cf) });
44 }
45 
makeWithWorkingColorSpace(sk_sp<SkColorSpace> cs) const46 sk_sp<PrecompileShader> PrecompileShader::makeWithWorkingColorSpace(sk_sp<SkColorSpace> cs) const {
47     if (!cs) {
48         return sk_ref_sp(this);
49     }
50 
51     return PrecompileShaders::WorkingColorSpace({ sk_ref_sp(this) }, { std::move(cs) });
52 }
53 
54 //--------------------------------------------------------------------------------------------------
55 class PrecompileEmptyShader final : public PrecompileShader {
56 private:
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const57     void addToKey(const KeyContext& keyContext,
58                   PaintParamsKeyBuilder* builder,
59                   PipelineDataGatherer* gatherer,
60                   int desiredCombination) const override {
61 
62         SkASSERT(desiredCombination == 0); // The empty shader only ever has one combination
63 
64         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
65     }
66 };
67 
Empty()68 sk_sp<PrecompileShader> PrecompileShaders::Empty() {
69     return sk_make_sp<PrecompileEmptyShader>();
70 }
71 
72 //--------------------------------------------------------------------------------------------------
73 class PrecompileColorShader final : public PrecompileShader {
74 private:
isConstant(int desiredCombination) const75     bool isConstant(int desiredCombination) const override {
76         SkASSERT(desiredCombination == 0); // The color shader only ever has one combination
77         return true;
78     }
79 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const80     void addToKey(const KeyContext& keyContext,
81                   PaintParamsKeyBuilder* builder,
82                   PipelineDataGatherer* gatherer,
83                   int desiredCombination) const override {
84 
85         SkASSERT(desiredCombination == 0); // The color shader only ever has one combination
86 
87         // The white PMColor is just a placeholder for the actual paint params color
88         SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, SK_PMColor4fWHITE);
89     }
90 };
91 
Color()92 sk_sp<PrecompileShader> PrecompileShaders::Color() {
93     return sk_make_sp<PrecompileColorShader>();
94 }
95 
96 // The colorSpace is safe to ignore - it is just applied to the color and doesn't modify the
97 // generated program.
Color(sk_sp<SkColorSpace>)98 sk_sp<PrecompileShader> PrecompileShaders::Color(sk_sp<SkColorSpace>) {
99     return sk_make_sp<PrecompileColorShader>();
100 }
101 
102 //--------------------------------------------------------------------------------------------------
103 class PrecompileBlendShader final : public PrecompileShader {
104 public:
PrecompileBlendShader(PrecompileBlenderList && blenders,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs)105     PrecompileBlendShader(PrecompileBlenderList&& blenders,
106                           SkSpan<const sk_sp<PrecompileShader>> dsts,
107                           SkSpan<const sk_sp<PrecompileShader>> srcs)
108             : fBlenderOptions(std::move(blenders))
109             , fDstOptions(dsts.begin(), dsts.end())
110             , fSrcOptions(srcs.begin(), srcs.end()) {
111         fNumDstCombos = 0;
112         for (const auto& d : fDstOptions) {
113             fNumDstCombos += d->priv().numCombinations();
114         }
115 
116         fNumSrcCombos = 0;
117         for (const auto& s : fSrcOptions) {
118             fNumSrcCombos += s->priv().numCombinations();
119         }
120     }
121 
122 private:
numChildCombinations() const123     int numChildCombinations() const override {
124         return fBlenderOptions.numCombinations() * fNumDstCombos * fNumSrcCombos;
125     }
126 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const127     void addToKey(const KeyContext& keyContext,
128                   PaintParamsKeyBuilder* builder,
129                   PipelineDataGatherer* gatherer,
130                   int desiredCombination) const override {
131         SkASSERT(desiredCombination < this->numCombinations());
132 
133         const int desiredDstCombination = desiredCombination % fNumDstCombos;
134         int remainingCombinations = desiredCombination / fNumDstCombos;
135 
136         const int desiredSrcCombination = remainingCombinations % fNumSrcCombos;
137         remainingCombinations /= fNumSrcCombos;
138 
139         int desiredBlendCombination = remainingCombinations;
140         SkASSERT(desiredBlendCombination < fBlenderOptions.numCombinations());
141 
142         auto [blender, blenderCombination] = fBlenderOptions.selectOption(desiredBlendCombination);
143         if (blender->priv().asBlendMode()) {
144             // Coefficient and HSLC blends, and other fixed SkBlendMode blenders use the
145             // BlendCompose block to organize the children.
146             BlendComposeBlock::BeginBlock(keyContext, builder, gatherer);
147         } else {
148             // Runtime blenders are wrapped in the kBlend runtime shader, although functionally
149             // it is identical to the BlendCompose snippet.
150             const SkRuntimeEffect* blendEffect =
151                     GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kBlend);
152 
153             RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
154                                            { sk_ref_sp(blendEffect) });
155         }
156 
157         AddToKey<PrecompileShader>(keyContext, builder, gatherer, fSrcOptions,
158                                    desiredSrcCombination);
159         AddToKey<PrecompileShader>(keyContext, builder, gatherer, fDstOptions,
160                                    desiredDstCombination);
161 
162         if (blender->priv().asBlendMode()) {
163             SkASSERT(blenderCombination == 0);
164             AddBlendMode(keyContext, builder, gatherer, *blender->priv().asBlendMode());
165         } else {
166             blender->priv().addToKey(keyContext, builder, gatherer, blenderCombination);
167         }
168 
169         builder->endBlock();  // BlendComposeBlock or RuntimeEffectBlock
170     }
171 
172     PrecompileBlenderList fBlenderOptions;
173     std::vector<sk_sp<PrecompileShader>> fDstOptions;
174     std::vector<sk_sp<PrecompileShader>> fSrcOptions;
175 
176     int fNumDstCombos;
177     int fNumSrcCombos;
178 };
179 
Blend(SkSpan<const sk_sp<PrecompileBlender>> blenders,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs)180 sk_sp<PrecompileShader> PrecompileShaders::Blend(
181         SkSpan<const sk_sp<PrecompileBlender>> blenders,
182         SkSpan<const sk_sp<PrecompileShader>> dsts,
183         SkSpan<const sk_sp<PrecompileShader>> srcs) {
184     return sk_make_sp<PrecompileBlendShader>(PrecompileBlenderList(blenders), dsts, srcs);
185 }
186 
Blend(SkSpan<const SkBlendMode> blendModes,SkSpan<const sk_sp<PrecompileShader>> dsts,SkSpan<const sk_sp<PrecompileShader>> srcs)187 sk_sp<PrecompileShader> PrecompileShaders::Blend(
188         SkSpan<const SkBlendMode> blendModes,
189         SkSpan<const sk_sp<PrecompileShader>> dsts,
190         SkSpan<const sk_sp<PrecompileShader>> srcs) {
191     return sk_make_sp<PrecompileBlendShader>(PrecompileBlenderList(blendModes), dsts, srcs);
192 }
193 
194 //--------------------------------------------------------------------------------------------------
195 class PrecompileCoordClampShader final : public PrecompileShader {
196 public:
PrecompileCoordClampShader(SkSpan<const sk_sp<PrecompileShader>> shaders)197     PrecompileCoordClampShader(SkSpan<const sk_sp<PrecompileShader>> shaders)
198             : fShaders(shaders.begin(), shaders.end()) {
199         fNumShaderCombos = 0;
200         for (const auto& s : fShaders) {
201             fNumShaderCombos += s->priv().numCombinations();
202         }
203     }
204 
205 private:
numChildCombinations() const206     int numChildCombinations() const override {
207         return fNumShaderCombos;
208     }
209 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const210     void addToKey(const KeyContext& keyContext,
211                   PaintParamsKeyBuilder* builder,
212                   PipelineDataGatherer* gatherer,
213                   int desiredCombination) const override {
214         SkASSERT(desiredCombination < fNumShaderCombos);
215 
216         constexpr SkRect kIgnored { 0, 0, 256, 256 }; // ignored bc we're precompiling
217 
218         // TODO: update CoordClampShaderBlock so this is optional
219         CoordClampShaderBlock::CoordClampData data(kIgnored);
220 
221         CoordClampShaderBlock::BeginBlock(keyContext, builder, gatherer, data);
222             AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders, desiredCombination);
223         builder->endBlock();
224     }
225 
226     std::vector<sk_sp<PrecompileShader>> fShaders;
227     int fNumShaderCombos;
228 };
229 
CoordClamp(SkSpan<const sk_sp<PrecompileShader>> input)230 sk_sp<PrecompileShader> PrecompileShaders::CoordClamp(SkSpan<const sk_sp<PrecompileShader>> input) {
231     return sk_make_sp<PrecompileCoordClampShader>(input);
232 }
233 
234 //--------------------------------------------------------------------------------------------------
235 class PrecompileImageShader final : public PrecompileShader {
236 public:
PrecompileImageShader(SkEnumBitMask<PrecompileImageShaderFlags> flags,SkSpan<const SkColorInfo> colorInfos,SkSpan<const SkTileMode> tileModes,bool raw)237     PrecompileImageShader(SkEnumBitMask<PrecompileImageShaderFlags> flags,
238                           SkSpan<const SkColorInfo> colorInfos,
239                           SkSpan<const SkTileMode> tileModes,
240                           bool raw)
241             : fNumExtraSamplingTilingCombos((flags & PrecompileImageShaderFlags::kExcludeCubic)
242                                                     ? 1  // Just kHWTiled
243                                                     : kExtraNumSamplingTilingCombos)
244             , fColorInfos(!colorInfos.empty()
245                             ? std::vector<SkColorInfo>(colorInfos.begin(), colorInfos.end())
246                             : raw ? RawImageDefaultColorInfos()
247                                   : (flags & PrecompileImageShaderFlags::kExcludeAlpha)
248                                              ? NonAlphaOnlyDefaultColorInfos()
249                                              : DefaultColorInfos())
250             , fTileModes(!tileModes.empty()
251                             ? std::vector<SkTileMode>(tileModes.begin(), tileModes.end())
252                             : DefaultTileModes())
253             , fUseDstColorSpace(!colorInfos.empty())
254             , fRaw(raw) {}
255 
256 private:
257     // In addition to the tile mode options provided by the client, we can precompile two additional
258     // sampling/tiling variants: hardware-tiled and cubic sampling (which always uses the most
259     // generic tiling shader).
260     inline static constexpr int kExtraNumSamplingTilingCombos = 2;
261     inline static constexpr int kCubicSampled = 1;
262     inline static constexpr int kHWTiled      = 0;
263 
264     // These color info objects are defined assuming an sRGB destination.
265     // Most specialized color space transform shader, no actual color space handling.
DefaultColorInfoPremul()266     static SkColorInfo DefaultColorInfoPremul() {
267         return { kRGBA_8888_SkColorType, kPremul_SkAlphaType, SkColorSpace::MakeSRGB() };
268     }
269     // sRGB-to-sRGB specialized color space transform shader.
DefaultColorInfoSRGB()270     static SkColorInfo DefaultColorInfoSRGB() {
271         return { kRGBA_8888_SkColorType, kPremul_SkAlphaType,
272                  sk_srgb_singleton()->makeColorSpin() };
273     }
274     // Most general color space transform shader.
DefaultColorInfoGeneral()275     static SkColorInfo DefaultColorInfoGeneral() {
276         return { kRGBA_8888_SkColorType, kPremul_SkAlphaType, SkColorSpace::MakeSRGBLinear() };
277     }
278     // Alpha-only, most general color space transform shader.
DefaultColorInfoAlphaOnly()279     static SkColorInfo DefaultColorInfoAlphaOnly() {
280         return { kAlpha_8_SkColorType, kPremul_SkAlphaType, SkColorSpace::MakeSRGBLinear() };
281     }
282 
283     // A fixed list of SkColorInfos that will trigger each possible combination of alpha-only
284     // handling and color space transform variants, when drawn to an sRGB destination.
DefaultColorInfos()285     static std::vector<SkColorInfo> DefaultColorInfos() {
286         return { DefaultColorInfoPremul(), DefaultColorInfoSRGB(), DefaultColorInfoGeneral(),
287                  DefaultColorInfoAlphaOnly() };
288     }
289     // A fixed list of SkColorInfos that will trigger each color space transform shader variant when
290     // drawn to an sRGB destination.
NonAlphaOnlyDefaultColorInfos()291     static std::vector<SkColorInfo> NonAlphaOnlyDefaultColorInfos() {
292         return { DefaultColorInfoPremul(), DefaultColorInfoSRGB(), DefaultColorInfoGeneral() };
293     }
294     // A fixed list of SkColorInfos that will trigger each color space transform shader variant
295     // possible from a raw image draw. The general shader is still required if the image is
296     // alpha-only, because the read swizzle is implemented as a gamut transformation.
RawImageDefaultColorInfos()297     static std::vector<SkColorInfo> RawImageDefaultColorInfos() {
298         return { DefaultColorInfoPremul(), DefaultColorInfoAlphaOnly() };
299     }
300 
301     // A fixed list of SkTileModes that will trigger each tiling shader variant.
DefaultTileModes()302     static std::vector<SkTileMode> DefaultTileModes() {
303         return { SkTileMode::kClamp, SkTileMode::kRepeat };
304     }
305 
306     const int fNumExtraSamplingTilingCombos;
307 
308     const std::vector<SkColorInfo> fColorInfos;
309     const std::vector<SkTileMode> fTileModes;
310 
311     // If true, use the destination color space from the KeyContext provided to addToKey.
312     // This is true if and only if the client has provided a list of color infos. Otherwise, we
313     // always use an sRGB destination per the default SkColorInfo lists defined above.
314     const bool fUseDstColorSpace;
315 
316     // Whether this precompiles raw image shaders.
317     const bool fRaw;
318 
numIntrinsicCombinations() const319     int numIntrinsicCombinations() const override {
320         // TODO(b/400682634) If color infos were provided by the client, and we're using the
321         // destination color space to determine what color space transform shaders to use, we can
322         // end up generating duplicate shaders, and the actual number of unique shaders generated
323         // will be less than the number calculated here.
324         return fColorInfos.size() * (fTileModes.size() + fNumExtraSamplingTilingCombos);
325     }
326 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const327     void addToKey(const KeyContext& keyContext,
328                   PaintParamsKeyBuilder* builder,
329                   PipelineDataGatherer* gatherer,
330                   int desiredCombination) const override {
331         SkASSERT(this->numChildCombinations() == 1);
332         SkASSERT(desiredCombination < this->numIntrinsicCombinations());
333 
334         const int numSamplingTilingCombos = fTileModes.size() + fNumExtraSamplingTilingCombos;
335         const int desiredSamplingTilingCombo = desiredCombination % numSamplingTilingCombos;
336         desiredCombination /= numSamplingTilingCombos;
337 
338         const int desiredColorInfo = desiredCombination;
339         SkASSERT(desiredColorInfo < static_cast<int>(fColorInfos.size()));
340 
341         static constexpr SkSamplingOptions kDefaultCubicSampling(SkCubicResampler::Mitchell());
342         static constexpr SkSamplingOptions kDefaultSampling;
343 
344         // ImageShaderBlock will use hardware tiling when the subset covers the entire image, so we
345         // create subset + image size combinations where subset == imgSize (for a shader that uses
346         // hardware tiling) and subset < imgSize (for a shader that does shader-based tiling).
347         static constexpr SkRect kSubset = SkRect::MakeWH(1.0f, 1.0f);
348         static constexpr SkISize kHWTileableSize = SkISize::Make(1, 1);
349         static constexpr SkISize kShaderTileableSize = SkISize::Make(2, 2);
350 
351         const int numTileModes = fTileModes.size();
352         const SkTileMode tileMode = (desiredSamplingTilingCombo < numTileModes)
353                                             ? fTileModes[desiredSamplingTilingCombo]
354                                             : SkTileMode::kClamp;
355         const SkISize imgSize = (desiredSamplingTilingCombo >= numTileModes &&
356                                  desiredSamplingTilingCombo - numTileModes == kHWTiled)
357                                         ? kHWTileableSize
358                                         : kShaderTileableSize;
359         const SkSamplingOptions sampling =
360                 (desiredSamplingTilingCombo >= numTileModes &&
361                  desiredSamplingTilingCombo - numTileModes == kCubicSampled)
362                         ? kDefaultCubicSampling
363                         : kDefaultSampling;
364 
365         const ImageShaderBlock::ImageData imgData(sampling, tileMode, tileMode, imgSize, kSubset);
366 
367         const SkColorInfo& colorInfo = fColorInfos[desiredColorInfo];
368         const bool alphaOnly = SkColorTypeIsAlphaOnly(colorInfo.colorType());
369 
370         const Caps* caps = keyContext.caps();
371         Swizzle readSwizzle = caps->getReadSwizzle(
372                 colorInfo.colorType(),
373                 caps->getDefaultSampledTextureInfo(
374                         colorInfo.colorType(), Mipmapped::kNo, Protected::kNo, Renderable::kNo));
375         if (alphaOnly) {
376             readSwizzle = Swizzle::Concat(readSwizzle, Swizzle("000a"));
377         }
378 
379         ColorSpaceTransformBlock::ColorSpaceTransformData colorXformData(
380                 SwizzleClassToReadEnum(readSwizzle));
381 
382         if (!fRaw) {
383             const SkColorSpace* dstColorSpace = fUseDstColorSpace
384                                                         ? keyContext.dstColorInfo().colorSpace()
385                                                         : sk_srgb_singleton();
386             colorXformData.fSteps = SkColorSpaceXformSteps(
387                     colorInfo.colorSpace(), colorInfo.alphaType(),
388                     dstColorSpace, colorInfo.alphaType());
389 
390             if (alphaOnly) {
391                 Blend(keyContext, builder, gatherer,
392                       /* addBlendToKey= */ [&] () -> void {
393                           AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kDstIn);
394                       },
395                       /* addSrcToKey= */ [&] () -> void {
396                           Compose(keyContext, builder, gatherer,
397                                   /* addInnerToKey= */ [&]() -> void {
398                                       ImageShaderBlock::AddBlock(keyContext, builder, gatherer,
399                                                                  imgData);
400                                   },
401                                   /* addOuterToKey= */ [&]() -> void {
402                                       ColorSpaceTransformBlock::AddBlock(keyContext, builder,
403                                                                          gatherer, colorXformData);
404                                   });
405                       },
406                       /* addDstToKey= */ [&]() -> void {
407                           RGBPaintColorBlock::AddBlock(keyContext, builder, gatherer);
408                       });
409                 return;
410             }
411         }
412 
413         Compose(keyContext, builder, gatherer,
414                 /* addInnerToKey= */ [&]() -> void {
415                     ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
416                 },
417                 /* addOuterToKey= */ [&]() -> void {
418                     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer,
419                                                        colorXformData);
420                 });
421     }
422 };
423 
Image(SkSpan<const SkColorInfo> colorInfos,SkSpan<const SkTileMode> tileModes)424 sk_sp<PrecompileShader> PrecompileShaders::Image(SkSpan<const SkColorInfo> colorInfos,
425                                                  SkSpan<const SkTileMode> tileModes) {
426     return PrecompileShaders::LocalMatrix(
427             { sk_make_sp<PrecompileImageShader>(PrecompileImageShaderFlags::kNone,
428                                                 colorInfos, tileModes,
429                                                 /* raw= */false) });
430 }
431 
RawImage(SkSpan<const SkColorInfo> colorInfos,SkSpan<const SkTileMode> tileModes)432 sk_sp<PrecompileShader> PrecompileShaders::RawImage(SkSpan<const SkColorInfo> colorInfos,
433                                                     SkSpan<const SkTileMode> tileModes) {
434     return PrecompileShaders::LocalMatrix(
435             { sk_make_sp<PrecompileImageShader>(PrecompileImageShaderFlags::kExcludeCubic,
436                                                 colorInfos, tileModes,
437                                                 /* raw= */true) });
438 }
439 
Image(SkEnumBitMask<PrecompileImageShaderFlags> flags)440 sk_sp<PrecompileShader> PrecompileShadersPriv::Image(
441         SkEnumBitMask<PrecompileImageShaderFlags> flags) {
442     return PrecompileShaders::LocalMatrix(
443             { sk_make_sp<PrecompileImageShader>(flags,
444                                                 SkSpan<const SkColorInfo>(),
445                                                 SkSpan<const SkTileMode>(),
446                                                 /* raw= */ false) });
447 }
448 
RawImage(SkEnumBitMask<PrecompileImageShaderFlags> flags)449 sk_sp<PrecompileShader> PrecompileShadersPriv::RawImage(
450         SkEnumBitMask<PrecompileImageShaderFlags> flags) {
451     return PrecompileShaders::LocalMatrix(
452             { sk_make_sp<PrecompileImageShader>(flags | PrecompileImageShaderFlags::kExcludeCubic,
453                                                 SkSpan<const SkColorInfo>(),
454                                                 SkSpan<const SkTileMode>(),
455                                                 /* raw= */ true) });
456 }
457 
458 //--------------------------------------------------------------------------------------------------
459 class PrecompileYUVImageShader : public PrecompileShader {
460 public:
PrecompileYUVImageShader()461     PrecompileYUVImageShader() {}
462 
463 private:
464     // There are 12 intrinsic YUV shaders:
465     //  4 tiling modes
466     //    HW tiling w/o swizzle
467     //    HW tiling w/ swizzle
468     //    cubic shader tiling
469     //    non-cubic shader tiling
470     //  crossed with 3 color space transforms:
471     //    premul/alpha-swizzle only
472     //    srgb-to-srgb
473     //    general transform
474     inline static constexpr int kNumTilingModes     = 4;
475     inline static constexpr int kHWTiledNoSwizzle   = 3;
476     inline static constexpr int kHWTiledWithSwizzle = 2;
477     inline static constexpr int kCubicShaderTiled   = 1;
478     inline static constexpr int kShaderTiled        = 0;
479 
480     inline static constexpr int kNumColorSpaceCombinations = 3;
481     inline static constexpr int kColorSpacePremul  = 2;
482     inline static constexpr int kColorSpaceSRGB    = 1;
483     inline static constexpr int kColorSpaceGeneral = 0;
484 
485     inline static constexpr int kNumIntrinsicCombinations =
486             kNumTilingModes * kNumColorSpaceCombinations;
487 
numIntrinsicCombinations() const488     int numIntrinsicCombinations() const override { return kNumIntrinsicCombinations; }
489 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const490     void addToKey(const KeyContext& keyContext,
491                   PaintParamsKeyBuilder* builder,
492                   PipelineDataGatherer* gatherer,
493                   int desiredCombination) const override {
494         SkASSERT(desiredCombination < kNumIntrinsicCombinations);
495 
496         int desiredColorSpaceCombo = desiredCombination % kNumColorSpaceCombinations;
497         int desiredTiling = desiredCombination / kNumColorSpaceCombinations;
498         SkASSERT(desiredTiling < kNumTilingModes);
499 
500         static constexpr SkSamplingOptions kDefaultCubicSampling(SkCubicResampler::Mitchell());
501         static constexpr SkSamplingOptions kDefaultSampling;
502 
503         YUVImageShaderBlock::ImageData imgData(desiredTiling == kCubicShaderTiled
504                                                                        ? kDefaultCubicSampling
505                                                                        : kDefaultSampling,
506                                                SkTileMode::kClamp, SkTileMode::kClamp,
507                                                /* imgSize= */ { 1, 1 },
508                                                /* subset= */ desiredTiling == kShaderTiled
509                                                                      ? SkRect::MakeEmpty()
510                                                                      : SkRect::MakeWH(1, 1));
511 
512         static constexpr SkV4 kRedChannel{ 1.f, 0.f, 0.f, 0.f };
513         imgData.fChannelSelect[0] = kRedChannel;
514         imgData.fChannelSelect[1] = kRedChannel;
515         if (desiredTiling == kHWTiledNoSwizzle) {
516             imgData.fChannelSelect[2] = kRedChannel;
517         } else {
518             // Having a non-red channel selector forces a swizzle
519             imgData.fChannelSelect[2] = { 0.f, 1.f, 0.f, 0.f};
520         }
521         imgData.fChannelSelect[3] = kRedChannel;
522 
523         imgData.fYUVtoRGBMatrix.setAll(1, 0, 0, 0, 1, 0, 0, 0, 0);
524         imgData.fYUVtoRGBTranslate = { 0, 0, 0 };
525 
526         static sk_sp<SkColorSpace> srgbSpinColorSpace = sk_srgb_singleton()->makeColorSpin();
527         const ColorSpaceTransformBlock::ColorSpaceTransformData colorXformData =
528                 desiredColorSpaceCombo == kColorSpacePremul
529                         ? ColorSpaceTransformBlock::ColorSpaceTransformData(
530                                   skgpu::graphite::ReadSwizzle::kRGB1) :
531                 desiredColorSpaceCombo == kColorSpaceSRGB
532                         ? ColorSpaceTransformBlock::ColorSpaceTransformData(
533                                   sk_srgb_singleton(), kPremul_SkAlphaType,
534                                   srgbSpinColorSpace.get(), kPremul_SkAlphaType)
535                         : ColorSpaceTransformBlock::ColorSpaceTransformData(
536                                   sk_srgb_singleton(), kPremul_SkAlphaType,
537                                   sk_srgb_linear_singleton(), kPremul_SkAlphaType);
538         Compose(keyContext, builder, gatherer,
539                 /* addInnerToKey= */ [&]() -> void {
540                     YUVImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
541                 },
542                 /* addOuterToKey= */ [&]() -> void {
543                     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer,
544                                                        colorXformData);
545                 });
546     }
547 };
548 
YUVImage()549 sk_sp<PrecompileShader> PrecompileShaders::YUVImage() {
550     return PrecompileShaders::LocalMatrix({ sk_make_sp<PrecompileYUVImageShader>() });
551 }
552 
553 //--------------------------------------------------------------------------------------------------
554 class PrecompilePerlinNoiseShader final : public PrecompileShader {
555 public:
PrecompilePerlinNoiseShader()556     PrecompilePerlinNoiseShader() {}
557 
558 private:
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const559     void addToKey(const KeyContext& keyContext,
560                   PaintParamsKeyBuilder* builder,
561                   PipelineDataGatherer* gatherer,
562                   int desiredCombination) const override {
563 
564         SkASSERT(desiredCombination == 0); // The Perlin noise shader only ever has one combination
565 
566         // TODO: update PerlinNoiseShaderBlock so the NoiseData is optional
567         static const PerlinNoiseShaderBlock::PerlinNoiseData kIgnoredNoiseData(
568                 PerlinNoiseShaderBlock::Type::kFractalNoise, { 0.0f, 0.0f }, 2, {1, 1});
569 
570         PerlinNoiseShaderBlock::AddBlock(keyContext, builder, gatherer, kIgnoredNoiseData);
571     }
572 
573 };
574 
MakeFractalNoise()575 sk_sp<PrecompileShader> PrecompileShaders::MakeFractalNoise() {
576     return sk_make_sp<PrecompilePerlinNoiseShader>();
577 }
578 
MakeTurbulence()579 sk_sp<PrecompileShader> PrecompileShaders::MakeTurbulence() {
580     return sk_make_sp<PrecompilePerlinNoiseShader>();
581 }
582 
583 namespace {
584 
get_gradient_intermediate_cs(SkColorSpace * dstColorSpace,SkGradientShader::Interpolation interpolation)585 sk_sp<SkColorSpace> get_gradient_intermediate_cs(SkColorSpace* dstColorSpace,
586                                                  SkGradientShader::Interpolation interpolation) {
587     // Any gradient shader will do, as long as it has the correct interpolation settings.
588     constexpr SkPoint pts[2] = {{0.f, 0.f}, {1.f, 0.f}};
589     constexpr SkColor4f colors[2] = {SkColors::kBlack, SkColors::kWhite};
590     constexpr float pos[2] = {0.f, 1.f};
591     SkLinearGradient shader(pts, {colors, nullptr, pos, 2, SkTileMode::kClamp, interpolation});
592 
593     SkColor4fXformer xformedColors(&shader, dstColorSpace);
594     return xformedColors.fIntermediateColorSpace;
595 }
596 
597 }  // anonymous namespace
598 
599 //--------------------------------------------------------------------------------------------------
600 class PrecompileGradientShader final : public PrecompileShader {
601 public:
PrecompileGradientShader(SkShaderBase::GradientType type,const SkGradientShader::Interpolation & interpolation)602     PrecompileGradientShader(SkShaderBase::GradientType type,
603                              const SkGradientShader::Interpolation& interpolation)
604             : fType(type), fInterpolation(interpolation) {}
605 
606 private:
607     /*
608      * The gradients currently have three specializations based on the number of stops.
609      */
610     inline static constexpr int kNumStopVariants = 3;
611     inline static constexpr int kStopVariants[kNumStopVariants] =
612             { 4, 8, GradientShaderBlocks::GradientData::kNumInternalStorageStops+1 };
613 
numIntrinsicCombinations() const614     int numIntrinsicCombinations() const override { return kNumStopVariants; }
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(this->numChildCombinations() == 1);
621         SkASSERT(desiredCombination < kNumStopVariants);
622 
623         bool useStorageBuffer = keyContext.caps()->gradientBufferSupport();
624 
625         GradientShaderBlocks::GradientData gradData(fType,
626                                                     kStopVariants[desiredCombination],
627                                                     useStorageBuffer);
628 
629         // The logic for setting up color spaces here should match that in the "add_gradient_to_key"
630         // functions from src/gpu/graphite/KeyHelpers.cpp.
631         sk_sp<SkColorSpace> intermediateCS = get_gradient_intermediate_cs(
632                 keyContext.dstColorInfo().colorSpace(), fInterpolation);
633         const SkColorSpace* dstCS = keyContext.dstColorInfo().colorSpace()
634                                             ? keyContext.dstColorInfo().colorSpace()
635                                             : sk_srgb_singleton();
636 
637         ColorSpaceTransformBlock::ColorSpaceTransformData csData(
638                 intermediateCS.get(), kPremul_SkAlphaType,
639                 dstCS, kPremul_SkAlphaType);
640 
641         Compose(keyContext, builder, gatherer,
642                 /* addInnerToKey= */ [&]() -> void {
643                     GradientShaderBlocks::AddBlock(keyContext, builder, gatherer, gradData);
644                 },
645                 /* addOuterToKey= */  [&]() -> void {
646                     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData);
647                 });
648     }
649 
650     const SkShaderBase::GradientType fType;
651     const SkGradientShader::Interpolation fInterpolation;
652 };
653 
LinearGradient(SkGradientShader::Interpolation interpolation)654 sk_sp<PrecompileShader> PrecompileShaders::LinearGradient(
655         SkGradientShader::Interpolation interpolation) {
656     sk_sp<PrecompileShader> s = sk_make_sp<PrecompileGradientShader>(
657             SkShaderBase::GradientType::kLinear, interpolation);
658     return PrecompileShaders::LocalMatrix({ std::move(s) });
659 }
660 
RadialGradient(SkGradientShader::Interpolation interpolation)661 sk_sp<PrecompileShader> PrecompileShaders::RadialGradient(
662         SkGradientShader::Interpolation interpolation) {
663     sk_sp<PrecompileShader> s = sk_make_sp<PrecompileGradientShader>(
664             SkShaderBase::GradientType::kRadial, interpolation);
665     return PrecompileShaders::LocalMatrix({ std::move(s) });
666 }
667 
SweepGradient(SkGradientShader::Interpolation interpolation)668 sk_sp<PrecompileShader> PrecompileShaders::SweepGradient(
669         SkGradientShader::Interpolation interpolation) {
670     sk_sp<PrecompileShader> s =
671             sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kSweep, interpolation);
672     return PrecompileShaders::LocalMatrix({ std::move(s) });
673 }
674 
TwoPointConicalGradient(SkGradientShader::Interpolation interpolation)675 sk_sp<PrecompileShader> PrecompileShaders::TwoPointConicalGradient(
676         SkGradientShader::Interpolation interpolation) {
677     sk_sp<PrecompileShader> s = sk_make_sp<PrecompileGradientShader>(
678             SkShaderBase::GradientType::kConical, interpolation);
679     return PrecompileShaders::LocalMatrix({ std::move(s) });
680 }
681 
682 //--------------------------------------------------------------------------------------------------
683 // The PictureShader ultimately turns into an SkImageShader optionally wrapped in a
684 // LocalMatrixShader.
685 // Note that this means each precompile PictureShader will add 24 combinations:
686 //    2 (pictureshader LM) x 12 (imageShader variations)
Picture()687 sk_sp<PrecompileShader> PrecompileShaders::Picture() {
688     // Note: We don't need to consider the PrecompileYUVImageShader since the image
689     // being drawn was created internally by Skia (as non-YUV).
690     return PrecompileShadersPriv::LocalMatrixBothVariants({ PrecompileShaders::Image() });
691 }
692 
Picture(bool withLM)693 sk_sp<PrecompileShader> PrecompileShadersPriv::Picture(bool withLM) {
694     sk_sp<PrecompileShader> s = PrecompileShaders::Image();
695     if (withLM) {
696         return PrecompileShaders::LocalMatrix({ std::move(s) });
697     }
698     return s;
699 }
700 
701 //--------------------------------------------------------------------------------------------------
702 // In the main Skia API the SkLocalMatrixShader is optimized away when the LM is the identity
703 // or omitted. The PrecompileLocalMatrixShader captures this by adding two intrinsic options.
704 // One with the LMShader wrapping the child and one without the LMShader.
705 class PrecompileLocalMatrixShader final : public PrecompileShader {
706 public:
707     enum class Flags {
708         kNone                  = 0b00,
709         kIsPerspective         = 0b01,
710         kIncludeWithOutVariant = 0b10,
711     };
712 
PrecompileLocalMatrixShader(SkSpan<const sk_sp<PrecompileShader>> wrapped,SkEnumBitMask<Flags> flags=Flags::kNone)713     PrecompileLocalMatrixShader(SkSpan<const sk_sp<PrecompileShader>> wrapped,
714                                 SkEnumBitMask<Flags> flags = Flags::kNone)
715             : fWrapped(wrapped.begin(), wrapped.end())
716             , fFlags(flags) {
717         fNumWrappedCombos = 0;
718         for (const auto& s : fWrapped) {
719             fNumWrappedCombos += s->priv().numCombinations();
720         }
721     }
722 
isConstant(int desiredCombination) const723     bool isConstant(int desiredCombination) const override {
724         SkASSERT(desiredCombination < this->numCombinations());
725 
726         /*
727          * Regardless of whether the LocalMatrixShader elides itself or not, we always want
728          * the Constant-ness of the wrapped shader.
729          */
730         int desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations;
731         SkASSERT(desiredWrappedCombination < fNumWrappedCombos);
732 
733         std::pair<sk_sp<PrecompileShader>, int> wrapped =
734                 PrecompileBase::SelectOption(SkSpan(fWrapped), desiredWrappedCombination);
735         if (wrapped.first) {
736             return wrapped.first->priv().isConstant(wrapped.second);
737         }
738 
739         return false;
740     }
741 
getWrapped() const742     SkSpan<const sk_sp<PrecompileShader>> getWrapped() const {
743         return fWrapped;
744     }
745 
getFlags() const746     SkEnumBitMask<Flags> getFlags() const { return fFlags; }
747 
748 private:
749     // The LocalMatrixShader has two potential variants: with and without the LocalMatrixShader
750     // In the "with" variant, the kIsPerspective flag will determine if the shader performs
751     // the perspective division or not.
752     inline static constexpr int kNumIntrinsicCombinations = 2;
753     inline static constexpr int kWithLocalMatrix    = 1;
754     inline static constexpr int kWithoutLocalMatrix = 0;
755 
isALocalMatrixShader() const756     bool isALocalMatrixShader() const override { return true; }
757 
numIntrinsicCombinations() const758     int numIntrinsicCombinations() const override {
759         if (!(fFlags & Flags::kIncludeWithOutVariant)) {
760             return 1;   // just kWithLocalMatrix
761         }
762         return kNumIntrinsicCombinations;
763     }
764 
numChildCombinations() const765     int numChildCombinations() const override { return fNumWrappedCombos; }
766 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const767     void addToKey(const KeyContext& keyContext,
768                   PaintParamsKeyBuilder* builder,
769                   PipelineDataGatherer* gatherer,
770                   int desiredCombination) const override {
771         SkASSERT(desiredCombination < this->numCombinations());
772 
773         int desiredLMCombination, desiredWrappedCombination;
774 
775         if (!(fFlags & Flags::kIncludeWithOutVariant)) {
776             desiredLMCombination = kWithLocalMatrix;
777             desiredWrappedCombination = desiredCombination;
778         } else {
779             desiredLMCombination = desiredCombination % kNumIntrinsicCombinations;
780             desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations;
781         }
782         SkASSERT(desiredWrappedCombination < fNumWrappedCombos);
783 
784         if (desiredLMCombination == kWithLocalMatrix) {
785             SkMatrix matrix = SkMatrix::I();
786             if (fFlags & Flags::kIsPerspective) {
787                 matrix.setPerspX(0.1f);
788             }
789             LocalMatrixShaderBlock::LMShaderData lmShaderData(matrix);
790 
791             LocalMatrixShaderBlock::BeginBlock(keyContext, builder, gatherer, matrix);
792         }
793 
794         AddToKey<PrecompileShader>(keyContext, builder, gatherer, fWrapped,
795                                    desiredWrappedCombination);
796 
797         if (desiredLMCombination == kWithLocalMatrix) {
798             builder->endBlock();
799         }
800     }
801 
802     std::vector<sk_sp<PrecompileShader>> fWrapped;
803     int fNumWrappedCombos;
804     SkEnumBitMask<Flags> fFlags;
805 };
806 
LocalMatrix(SkSpan<const sk_sp<PrecompileShader>> wrapped,bool isPerspective)807 sk_sp<PrecompileShader> PrecompileShaders::LocalMatrix(
808         SkSpan<const sk_sp<PrecompileShader>> wrapped,
809         bool isPerspective) {
810     return sk_make_sp<PrecompileLocalMatrixShader>(
811             std::move(wrapped),
812             isPerspective ? PrecompileLocalMatrixShader::Flags::kIsPerspective
813                           : PrecompileLocalMatrixShader::Flags::kNone);
814 }
815 
LocalMatrixBothVariants(SkSpan<const sk_sp<PrecompileShader>> wrapped)816 sk_sp<PrecompileShader> PrecompileShadersPriv::LocalMatrixBothVariants(
817         SkSpan<const sk_sp<PrecompileShader>> wrapped) {
818     return sk_make_sp<PrecompileLocalMatrixShader>(
819             std::move(wrapped),
820             PrecompileLocalMatrixShader::Flags::kIncludeWithOutVariant);
821 }
822 
makeWithLocalMatrix(bool isPerspective) const823 sk_sp<PrecompileShader> PrecompileShader::makeWithLocalMatrix(bool isPerspective) const {
824     if (this->priv().isALocalMatrixShader()) {
825         // SkShader::makeWithLocalMatrix collapses chains of localMatrix shaders so we need to
826         // follow suit here, folding in any new perspective flag if needed.
827         auto thisAsLMShader = static_cast<const PrecompileLocalMatrixShader*>(this);
828         if (isPerspective && !(thisAsLMShader->getFlags() &
829                 PrecompileLocalMatrixShader::Flags::kIsPerspective)) {
830             return sk_make_sp<PrecompileLocalMatrixShader>(
831                 thisAsLMShader->getWrapped(),
832                 thisAsLMShader->getFlags() | PrecompileLocalMatrixShader::Flags::kIsPerspective);
833         }
834 
835         return sk_ref_sp(this);
836     }
837 
838     return PrecompileShaders::LocalMatrix({ sk_ref_sp(this) }, isPerspective);
839 }
840 
841 //--------------------------------------------------------------------------------------------------
842 class PrecompileColorFilterShader final : public PrecompileShader {
843 public:
PrecompileColorFilterShader(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters)844     PrecompileColorFilterShader(SkSpan<const sk_sp<PrecompileShader>> shaders,
845                                 SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters)
846             : fShaders(shaders.begin(), shaders.end())
847             , fColorFilters(colorFilters.begin(), colorFilters.end()) {
848         fNumShaderCombos = 0;
849         for (const auto& s : fShaders) {
850             fNumShaderCombos += s->priv().numCombinations();
851         }
852         fNumColorFilterCombos = 0;
853         for (const auto& cf : fColorFilters) {
854             fNumColorFilterCombos += cf->priv().numCombinations();
855         }
856     }
857 
858 private:
numChildCombinations() const859     int numChildCombinations() const override { return fNumShaderCombos * fNumColorFilterCombos; }
860 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const861     void addToKey(const KeyContext& keyContext,
862                   PaintParamsKeyBuilder* builder,
863                   PipelineDataGatherer* gatherer,
864                   int desiredCombination) const override {
865         SkASSERT(desiredCombination < this->numCombinations());
866 
867         int desiredShaderCombination = desiredCombination % fNumShaderCombos;
868         int desiredColorFilterCombination = desiredCombination / fNumShaderCombos;
869         SkASSERT(desiredColorFilterCombination < fNumColorFilterCombos);
870 
871         Compose(keyContext, builder, gatherer,
872                 /* addInnerToKey= */ [&]() -> void {
873                     AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders,
874                                                desiredShaderCombination);
875                 },
876                 /* addOuterToKey= */ [&]() -> void {
877                     AddToKey<PrecompileColorFilter>(keyContext, builder, gatherer, fColorFilters,
878                                                     desiredColorFilterCombination);
879                 });
880     }
881 
882     std::vector<sk_sp<PrecompileShader>>      fShaders;
883     std::vector<sk_sp<PrecompileColorFilter>> fColorFilters;
884     int fNumShaderCombos;
885     int fNumColorFilterCombos;
886 };
887 
ColorFilter(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters)888 sk_sp<PrecompileShader> PrecompileShaders::ColorFilter(
889         SkSpan<const sk_sp<PrecompileShader>> shaders,
890         SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters) {
891     return sk_make_sp<PrecompileColorFilterShader>(std::move(shaders), std::move(colorFilters));
892 }
893 
894 //--------------------------------------------------------------------------------------------------
895 class PrecompileWorkingColorSpaceShader final : public PrecompileShader {
896 public:
PrecompileWorkingColorSpaceShader(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<SkColorSpace>> colorSpaces)897     PrecompileWorkingColorSpaceShader(SkSpan<const sk_sp<PrecompileShader>> shaders,
898                                       SkSpan<const sk_sp<SkColorSpace>> colorSpaces)
899             : fShaders(shaders.begin(), shaders.end())
900             , fColorSpaces(colorSpaces.begin(), colorSpaces.end()) {
901         fNumShaderCombos = 0;
902         for (const auto& s : fShaders) {
903             fNumShaderCombos += s->priv().numCombinations();
904         }
905     }
906 
907 private:
numChildCombinations() const908     int numChildCombinations() const override { return fNumShaderCombos * fColorSpaces.size(); }
909 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const910     void addToKey(const KeyContext& keyContext,
911                   PaintParamsKeyBuilder* builder,
912                   PipelineDataGatherer* gatherer,
913                   int desiredCombination) const override {
914         SkASSERT(desiredCombination < this->numCombinations());
915 
916         int desiredShaderCombination = desiredCombination % fNumShaderCombos;
917         int desiredColorSpaceCombination = desiredCombination / fNumShaderCombos;
918         SkASSERT(desiredColorSpaceCombination < (int) fColorSpaces.size());
919 
920         const SkColorInfo& dstInfo = keyContext.dstColorInfo();
921         const SkAlphaType dstAT = dstInfo.alphaType();
922         sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace();
923         if (!dstCS) {
924             dstCS = SkColorSpace::MakeSRGB();
925         }
926 
927         sk_sp<SkColorSpace> workingCS = fColorSpaces[desiredColorSpaceCombination];
928         SkColorInfo workingInfo(dstInfo.colorType(), dstAT, workingCS);
929         KeyContextWithColorInfo workingContext(keyContext, workingInfo);
930 
931         Compose(keyContext, builder, gatherer,
932                 /* addInnerToKey= */ [&]() -> void {
933                     AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders,
934                                                desiredShaderCombination);
935                 },
936                 /* addOuterToKey= */ [&]() -> void {
937                     ColorSpaceTransformBlock::ColorSpaceTransformData data(
938                             workingCS.get(), dstAT, dstCS.get(), dstAT);
939                     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
940                 });
941     }
942 
943     std::vector<sk_sp<PrecompileShader>> fShaders;
944     std::vector<sk_sp<SkColorSpace>>     fColorSpaces;
945     int fNumShaderCombos;
946 };
947 
WorkingColorSpace(SkSpan<const sk_sp<PrecompileShader>> shaders,SkSpan<const sk_sp<SkColorSpace>> colorSpaces)948 sk_sp<PrecompileShader> PrecompileShaders::WorkingColorSpace(
949         SkSpan<const sk_sp<PrecompileShader>> shaders,
950         SkSpan<const sk_sp<SkColorSpace>> colorSpaces) {
951     return sk_make_sp<PrecompileWorkingColorSpaceShader>(std::move(shaders),
952                                                          std::move(colorSpaces));
953 }
954 
955 //--------------------------------------------------------------------------------------------------
956 // In Graphite this acts as a non-elidable LocalMatrixShader
957 class PrecompileCTMShader final : public PrecompileShader {
958 public:
PrecompileCTMShader(SkSpan<const sk_sp<PrecompileShader>> wrapped)959     PrecompileCTMShader(SkSpan<const sk_sp<PrecompileShader>> wrapped)
960             : fWrapped(wrapped.begin(), wrapped.end()) {
961         fNumWrappedCombos = 0;
962         for (const auto& s : fWrapped) {
963             fNumWrappedCombos += s->priv().numCombinations();
964         }
965     }
966 
isConstant(int desiredCombination) const967     bool isConstant(int desiredCombination) const override {
968         SkASSERT(desiredCombination < fNumWrappedCombos);
969 
970         std::pair<sk_sp<PrecompileShader>, int> wrapped =
971                 PrecompileBase::SelectOption(SkSpan(fWrapped), desiredCombination);
972         if (wrapped.first) {
973             return wrapped.first->priv().isConstant(wrapped.second);
974         }
975 
976         return false;
977     }
978 
979 private:
numChildCombinations() const980     int numChildCombinations() const override { return fNumWrappedCombos; }
981 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const982     void addToKey(const KeyContext& keyContext,
983                   PaintParamsKeyBuilder* builder,
984                   PipelineDataGatherer* gatherer,
985                   int desiredCombination) const override {
986         SkASSERT(desiredCombination < fNumWrappedCombos);
987 
988         LocalMatrixShaderBlock::LMShaderData kIgnoredLMShaderData(SkMatrix::I());
989 
990         LocalMatrixShaderBlock::BeginBlock(keyContext, builder, gatherer, kIgnoredLMShaderData);
991 
992             AddToKey<PrecompileShader>(keyContext, builder, gatherer, fWrapped, desiredCombination);
993 
994         builder->endBlock();
995     }
996 
997     std::vector<sk_sp<PrecompileShader>> fWrapped;
998     int fNumWrappedCombos;
999 };
1000 
CTM(SkSpan<const sk_sp<PrecompileShader>> wrapped)1001 sk_sp<PrecompileShader> PrecompileShadersPriv::CTM(SkSpan<const sk_sp<PrecompileShader>> wrapped) {
1002     return sk_make_sp<PrecompileCTMShader>(std::move(wrapped));
1003 }
1004 
1005 //--------------------------------------------------------------------------------------------------
1006 class PrecompileBlurShader final : public PrecompileShader {
1007 public:
PrecompileBlurShader(sk_sp<PrecompileShader> wrapped)1008     PrecompileBlurShader(sk_sp<PrecompileShader> wrapped)
1009             : fWrapped(std::move(wrapped)) {
1010         fNumWrappedCombos = fWrapped->priv().numCombinations();
1011     }
1012 
1013 private:
1014     // 6 known 1D blur effects + 6 known 2D blur effects
1015     inline static constexpr int kNumIntrinsicCombinations = 12;
1016 
numIntrinsicCombinations() const1017     int numIntrinsicCombinations() const override { return kNumIntrinsicCombinations; }
1018 
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 < this->numCombinations());
1026 
1027         using namespace SkKnownRuntimeEffects;
1028 
1029         int desiredBlurCombination = desiredCombination % kNumIntrinsicCombinations;
1030         int desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations;
1031         SkASSERT(desiredWrappedCombination < fNumWrappedCombos);
1032 
1033         static const StableKey kIDs[kNumIntrinsicCombinations] = {
1034                 StableKey::k1DBlur4,  StableKey::k1DBlur8,  StableKey::k1DBlur12,
1035                 StableKey::k1DBlur16, StableKey::k1DBlur20, StableKey::k1DBlur28,
1036 
1037                 StableKey::k2DBlur4,  StableKey::k2DBlur8,  StableKey::k2DBlur12,
1038                 StableKey::k2DBlur16, StableKey::k2DBlur20, StableKey::k2DBlur28,
1039         };
1040 
1041         const SkRuntimeEffect* fEffect = GetKnownRuntimeEffect(kIDs[desiredBlurCombination]);
1042 
1043         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1044 
1045         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(fEffect) });
1046             fWrapped->priv().addToKey(childContext, builder, gatherer, desiredWrappedCombination);
1047         builder->endBlock();
1048     }
1049 
1050     sk_sp<PrecompileShader> fWrapped;
1051     int fNumWrappedCombos;
1052 };
1053 
Blur(sk_sp<PrecompileShader> wrapped)1054 sk_sp<PrecompileShader> PrecompileShadersPriv::Blur(sk_sp<PrecompileShader> wrapped) {
1055     return sk_make_sp<PrecompileBlurShader>(std::move(wrapped));
1056 }
1057 
1058 //--------------------------------------------------------------------------------------------------
1059 class PrecompileMatrixConvolutionShader final : public PrecompileShader {
1060 public:
PrecompileMatrixConvolutionShader(sk_sp<PrecompileShader> wrapped)1061     PrecompileMatrixConvolutionShader(sk_sp<PrecompileShader> wrapped)
1062             : fWrapped(std::move(wrapped)) {
1063         fNumWrappedCombos = fWrapped->priv().numCombinations();
1064 
1065         // When the matrix convolution ImageFilter uses a texture we know it will only ever
1066         // be SkFilterMode::kNearest and SkTileMode::kClamp.
1067         // TODO: add a PrecompileImageShaderFlags to further limit the raw image shader
1068         // combinations. Right now we're getting two combinations for the raw shader
1069         // (sk_image_shader and sk_hw_image_shader).
1070         fRawImageShader =
1071                 PrecompileShadersPriv::RawImage(PrecompileImageShaderFlags::kExcludeCubic);
1072         fNumRawImageShaderCombos = fRawImageShader->priv().numCombinations();
1073     }
1074 
1075 private:
numIntrinsicCombinations() const1076     int numIntrinsicCombinations() const override {
1077         // The uniform version only has one option but the two texture-based versions will
1078         // have as many combinations as the raw image shader.
1079         return 1 + 2 * fNumRawImageShaderCombos;
1080     }
1081 
numChildCombinations() const1082     int numChildCombinations() const override { return fNumWrappedCombos; }
1083 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const1084     void addToKey(const KeyContext& keyContext,
1085                   PaintParamsKeyBuilder* builder,
1086                   PipelineDataGatherer* gatherer,
1087                   int desiredCombination) const override {
1088 
1089         int desiredTextureCombination = 0;
1090 
1091         const int desiredWrappedCombination = desiredCombination % fNumWrappedCombos;
1092         int remainingCombinations = desiredCombination / fNumWrappedCombos;
1093 
1094         SkKnownRuntimeEffects::StableKey stableKey = SkKnownRuntimeEffects::StableKey::kInvalid;
1095         if (remainingCombinations == 0) {
1096             stableKey = SkKnownRuntimeEffects::StableKey::kMatrixConvUniforms;
1097         } else {
1098             static constexpr SkKnownRuntimeEffects::StableKey kTextureBasedStableKeys[] = {
1099                     SkKnownRuntimeEffects::StableKey::kMatrixConvTexSm,
1100                     SkKnownRuntimeEffects::StableKey::kMatrixConvTexLg,
1101             };
1102 
1103             --remainingCombinations;
1104             stableKey = kTextureBasedStableKeys[remainingCombinations % 2];
1105             desiredTextureCombination = remainingCombinations / 2;
1106             SkASSERT(desiredTextureCombination < fNumRawImageShaderCombos);
1107         }
1108 
1109         const SkRuntimeEffect* fEffect = GetKnownRuntimeEffect(stableKey);
1110 
1111         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1112 
1113         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(fEffect) });
1114             fWrapped->priv().addToKey(childContext, builder, gatherer, desiredWrappedCombination);
1115             if (stableKey != SkKnownRuntimeEffects::StableKey::kMatrixConvUniforms) {
1116                 fRawImageShader->priv().addToKey(childContext, builder, gatherer,
1117                                                  desiredTextureCombination);
1118             }
1119         builder->endBlock();
1120     }
1121 
1122     sk_sp<PrecompileShader> fWrapped;
1123     int fNumWrappedCombos;
1124     sk_sp<PrecompileShader> fRawImageShader;
1125     int fNumRawImageShaderCombos;
1126 };
1127 
MatrixConvolution(sk_sp<skgpu::graphite::PrecompileShader> wrapped)1128 sk_sp<PrecompileShader> PrecompileShadersPriv::MatrixConvolution(
1129         sk_sp<skgpu::graphite::PrecompileShader> wrapped) {
1130     return sk_make_sp<PrecompileMatrixConvolutionShader>(std::move(wrapped));
1131 }
1132 
1133 //--------------------------------------------------------------------------------------------------
1134 class PrecompileMorphologyShader final : public PrecompileShader {
1135 public:
PrecompileMorphologyShader(sk_sp<PrecompileShader> wrapped,SkKnownRuntimeEffects::StableKey stableKey)1136     PrecompileMorphologyShader(sk_sp<PrecompileShader> wrapped,
1137                                SkKnownRuntimeEffects::StableKey stableKey)
1138             : fWrapped(std::move(wrapped))
1139             , fStableKey(stableKey) {
1140         fNumWrappedCombos = fWrapped->priv().numCombinations();
1141         SkASSERT(stableKey == SkKnownRuntimeEffects::StableKey::kLinearMorphology ||
1142                  stableKey == SkKnownRuntimeEffects::StableKey::kSparseMorphology);
1143     }
1144 
1145 private:
numChildCombinations() const1146     int numChildCombinations() const override { return fNumWrappedCombos; }
1147 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const1148     void addToKey(const KeyContext& keyContext,
1149                   PaintParamsKeyBuilder* builder,
1150                   PipelineDataGatherer* gatherer,
1151                   int desiredCombination) const override {
1152         SkASSERT(desiredCombination < fNumWrappedCombos);
1153 
1154         const SkRuntimeEffect* effect = GetKnownRuntimeEffect(fStableKey);
1155 
1156         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1157 
1158         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(effect) });
1159             fWrapped->priv().addToKey(childContext, builder, gatherer, desiredCombination);
1160         builder->endBlock();
1161     }
1162 
1163     sk_sp<PrecompileShader> fWrapped;
1164     int fNumWrappedCombos;
1165     SkKnownRuntimeEffects::StableKey fStableKey;
1166 };
1167 
LinearMorphology(sk_sp<PrecompileShader> wrapped)1168 sk_sp<PrecompileShader> PrecompileShadersPriv::LinearMorphology(sk_sp<PrecompileShader> wrapped) {
1169     return sk_make_sp<PrecompileMorphologyShader>(
1170             std::move(wrapped),
1171             SkKnownRuntimeEffects::StableKey::kLinearMorphology);
1172 }
1173 
SparseMorphology(sk_sp<PrecompileShader> wrapped)1174 sk_sp<PrecompileShader> PrecompileShadersPriv::SparseMorphology(sk_sp<PrecompileShader> wrapped) {
1175     return sk_make_sp<PrecompileMorphologyShader>(
1176             std::move(wrapped),
1177             SkKnownRuntimeEffects::StableKey::kSparseMorphology);
1178 }
1179 
1180 //--------------------------------------------------------------------------------------------------
1181 class PrecompileDisplacementShader final : public PrecompileShader {
1182 public:
PrecompileDisplacementShader(sk_sp<PrecompileShader> displacement,sk_sp<PrecompileShader> color)1183     PrecompileDisplacementShader(sk_sp<PrecompileShader> displacement,
1184                                  sk_sp<PrecompileShader> color)
1185             : fDisplacement(std::move(displacement))
1186             , fColor(std::move(color)) {
1187         fNumDisplacementCombos = fDisplacement->priv().numCombinations();
1188         fNumColorCombos = fColor->priv().numCombinations();
1189     }
1190 
1191 private:
numChildCombinations() const1192     int numChildCombinations() const override { return fNumDisplacementCombos * fNumColorCombos; }
1193 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const1194     void addToKey(const KeyContext& keyContext,
1195                   PaintParamsKeyBuilder* builder,
1196                   PipelineDataGatherer* gatherer,
1197                   int desiredCombination) const override {
1198         SkASSERT(desiredCombination < this->numChildCombinations());
1199 
1200         const int desiredDisplacementCombination = desiredCombination % fNumDisplacementCombos;
1201         const int desiredColorCombination = desiredCombination / fNumDisplacementCombos;
1202         SkASSERT(desiredColorCombination < fNumColorCombos);
1203 
1204         const SkRuntimeEffect* fEffect =
1205                 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kDisplacement);
1206 
1207         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1208 
1209         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(fEffect) });
1210             fDisplacement->priv().addToKey(childContext, builder, gatherer,
1211                                            desiredDisplacementCombination);
1212             fColor->priv().addToKey(childContext, builder, gatherer,
1213                                     desiredColorCombination);
1214         builder->endBlock();
1215     }
1216 
1217     sk_sp<PrecompileShader> fDisplacement;
1218     int fNumDisplacementCombos;
1219     sk_sp<PrecompileShader> fColor;
1220     int fNumColorCombos;
1221 };
1222 
1223 //--------------------------------------------------------------------------------------------------
Displacement(sk_sp<PrecompileShader> displacement,sk_sp<PrecompileShader> color)1224 sk_sp<PrecompileShader> PrecompileShadersPriv::Displacement(sk_sp<PrecompileShader> displacement,
1225                                                             sk_sp<PrecompileShader> color) {
1226     return sk_make_sp<PrecompileDisplacementShader>(std::move(displacement), std::move(color));
1227 }
1228 
1229 //--------------------------------------------------------------------------------------------------
1230 class PrecompileLightingShader final : public PrecompileShader {
1231 public:
PrecompileLightingShader(sk_sp<PrecompileShader> wrapped)1232     PrecompileLightingShader(sk_sp<PrecompileShader> wrapped)
1233             : fWrapped(std::move(wrapped)) {
1234         fNumWrappedCombos = fWrapped->priv().numCombinations();
1235     }
1236 
1237 private:
numChildCombinations() const1238     int numChildCombinations() const override { return fNumWrappedCombos; }
1239 
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const1240     void addToKey(const KeyContext& keyContext,
1241                   PaintParamsKeyBuilder* builder,
1242                   PipelineDataGatherer* gatherer,
1243                   int desiredCombination) const override {
1244         SkASSERT(desiredCombination < fNumWrappedCombos);
1245 
1246         const SkRuntimeEffect* normalEffect =
1247                 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kNormal);
1248         const SkRuntimeEffect* lightingEffect =
1249                 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kLighting);
1250 
1251         KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1252 
1253         RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
1254                                        { sk_ref_sp(lightingEffect) });
1255             RuntimeEffectBlock::BeginBlock(childContext, builder, gatherer,
1256                                            { sk_ref_sp(normalEffect) });
1257                 fWrapped->priv().addToKey(childContext, builder, gatherer, desiredCombination);
1258             builder->endBlock();
1259         builder->endBlock();
1260     }
1261 
1262     sk_sp<PrecompileShader> fWrapped;
1263     int fNumWrappedCombos;
1264 };
1265 
Lighting(sk_sp<PrecompileShader> wrapped)1266 sk_sp<PrecompileShader> PrecompileShadersPriv::Lighting(sk_sp<PrecompileShader> wrapped) {
1267     return sk_make_sp<PrecompileLightingShader>(std::move(wrapped));
1268 }
1269 
1270 //--------------------------------------------------------------------------------------------------
1271 
1272 } // namespace skgpu::graphite
1273