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