• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/graphite/KeyHelpers.h"
9 
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkM44.h"
15 #include "include/core/SkScalar.h"
16 #include "include/effects/SkRuntimeEffect.h"
17 #include "include/gpu/graphite/Surface.h"
18 #include "src/base/SkHalf.h"
19 #include "src/core/SkBlendModeBlender.h"
20 #include "src/core/SkBlenderBase.h"
21 #include "src/core/SkColorSpacePriv.h"
22 #include "src/core/SkDebugUtils.h"
23 #include "src/core/SkRuntimeBlender.h"
24 #include "src/core/SkRuntimeEffectPriv.h"
25 #include "src/core/SkYUVMath.h"
26 #include "src/effects/colorfilters/SkBlendModeColorFilter.h"
27 #include "src/effects/colorfilters/SkColorFilterBase.h"
28 #include "src/effects/colorfilters/SkColorSpaceXformColorFilter.h"
29 #include "src/effects/colorfilters/SkComposeColorFilter.h"
30 #include "src/effects/colorfilters/SkGaussianColorFilter.h"
31 #include "src/effects/colorfilters/SkMatrixColorFilter.h"
32 #include "src/effects/colorfilters/SkRuntimeColorFilter.h"
33 #include "src/effects/colorfilters/SkTableColorFilter.h"
34 #include "src/effects/colorfilters/SkWorkingFormatColorFilter.h"
35 #include "src/gpu/Blend.h"
36 #include "src/gpu/DitherUtils.h"
37 #include "src/gpu/Swizzle.h"
38 #include "src/gpu/graphite/Caps.h"
39 #include "src/gpu/graphite/DrawContext.h"
40 #include "src/gpu/graphite/Image_Base_Graphite.h"
41 #include "src/gpu/graphite/Image_Graphite.h"
42 #include "src/gpu/graphite/Image_YUVA_Graphite.h"
43 #include "src/gpu/graphite/KeyContext.h"
44 #include "src/gpu/graphite/KeyHelpers.h"
45 #include "src/gpu/graphite/Log.h"
46 #include "src/gpu/graphite/PaintParams.h"
47 #include "src/gpu/graphite/PaintParamsKey.h"
48 #include "src/gpu/graphite/PipelineData.h"
49 #include "src/gpu/graphite/ReadSwizzle.h"
50 #include "src/gpu/graphite/RecorderPriv.h"
51 #include "src/gpu/graphite/ResourceProvider.h"
52 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
53 #include "src/gpu/graphite/ShaderCodeDictionary.h"
54 #include "src/gpu/graphite/Surface_Graphite.h"
55 #include "src/gpu/graphite/Texture.h"
56 #include "src/gpu/graphite/TextureProxy.h"
57 #include "src/gpu/graphite/TextureProxyView.h"
58 #include "src/gpu/graphite/TextureUtils.h"
59 #include "src/gpu/graphite/Uniform.h"
60 #include "src/gpu/graphite/UniformManager.h"
61 #include "src/image/SkImage_Base.h"
62 #include "src/shaders/SkBlendShader.h"
63 #include "src/shaders/SkColorFilterShader.h"
64 #include "src/shaders/SkColorShader.h"
65 #include "src/shaders/SkCoordClampShader.h"
66 #include "src/shaders/SkEmptyShader.h"
67 #include "src/shaders/SkImageShader.h"
68 #include "src/shaders/SkLocalMatrixShader.h"
69 #include "src/shaders/SkPerlinNoiseShaderImpl.h"
70 #include "src/shaders/SkPerlinNoiseShaderType.h"
71 #include "src/shaders/SkPictureShader.h"
72 #include "src/shaders/SkRuntimeShader.h"
73 #include "src/shaders/SkShaderBase.h"
74 #include "src/shaders/SkTransformShader.h"
75 #include "src/shaders/SkTriColorShader.h"
76 #include "src/shaders/SkWorkingColorSpaceShader.h"
77 #include "src/shaders/gradients/SkConicalGradient.h"
78 #include "src/shaders/gradients/SkLinearGradient.h"
79 #include "src/shaders/gradients/SkRadialGradient.h"
80 #include "src/shaders/gradients/SkSweepGradient.h"
81 
82 using namespace skia_private;
83 
84 namespace skgpu::graphite {
85 
86 //--------------------------------------------------------------------------------------------------
87 
88 namespace {
89 
90 // Automatically calls beginStruct() with the required alignment and endStruct() when it is deleted.
91 // Automatically registers uniform expectations in debug builds.
92 class ScopedUniformWriter {
93 public:
ScopedUniformWriter(PipelineDataGatherer * gatherer,const ShaderCodeDictionary * dict,BuiltInCodeSnippetID codeSnippetID)94     ScopedUniformWriter(PipelineDataGatherer* gatherer,
95                         const ShaderCodeDictionary* dict,
96                         BuiltInCodeSnippetID codeSnippetID)
97             : ScopedUniformWriter(gatherer, dict->getEntry(codeSnippetID)) {}
98 
~ScopedUniformWriter()99     ~ScopedUniformWriter() {
100         if (fGatherer) {
101             fGatherer->endStruct();
102         }
103     }
104 
105 private:
ScopedUniformWriter(PipelineDataGatherer * gatherer,const ShaderSnippet * snippet)106     ScopedUniformWriter(PipelineDataGatherer* gatherer, const ShaderSnippet* snippet)
107 #if defined(SK_DEBUG)
108         : fValidator(gatherer, snippet->fUniforms, SkToBool(snippet->fUniformStructName))
109 #endif
110     {
111         if (snippet->fUniformStructName) {
112             gatherer->beginStruct(snippet->fRequiredAlignment);
113             fGatherer = gatherer;
114         } else {
115             fGatherer = nullptr;
116         }
117     }
118 
119     PipelineDataGatherer* fGatherer;
120     SkDEBUGCODE(UniformExpectationsValidator fValidator;)
121 };
122 
123 #define BEGIN_WRITE_UNIFORMS(gatherer, dict, codeSnippetID) \
124     ScopedUniformWriter scope{gatherer, dict, codeSnippetID};
125 
add_solid_uniform_data(const ShaderCodeDictionary * dict,const SkPMColor4f & premulColor,PipelineDataGatherer * gatherer)126 void add_solid_uniform_data(const ShaderCodeDictionary* dict,
127                             const SkPMColor4f& premulColor,
128                             PipelineDataGatherer* gatherer) {
129     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kSolidColorShader)
130     gatherer->write(premulColor);
131 }
132 
133 } // anonymous namespace
134 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkPMColor4f & premulColor)135 void SolidColorShaderBlock::AddBlock(const KeyContext& keyContext,
136                                      PaintParamsKeyBuilder* builder,
137                                      PipelineDataGatherer* gatherer,
138                                      const SkPMColor4f& premulColor) {
139     add_solid_uniform_data(keyContext.dict(), premulColor, gatherer);
140 
141     builder->addBlock(BuiltInCodeSnippetID::kSolidColorShader);
142 }
143 
144 //--------------------------------------------------------------------------------------------------
145 
146 namespace {
147 
add_rgb_paint_color_uniform_data(const ShaderCodeDictionary * dict,const SkPMColor4f & premulColor,PipelineDataGatherer * gatherer)148 void add_rgb_paint_color_uniform_data(const ShaderCodeDictionary* dict,
149                                       const SkPMColor4f& premulColor,
150                                       PipelineDataGatherer* gatherer) {
151     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kRGBPaintColor)
152     gatherer->writePaintColor(premulColor);
153 }
154 
add_alpha_only_paint_color_uniform_data(const ShaderCodeDictionary * dict,const SkPMColor4f & premulColor,PipelineDataGatherer * gatherer)155 void add_alpha_only_paint_color_uniform_data(const ShaderCodeDictionary* dict,
156                                              const SkPMColor4f& premulColor,
157                                              PipelineDataGatherer* gatherer) {
158     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAlphaOnlyPaintColor)
159     gatherer->writePaintColor(premulColor);
160 }
161 
162 } // anonymous namespace
163 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)164 void RGBPaintColorBlock::AddBlock(const KeyContext& keyContext,
165                                   PaintParamsKeyBuilder* builder,
166                                   PipelineDataGatherer* gatherer) {
167     add_rgb_paint_color_uniform_data(keyContext.dict(), keyContext.paintColor(), gatherer);
168 
169     builder->addBlock(BuiltInCodeSnippetID::kRGBPaintColor);
170 }
171 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)172 void AlphaOnlyPaintColorBlock::AddBlock(const KeyContext& keyContext,
173                                         PaintParamsKeyBuilder* builder,
174                                         PipelineDataGatherer* gatherer) {
175     add_alpha_only_paint_color_uniform_data(keyContext.dict(), keyContext.paintColor(), gatherer);
176 
177     builder->addBlock(BuiltInCodeSnippetID::kAlphaOnlyPaintColor);
178 }
179 
180 //--------------------------------------------------------------------------------------------------
181 
182 namespace {
183 
add_gradient_preamble(const GradientShaderBlocks::GradientData & gradData,PipelineDataGatherer * gatherer)184 void add_gradient_preamble(const GradientShaderBlocks::GradientData& gradData,
185                            PipelineDataGatherer* gatherer) {
186     constexpr int kInternalStopLimit = GradientShaderBlocks::GradientData::kNumInternalStorageStops;
187 
188     if (gradData.fNumStops <= kInternalStopLimit) {
189         if (gradData.fNumStops <= 4) {
190             // Round up to 4 stops.
191             gatherer->writeArray(SkSpan{gradData.fColors, 4});
192             gatherer->write(gradData.fOffsets[0]);
193         } else if (gradData.fNumStops <= 8) {
194             // Round up to 8 stops.
195             gatherer->writeArray(SkSpan{gradData.fColors, 8});
196             gatherer->writeArray(SkSpan{gradData.fOffsets, 2});
197         } else {
198             // Did kNumInternalStorageStops change?
199             SkUNREACHABLE;
200         }
201     }
202 }
203 
204 // All the gradients share a common postamble of:
205 //   numStops - for texture-based gradients
206 //   tilemode
207 //   colorSpace
208 //   doUnPremul
add_gradient_postamble(const GradientShaderBlocks::GradientData & gradData,int bufferOffset,PipelineDataGatherer * gatherer)209 void add_gradient_postamble(const GradientShaderBlocks::GradientData& gradData,
210                             int bufferOffset,
211                             PipelineDataGatherer* gatherer) {
212     using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
213 
214     constexpr int kInternalStopLimit = GradientShaderBlocks::GradientData::kNumInternalStorageStops;
215 
216     static_assert(static_cast<int>(ColorSpace::kLab)           == 2);
217     static_assert(static_cast<int>(ColorSpace::kOKLab)         == 3);
218     static_assert(static_cast<int>(ColorSpace::kOKLabGamutMap) == 4);
219     static_assert(static_cast<int>(ColorSpace::kLCH)           == 5);
220     static_assert(static_cast<int>(ColorSpace::kOKLCH)         == 6);
221     static_assert(static_cast<int>(ColorSpace::kOKLCHGamutMap) == 7);
222     static_assert(static_cast<int>(ColorSpace::kHSL)           == 9);
223     static_assert(static_cast<int>(ColorSpace::kHWB)           == 10);
224 
225     bool inputPremul = static_cast<bool>(gradData.fInterpolation.fInPremul);
226 
227     if (gradData.fNumStops > kInternalStopLimit) {
228         gatherer->write(gradData.fNumStops);
229         if (gradData.fUseStorageBuffer) {
230             gatherer->write(bufferOffset);
231         }
232     }
233 
234     gatherer->write(static_cast<int>(gradData.fTM));
235     gatherer->write(static_cast<int>(gradData.fInterpolation.fColorSpace));
236     gatherer->write(static_cast<int>(inputPremul));
237 }
238 
add_linear_gradient_uniform_data(const ShaderCodeDictionary * dict,BuiltInCodeSnippetID codeSnippetID,const GradientShaderBlocks::GradientData & gradData,int bufferOffset,PipelineDataGatherer * gatherer)239 void add_linear_gradient_uniform_data(const ShaderCodeDictionary* dict,
240                                       BuiltInCodeSnippetID codeSnippetID,
241                                       const GradientShaderBlocks::GradientData& gradData,
242                                       int bufferOffset,
243                                       PipelineDataGatherer* gatherer) {
244     BEGIN_WRITE_UNIFORMS(gatherer, dict, codeSnippetID)
245 
246     add_gradient_preamble(gradData, gatherer);
247     add_gradient_postamble(gradData, bufferOffset, gatherer);
248 };
249 
add_radial_gradient_uniform_data(const ShaderCodeDictionary * dict,BuiltInCodeSnippetID codeSnippetID,const GradientShaderBlocks::GradientData & gradData,int bufferOffset,PipelineDataGatherer * gatherer)250 void add_radial_gradient_uniform_data(const ShaderCodeDictionary* dict,
251                                       BuiltInCodeSnippetID codeSnippetID,
252                                       const GradientShaderBlocks::GradientData& gradData,
253                                       int bufferOffset,
254                                       PipelineDataGatherer* gatherer) {
255     BEGIN_WRITE_UNIFORMS(gatherer, dict, codeSnippetID)
256 
257     add_gradient_preamble(gradData, gatherer);
258     add_gradient_postamble(gradData, bufferOffset, gatherer);
259 };
260 
add_sweep_gradient_uniform_data(const ShaderCodeDictionary * dict,BuiltInCodeSnippetID codeSnippetID,const GradientShaderBlocks::GradientData & gradData,int bufferOffset,PipelineDataGatherer * gatherer)261 void add_sweep_gradient_uniform_data(const ShaderCodeDictionary* dict,
262                                      BuiltInCodeSnippetID codeSnippetID,
263                                      const GradientShaderBlocks::GradientData& gradData,
264                                      int bufferOffset,
265                                      PipelineDataGatherer* gatherer) {
266     BEGIN_WRITE_UNIFORMS(gatherer, dict, codeSnippetID)
267 
268     add_gradient_preamble(gradData, gatherer);
269     gatherer->write(gradData.fBias);
270     gatherer->write(gradData.fScale);
271     add_gradient_postamble(gradData, bufferOffset, gatherer);
272 };
273 
add_conical_gradient_uniform_data(const ShaderCodeDictionary * dict,BuiltInCodeSnippetID codeSnippetID,const GradientShaderBlocks::GradientData & gradData,int bufferOffset,PipelineDataGatherer * gatherer)274 void add_conical_gradient_uniform_data(const ShaderCodeDictionary* dict,
275                                        BuiltInCodeSnippetID codeSnippetID,
276                                        const GradientShaderBlocks::GradientData& gradData,
277                                        int bufferOffset,
278                                        PipelineDataGatherer* gatherer) {
279     BEGIN_WRITE_UNIFORMS(gatherer, dict, codeSnippetID)
280 
281     float dRadius = gradData.fRadii[1] - gradData.fRadii[0];
282     bool isRadial = SkPoint::Distance(gradData.fPoints[1], gradData.fPoints[0])
283                                       < SK_ScalarNearlyZero;
284 
285     // When a == 0, encode invA == 1 for radial case, and invA == 0 for linear edge case.
286     float a = 0;
287     float invA = 1;
288     if (!isRadial) {
289         a = 1 - dRadius * dRadius;
290         if (std::abs(a) > SK_ScalarNearlyZero) {
291             invA = 1.0 / (2.0 * a);
292         } else {
293             a = 0;
294             invA = 0;
295         }
296     } else {
297         // Since radius0 is being scaled by 1 / dRadius, and the original radius
298         // is always positive, this gives us the original sign of dRadius.
299         dRadius = gradData.fRadii[0] > 0 ? 1 : -1;
300     }
301 
302     add_gradient_preamble(gradData, gatherer);
303     gatherer->write(gradData.fRadii[0]);
304     gatherer->write(dRadius);
305     gatherer->write(a);
306     gatherer->write(invA);
307     add_gradient_postamble(gradData, bufferOffset, gatherer);
308 };
309 
310 } // anonymous namespace
311 
312 // Writes the color and offset data directly in the gatherer gradient buffer and returns the
313 // offset the data begins at in the buffer.
write_color_and_offset_bufdata(int numStops,const SkPMColor4f * colors,const float * offsets,const SkGradientBaseShader * shader,PipelineDataGatherer * gatherer)314 static int write_color_and_offset_bufdata(int numStops,
315                                            const SkPMColor4f* colors,
316                                            const float* offsets,
317                                            const SkGradientBaseShader* shader,
318                                            PipelineDataGatherer* gatherer) {
319     auto [dstData, bufferOffset] = gatherer->allocateGradientData(numStops, shader);
320     if (dstData) {
321         // Data doesn't already exist so we need to write it.
322         // Writes all offset data, then color data. This way when binary searching through the
323         // offsets, there is better cache locality.
324         for (int i = 0, colorIdx = numStops; i < numStops; i++, colorIdx+=4) {
325             float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops - 1);
326             SkASSERT(offset >= 0.0f && offset <= 1.0f);
327 
328             dstData[i] = offset;
329             dstData[colorIdx + 0] = colors[i].fR;
330             dstData[colorIdx + 1] = colors[i].fG;
331             dstData[colorIdx + 2] = colors[i].fB;
332             dstData[colorIdx + 3] = colors[i].fA;
333         }
334     }
335 
336     return bufferOffset;
337 }
338 
GradientData(SkShaderBase::GradientType type,int numStops,bool useStorageBuffer)339 GradientShaderBlocks::GradientData::GradientData(SkShaderBase::GradientType type,
340                                                  int numStops,
341                                                  bool useStorageBuffer)
342         : fType(type)
343         , fPoints{{0.0f, 0.0f}, {0.0f, 0.0f}}
344         , fRadii{0.0f, 0.0f}
345         , fBias(0.0f)
346         , fScale(0.0f)
347         , fTM(SkTileMode::kClamp)
348         , fNumStops(numStops)
349         , fUseStorageBuffer(useStorageBuffer)
350         , fSrcColors(nullptr)
351         , fSrcOffsets(nullptr) {
352     sk_bzero(fColors, sizeof(fColors));
353     sk_bzero(fOffsets, sizeof(fOffsets));
354 }
355 
GradientData(SkShaderBase::GradientType type,SkPoint point0,SkPoint point1,float radius0,float radius1,float bias,float scale,SkTileMode tm,int numStops,const SkPMColor4f * colors,const float * offsets,const SkGradientBaseShader * shader,sk_sp<TextureProxy> colorsAndOffsetsProxy,bool useStorageBuffer,const SkGradientShader::Interpolation & interp)356 GradientShaderBlocks::GradientData::GradientData(SkShaderBase::GradientType type,
357                                                  SkPoint point0, SkPoint point1,
358                                                  float radius0, float radius1,
359                                                  float bias, float scale,
360                                                  SkTileMode tm,
361                                                  int numStops,
362                                                  const SkPMColor4f* colors,
363                                                  const float* offsets,
364                                                  const SkGradientBaseShader* shader,
365                                                  sk_sp<TextureProxy> colorsAndOffsetsProxy,
366                                                  bool useStorageBuffer,
367                                                  const SkGradientShader::Interpolation& interp)
368         : fType(type)
369         , fBias(bias)
370         , fScale(scale)
371         , fTM(tm)
372         , fNumStops(numStops)
373         , fUseStorageBuffer(useStorageBuffer)
374         , fSrcColors(colors)
375         , fSrcOffsets(offsets)
376         , fSrcShader(shader)
377         , fInterpolation(interp) {
378     SkASSERT(fNumStops >= 1);
379 
380     fPoints[0] = point0;
381     fPoints[1] = point1;
382     fRadii[0] = radius0;
383     fRadii[1] = radius1;
384 
385     if (fNumStops <= kNumInternalStorageStops) {
386         memcpy(fColors, colors, fNumStops * sizeof(SkColor4f));
387         float* rawOffsets = fOffsets[0].ptr();
388         if (offsets) {
389             memcpy(rawOffsets, offsets, fNumStops * sizeof(float));
390         } else {
391             for (int i = 0; i < fNumStops; ++i) {
392                 rawOffsets[i] = SkIntToFloat(i) / (fNumStops-1);
393             }
394         }
395 
396         // Extend the colors and offset, if necessary, to fill out the arrays.
397         // The unrolled binary search implementation assumes excess stops match the last real value.
398         for (int i = fNumStops; i < kNumInternalStorageStops; ++i) {
399             fColors[i] = fColors[fNumStops-1];
400             rawOffsets[i] = rawOffsets[fNumStops-1];
401         }
402     } else {
403         if (!fUseStorageBuffer) {
404             fColorsAndOffsetsProxy = std::move(colorsAndOffsetsProxy);
405             SkASSERT(fColorsAndOffsetsProxy);
406         }
407     }
408 }
409 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const GradientData & gradData)410 void GradientShaderBlocks::AddBlock(const KeyContext& keyContext,
411                                     PaintParamsKeyBuilder* builder,
412                                     PipelineDataGatherer* gatherer,
413                                     const GradientData& gradData) {
414     auto dict = keyContext.dict();
415 
416     int bufferOffset = 0;
417     if (gradData.fNumStops > GradientData::kNumInternalStorageStops && keyContext.recorder()) {
418         if (gradData.fUseStorageBuffer) {
419             bufferOffset = write_color_and_offset_bufdata(gradData.fNumStops,
420                                                           gradData.fSrcColors,
421                                                           gradData.fSrcOffsets,
422                                                           gradData.fSrcShader,
423                                                           gatherer);
424         } else {
425             SkASSERT(gradData.fColorsAndOffsetsProxy);
426             gatherer->add(gradData.fColorsAndOffsetsProxy,
427                           {SkFilterMode::kNearest, SkTileMode::kClamp});
428         }
429     }
430 
431     BuiltInCodeSnippetID codeSnippetID = BuiltInCodeSnippetID::kSolidColorShader;
432     switch (gradData.fType) {
433         case SkShaderBase::GradientType::kLinear:
434             codeSnippetID =
435                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kLinearGradientShader4
436                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kLinearGradientShader8
437                         : gradData.fUseStorageBuffer
438                             ? BuiltInCodeSnippetID::kLinearGradientShaderBuffer
439                             : BuiltInCodeSnippetID::kLinearGradientShaderTexture;
440             add_linear_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
441             break;
442         case SkShaderBase::GradientType::kRadial:
443             codeSnippetID =
444                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kRadialGradientShader4
445                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kRadialGradientShader8
446                         : gradData.fUseStorageBuffer
447                             ? BuiltInCodeSnippetID::kRadialGradientShaderBuffer
448                             : BuiltInCodeSnippetID::kRadialGradientShaderTexture;
449             add_radial_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
450             break;
451         case SkShaderBase::GradientType::kSweep:
452             codeSnippetID =
453                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kSweepGradientShader4
454                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kSweepGradientShader8
455                         : gradData.fUseStorageBuffer
456                             ? BuiltInCodeSnippetID::kSweepGradientShaderBuffer
457                             : BuiltInCodeSnippetID::kSweepGradientShaderTexture;
458             add_sweep_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
459             break;
460         case SkShaderBase::GradientType::kConical:
461             codeSnippetID =
462                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kConicalGradientShader4
463                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kConicalGradientShader8
464                         : gradData.fUseStorageBuffer
465                             ? BuiltInCodeSnippetID::kConicalGradientShaderBuffer
466                             : BuiltInCodeSnippetID::kConicalGradientShaderTexture;
467             add_conical_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
468             break;
469         case SkShaderBase::GradientType::kNone:
470         default:
471             SkDEBUGFAIL("Expected a gradient shader, but it wasn't one.");
472             break;
473     }
474 
475     builder->addBlock(codeSnippetID);
476 }
477 
478 //--------------------------------------------------------------------------------------------------
479 
480 namespace {
481 
add_localmatrixshader_uniform_data(const ShaderCodeDictionary * dict,const SkM44 & localMatrix,PipelineDataGatherer * gatherer)482 void add_localmatrixshader_uniform_data(const ShaderCodeDictionary* dict,
483                                         const SkM44& localMatrix,
484                                         PipelineDataGatherer* gatherer) {
485     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kLocalMatrixShader)
486 
487     gatherer->write(localMatrix);
488 }
489 
490 } // anonymous namespace
491 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const LMShaderData & lmShaderData)492 void LocalMatrixShaderBlock::BeginBlock(const KeyContext& keyContext,
493                                         PaintParamsKeyBuilder* builder,
494                                         PipelineDataGatherer* gatherer,
495                                         const LMShaderData& lmShaderData) {
496 
497     add_localmatrixshader_uniform_data(keyContext.dict(), lmShaderData.fLocalMatrix, gatherer);
498 
499     builder->beginBlock(lmShaderData.fHasPerspective
500                                 ? BuiltInCodeSnippetID::kLocalMatrixShaderPersp
501                                 : BuiltInCodeSnippetID::kLocalMatrixShader);
502 }
503 
504 //--------------------------------------------------------------------------------------------------
505 
506 namespace {
507 
add_color_space_uniforms(const SkColorSpaceXformSteps & steps,ReadSwizzle readSwizzle,PipelineDataGatherer * gatherer)508 void add_color_space_uniforms(const SkColorSpaceXformSteps& steps,
509                               ReadSwizzle readSwizzle,
510                               PipelineDataGatherer* gatherer) {
511     SkMatrix gamutTransform;
512     const float identity[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
513     // TODO: it seems odd to copy this into an SkMatrix just to write it to the gatherer
514     // fSrcToDstMatrix is column-major, SkMatrix is row-major.
515     const float* m = steps.fFlags.gamut_transform ? steps.fSrcToDstMatrix : identity;
516     if (readSwizzle == ReadSwizzle::kRRR1) {
517         gamutTransform.setAll(m[0] + m[3] + m[6], 0, 0,
518                               m[1] + m[4] + m[7], 0, 0,
519                               m[2] + m[5] + m[8], 0, 0);
520     } else if (readSwizzle == ReadSwizzle::kBGRA) {
521         gamutTransform.setAll(m[6], m[3], m[0],
522                               m[7], m[4], m[1],
523                               m[8], m[5], m[2]);
524     } else if (readSwizzle == ReadSwizzle::k000R) {
525         gamutTransform.setAll(0, 0, 0,
526                               0, 0, 0,
527                               0, 0, 0);
528     } else if (steps.fFlags.gamut_transform) {
529         gamutTransform.setAll(m[0], m[3], m[6],
530                               m[1], m[4], m[7],
531                               m[2], m[5], m[8]);
532     }
533     gatherer->writeHalf(gamutTransform);
534 
535     // To encode whether to do premul/unpremul or make the output opaque, we use
536     // srcDEF_args.w and dstDEF_args.w:
537     // - identity: {0, 1}
538     // - do unpremul: {-1, 1}
539     // - do premul: {0, 0}
540     // - do both: {-1, 0}
541     // - alpha swizzle 1: {1, 1}
542     // - alpha swizzle r: {1, 0}
543     const bool alphaSwizzleR = readSwizzle == ReadSwizzle::k000R;
544     const bool alphaSwizzle1 = readSwizzle == ReadSwizzle::kRGB1 ||
545                                readSwizzle == ReadSwizzle::kRRR1;
546 
547     // It doesn't make sense to unpremul/premul in opaque cases, but we might get a request to
548     // anyways, which we can just ignore.
549     const bool unpremul = alphaSwizzle1 ? false : steps.fFlags.unpremul;
550     const bool premul = alphaSwizzle1 ? false : steps.fFlags.premul;
551 
552     const float srcW = unpremul ? -1.f :
553                        (alphaSwizzleR || alphaSwizzle1) ? 1.f :
554                                                           0.f;
555     const float dstW = (premul || alphaSwizzleR) ? 0.f : 1.f;
556 
557     // To encode which transfer function to apply, we use the src and dst gamma values:
558     // - identity: 0
559     // - sRGB: g > 0
560     // - PQ: -2
561     // - HLG: -1
562     if (steps.fFlags.linearize) {
563         const skcms_TFType type = skcms_TransferFunction_getType(&steps.fSrcTF);
564         const float srcG = type == skcms_TFType_sRGBish ? steps.fSrcTF.g :
565                            type == skcms_TFType_PQish ? -2.f :
566                            type == skcms_TFType_HLGish ? -1.f :
567                                                          0.f;
568         gatherer->writeHalf(SkV4{srcG, steps.fSrcTF.a, steps.fSrcTF.b, steps.fSrcTF.c});
569         gatherer->writeHalf(SkV4{steps.fSrcTF.d, steps.fSrcTF.e, steps.fSrcTF.f, srcW});
570     } else {
571         gatherer->writeHalf(SkV4{0.f, 0.f, 0.f, 0.f});
572         gatherer->writeHalf(SkV4{0.f, 0.f, 0.f, srcW});
573     }
574 
575     if (steps.fFlags.encode) {
576         const skcms_TFType type = skcms_TransferFunction_getType(&steps.fDstTFInv);
577         const float dstG = type == skcms_TFType_sRGBish ? steps.fDstTFInv.g :
578                            type == skcms_TFType_PQish ? -2.f :
579                            type == skcms_TFType_HLGinvish ? -1.f :
580                                                             0.f;
581         gatherer->writeHalf(SkV4{dstG, steps.fDstTFInv.a, steps.fDstTFInv.b, steps.fDstTFInv.c});
582         gatherer->writeHalf(SkV4{steps.fDstTFInv.d, steps.fDstTFInv.e, steps.fDstTFInv.f, dstW});
583     } else {
584         gatherer->writeHalf(SkV4{0.f, 0.f, 0.f, 0.f});
585         gatherer->writeHalf(SkV4{0.f, 0.f, 0.f, dstW});
586     }
587 }
588 
add_image_uniform_data(const ShaderCodeDictionary * dict,const ImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)589 void add_image_uniform_data(const ShaderCodeDictionary* dict,
590                             const ImageShaderBlock::ImageData& imgData,
591                             PipelineDataGatherer* gatherer) {
592     SkASSERT(!imgData.fSampling.useCubic);
593     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kImageShader)
594 
595     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
596     gatherer->write(imgData.fSubset);
597     gatherer->write(SkTo<int>(imgData.fTileModes.first));
598     gatherer->write(SkTo<int>(imgData.fTileModes.second));
599     gatherer->write(SkTo<int>(imgData.fSampling.filter));
600 }
601 
add_clamp_image_uniform_data(const ShaderCodeDictionary * dict,const ImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)602 void add_clamp_image_uniform_data(const ShaderCodeDictionary* dict,
603                                   const ImageShaderBlock::ImageData& imgData,
604                                   PipelineDataGatherer* gatherer) {
605     SkASSERT(!imgData.fSampling.useCubic);
606     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kImageShaderClamp)
607 
608     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
609 
610     // Matches GrTextureEffect::kLinearInset, to make sure we don't touch an outer row or column
611     // with a weight of 0 when linear filtering.
612     const float kLinearInset = 0.5f + 0.00001f;
613 
614     // The subset should clamp texel coordinates to an inset subset to prevent sampling neighboring
615     // texels when coords fall exactly at texel boundaries.
616     SkRect subsetInsetClamp = imgData.fSubset;
617     if (imgData.fSampling.filter == SkFilterMode::kNearest) {
618         subsetInsetClamp.roundOut(&subsetInsetClamp);
619     }
620     subsetInsetClamp.inset(kLinearInset, kLinearInset);
621     gatherer->write(subsetInsetClamp);
622 }
623 
add_cubic_image_uniform_data(const ShaderCodeDictionary * dict,const ImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)624 void add_cubic_image_uniform_data(const ShaderCodeDictionary* dict,
625                                   const ImageShaderBlock::ImageData& imgData,
626                                   PipelineDataGatherer* gatherer) {
627     SkASSERT(imgData.fSampling.useCubic);
628     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCubicImageShader)
629 
630     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
631     gatherer->write(imgData.fSubset);
632     gatherer->write(SkTo<int>(imgData.fTileModes.first));
633     gatherer->write(SkTo<int>(imgData.fTileModes.second));
634     const SkCubicResampler& cubic = imgData.fSampling.cubic;
635     gatherer->writeHalf(SkImageShader::CubicResamplerMatrix(cubic.B, cubic.C));
636 }
637 
add_hw_image_uniform_data(const ShaderCodeDictionary * dict,const ImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)638 void add_hw_image_uniform_data(const ShaderCodeDictionary* dict,
639                                const ImageShaderBlock::ImageData& imgData,
640                                PipelineDataGatherer* gatherer) {
641     SkASSERT(!imgData.fSampling.useCubic);
642     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kHWImageShader)
643 
644     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
645 }
646 
can_do_tiling_in_hw(const Caps * caps,const ImageShaderBlock::ImageData & imgData)647 bool can_do_tiling_in_hw(const Caps* caps, const ImageShaderBlock::ImageData& imgData) {
648     if (!caps->clampToBorderSupport() && (imgData.fTileModes.first == SkTileMode::kDecal ||
649                                           imgData.fTileModes.second == SkTileMode::kDecal)) {
650         return false;
651     }
652     return imgData.fSubset.contains(SkRect::Make(imgData.fImgSize));
653 }
654 
add_sampler_data_to_key(PaintParamsKeyBuilder * builder,const SamplerDesc & samplerDesc)655 void add_sampler_data_to_key(PaintParamsKeyBuilder* builder, const SamplerDesc& samplerDesc) {
656     if (samplerDesc.isImmutable()) {
657         builder->addData({samplerDesc.asSpan()});
658     } else {
659         // Means we have a regular dynamic sampler. Append a default SamplerDesc to convey this,
660         // allowing the key to maintain and convey sampler binding order.
661         builder->addData({{}});
662     }
663 }
664 
665 } // anonymous namespace
666 
ImageData(const SkSamplingOptions & sampling,SkTileMode tileModeX,SkTileMode tileModeY,SkISize imgSize,SkRect subset)667 ImageShaderBlock::ImageData::ImageData(const SkSamplingOptions& sampling,
668                                        SkTileMode tileModeX,
669                                        SkTileMode tileModeY,
670                                        SkISize imgSize,
671                                        SkRect subset)
672         : fSampling(sampling)
673         , fTileModes{tileModeX, tileModeY}
674         , fImgSize(imgSize)
675         , fSubset(subset) {
676 }
677 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ImageData & imgData)678 void ImageShaderBlock::AddBlock(const KeyContext& keyContext,
679                                 PaintParamsKeyBuilder* builder,
680                                 PipelineDataGatherer* gatherer,
681                                 const ImageData& imgData) {
682 
683     if (keyContext.recorder() && !imgData.fTextureProxy) {
684         builder->addBlock(BuiltInCodeSnippetID::kError);
685         return;
686     }
687 
688     const Caps* caps = keyContext.caps();
689     const bool doTilingInHw = !imgData.fSampling.useCubic && can_do_tiling_in_hw(caps, imgData);
690 
691     if (doTilingInHw) {
692         add_hw_image_uniform_data(keyContext.dict(), imgData, gatherer);
693         builder->beginBlock(BuiltInCodeSnippetID::kHWImageShader);
694     } else if (imgData.fSampling.useCubic) {
695         add_cubic_image_uniform_data(keyContext.dict(), imgData, gatherer);
696         builder->beginBlock(BuiltInCodeSnippetID::kCubicImageShader);
697     } else if (imgData.fTileModes.first == SkTileMode::kClamp &&
698                imgData.fTileModes.second == SkTileMode::kClamp) {
699         add_clamp_image_uniform_data(keyContext.dict(), imgData, gatherer);
700         builder->beginBlock(BuiltInCodeSnippetID::kImageShaderClamp);
701     } else {
702         add_image_uniform_data(keyContext.dict(), imgData, gatherer);
703         builder->beginBlock(BuiltInCodeSnippetID::kImageShader);
704     }
705 
706     static constexpr std::pair<SkTileMode, SkTileMode> kDefaultTileModes =
707             {SkTileMode::kClamp, SkTileMode::kClamp};
708 
709     // Image shaders must append immutable sampler data (or '0' in the more common case where
710     // regular samplers are used).
711     // TODO(b/392623124): In precompile mode (fTextureProxy == null), we still have a need for
712     // immutable samplers, which must be passed in somehow.
713     ImmutableSamplerInfo info = imgData.fTextureProxy
714             ? caps->getImmutableSamplerInfo(imgData.fTextureProxy->textureInfo())
715             : ImmutableSamplerInfo{};
716     SamplerDesc samplerDesc {imgData.fSampling,
717                              doTilingInHw ? imgData.fTileModes : kDefaultTileModes,
718                              info};
719     gatherer->add(imgData.fTextureProxy, samplerDesc);
720     add_sampler_data_to_key(builder, samplerDesc);
721 
722     builder->endBlock();
723 }
724 
725 //--------------------------------------------------------------------------------------------------
726 
727 // makes use of ImageShader functions, above
728 namespace {
729 
add_yuv_image_uniform_data(const ShaderCodeDictionary * dict,const YUVImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)730 void add_yuv_image_uniform_data(const ShaderCodeDictionary* dict,
731                                 const YUVImageShaderBlock::ImageData& imgData,
732                                 PipelineDataGatherer* gatherer) {
733     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kYUVImageShader)
734 
735     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
736     gatherer->write(SkSize::Make(1.f/imgData.fImgSizeUV.width(), 1.f/imgData.fImgSizeUV.height()));
737     gatherer->write(imgData.fSubset);
738     gatherer->write(imgData.fLinearFilterUVInset);
739     gatherer->write(SkTo<int>(imgData.fTileModes.first));
740     gatherer->write(SkTo<int>(imgData.fTileModes.second));
741     gatherer->write(SkTo<int>(imgData.fSampling.filter));
742     gatherer->write(SkTo<int>(imgData.fSamplingUV.filter));
743 
744     for (int i = 0; i < 4; ++i) {
745         gatherer->writeHalf(imgData.fChannelSelect[i]);
746     }
747     gatherer->writeHalf(imgData.fYUVtoRGBMatrix);
748     gatherer->writeHalf(imgData.fYUVtoRGBTranslate);
749 }
750 
add_cubic_yuv_image_uniform_data(const ShaderCodeDictionary * dict,const YUVImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)751 void add_cubic_yuv_image_uniform_data(const ShaderCodeDictionary* dict,
752                                       const YUVImageShaderBlock::ImageData& imgData,
753                                       PipelineDataGatherer* gatherer) {
754     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCubicYUVImageShader)
755 
756     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
757     gatherer->write(SkSize::Make(1.f/imgData.fImgSizeUV.width(), 1.f/imgData.fImgSizeUV.height()));
758     gatherer->write(imgData.fSubset);
759     gatherer->write(SkTo<int>(imgData.fTileModes.first));
760     gatherer->write(SkTo<int>(imgData.fTileModes.second));
761     const SkCubicResampler& cubic = imgData.fSampling.cubic;
762     gatherer->writeHalf(SkImageShader::CubicResamplerMatrix(cubic.B, cubic.C));
763 
764     for (int i = 0; i < 4; ++i) {
765         gatherer->writeHalf(imgData.fChannelSelect[i]);
766     }
767     gatherer->writeHalf(imgData.fYUVtoRGBMatrix);
768     gatherer->writeHalf(imgData.fYUVtoRGBTranslate);
769 }
770 
add_hw_yuv_image_uniform_data(const ShaderCodeDictionary * dict,const YUVImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)771 void add_hw_yuv_image_uniform_data(const ShaderCodeDictionary* dict,
772                                    const YUVImageShaderBlock::ImageData& imgData,
773                                    PipelineDataGatherer* gatherer) {
774     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kHWYUVImageShader)
775 
776     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
777     gatherer->write(SkSize::Make(1.f/imgData.fImgSizeUV.width(), 1.f/imgData.fImgSizeUV.height()));
778     for (int i = 0; i < 4; ++i) {
779         gatherer->writeHalf(imgData.fChannelSelect[i]);
780     }
781     gatherer->writeHalf(imgData.fYUVtoRGBMatrix);
782     gatherer->writeHalf(imgData.fYUVtoRGBTranslate);
783 }
784 
add_hw_yuv_no_swizzle_image_uniform_data(const ShaderCodeDictionary * dict,const YUVImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)785 void add_hw_yuv_no_swizzle_image_uniform_data(const ShaderCodeDictionary* dict,
786                                               const YUVImageShaderBlock::ImageData& imgData,
787                                               PipelineDataGatherer* gatherer) {
788     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kHWYUVNoSwizzleImageShader)
789 
790     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
791     gatherer->write(SkSize::Make(1.f/imgData.fImgSizeUV.width(), 1.f/imgData.fImgSizeUV.height()));
792     gatherer->writeHalf(imgData.fYUVtoRGBMatrix);
793     SkV4 yuvToRGBXlateAlphaParam = {
794         imgData.fYUVtoRGBTranslate.fX,
795         imgData.fYUVtoRGBTranslate.fY,
796         imgData.fYUVtoRGBTranslate.fZ,
797         imgData.fAlphaParam
798     };
799     gatherer->writeHalf(yuvToRGBXlateAlphaParam);
800 }
801 
802 } // anonymous namespace
803 
ImageData(const SkSamplingOptions & sampling,SkTileMode tileModeX,SkTileMode tileModeY,SkISize imgSize,SkRect subset)804 YUVImageShaderBlock::ImageData::ImageData(const SkSamplingOptions& sampling,
805                                           SkTileMode tileModeX,
806                                           SkTileMode tileModeY,
807                                           SkISize imgSize,
808                                           SkRect subset)
809         : fSampling(sampling)
810         , fSamplingUV(sampling)
811         , fTileModes{tileModeX, tileModeY}
812         , fImgSize(imgSize)
813         , fImgSizeUV(imgSize)
814         , fSubset(subset) {
815 }
816 
can_do_yuv_tiling_in_hw(const Caps * caps,const YUVImageShaderBlock::ImageData & imgData)817 static bool can_do_yuv_tiling_in_hw(const Caps* caps,
818                                     const YUVImageShaderBlock::ImageData& imgData) {
819     if (!caps->clampToBorderSupport() && (imgData.fTileModes.first == SkTileMode::kDecal ||
820                                           imgData.fTileModes.second == SkTileMode::kDecal)) {
821         return false;
822     }
823     // We depend on the subset code to handle cases where the UV dimensions times the
824     // subsample factors are not equal to the Y dimensions.
825     if (imgData.fImgSize != imgData.fImgSizeUV) {
826         return false;
827     }
828     // For nearest filtering when the Y texture size is larger than the UV texture size,
829     // we use linear filtering for the UV texture. In this case we also adjust pixel centers
830     // which may affect dependent texture reads.
831     if (imgData.fSampling.filter != imgData.fSamplingUV.filter) {
832         return false;
833     }
834     return imgData.fSubset.contains(SkRect::Make(imgData.fImgSize));
835 }
836 
no_yuv_swizzle(const YUVImageShaderBlock::ImageData & imgData)837 static bool no_yuv_swizzle(const YUVImageShaderBlock::ImageData& imgData) {
838     // Y_U_V or U_Y_V format, reading from R channel for each texture
839     if (imgData.fChannelSelect[0].x == 1 &&
840         imgData.fChannelSelect[1].x == 1 &&
841         imgData.fChannelSelect[2].x == 1 &&
842         imgData.fChannelSelect[3].x == 1) {
843         return true;
844     }
845 
846     return false;
847 }
848 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ImageData & imgData)849 void YUVImageShaderBlock::AddBlock(const KeyContext& keyContext,
850                                    PaintParamsKeyBuilder* builder,
851                                    PipelineDataGatherer* gatherer,
852                                    const ImageData& imgData) {
853     if (keyContext.recorder() &&
854         (!imgData.fTextureProxies[0] || !imgData.fTextureProxies[1] ||
855          !imgData.fTextureProxies[2] || !imgData.fTextureProxies[3])) {
856         builder->addBlock(BuiltInCodeSnippetID::kError);
857         return;
858     }
859 
860     const Caps* caps = keyContext.caps();
861     const bool doTilingInHw = !imgData.fSampling.useCubic && can_do_yuv_tiling_in_hw(caps, imgData);
862     const bool noYUVSwizzle = no_yuv_swizzle(imgData);
863 
864     auto uvTileModes = std::make_pair(imgData.fTileModes.first == SkTileMode::kDecal
865                                             ? SkTileMode::kClamp : imgData.fTileModes.first,
866                                       imgData.fTileModes.second == SkTileMode::kDecal
867                                             ? SkTileMode::kClamp : imgData.fTileModes.second);
868     gatherer->add(imgData.fTextureProxies[0], {imgData.fSampling, imgData.fTileModes});
869     gatherer->add(imgData.fTextureProxies[1], {imgData.fSamplingUV, uvTileModes});
870     gatherer->add(imgData.fTextureProxies[2], {imgData.fSamplingUV, uvTileModes});
871     gatherer->add(imgData.fTextureProxies[3], {imgData.fSampling, imgData.fTileModes});
872 
873     if (doTilingInHw && noYUVSwizzle) {
874         add_hw_yuv_no_swizzle_image_uniform_data(keyContext.dict(), imgData, gatherer);
875         builder->addBlock(BuiltInCodeSnippetID::kHWYUVNoSwizzleImageShader);
876     } else if (doTilingInHw) {
877         add_hw_yuv_image_uniform_data(keyContext.dict(), imgData, gatherer);
878         builder->addBlock(BuiltInCodeSnippetID::kHWYUVImageShader);
879     } else if (imgData.fSampling.useCubic) {
880         add_cubic_yuv_image_uniform_data(keyContext.dict(), imgData, gatherer);
881         builder->addBlock(BuiltInCodeSnippetID::kCubicYUVImageShader);
882     } else {
883         add_yuv_image_uniform_data(keyContext.dict(), imgData, gatherer);
884         builder->addBlock(BuiltInCodeSnippetID::kYUVImageShader);
885     }
886 }
887 
888 //--------------------------------------------------------------------------------------------------
889 
890 namespace {
891 
add_coordclamp_uniform_data(const ShaderCodeDictionary * dict,const CoordClampShaderBlock::CoordClampData & clampData,PipelineDataGatherer * gatherer)892 void add_coordclamp_uniform_data(const ShaderCodeDictionary* dict,
893                                  const CoordClampShaderBlock::CoordClampData& clampData,
894                                  PipelineDataGatherer* gatherer) {
895     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCoordClampShader)
896 
897     gatherer->write(clampData.fSubset);
898 }
899 
900 } // anonymous namespace
901 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const CoordClampData & clampData)902 void CoordClampShaderBlock::BeginBlock(const KeyContext& keyContext,
903                                        PaintParamsKeyBuilder* builder,
904                                        PipelineDataGatherer* gatherer,
905                                        const CoordClampData& clampData) {
906     add_coordclamp_uniform_data(keyContext.dict(), clampData, gatherer);
907 
908     builder->beginBlock(BuiltInCodeSnippetID::kCoordClampShader);
909 }
910 
911 //--------------------------------------------------------------------------------------------------
912 
913 namespace {
914 
add_dither_uniform_data(const ShaderCodeDictionary * dict,const DitherShaderBlock::DitherData & ditherData,PipelineDataGatherer * gatherer)915 void add_dither_uniform_data(const ShaderCodeDictionary* dict,
916                              const DitherShaderBlock::DitherData& ditherData,
917                              PipelineDataGatherer* gatherer) {
918     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kDitherShader)
919 
920     gatherer->writeHalf(ditherData.fRange);
921 }
922 
923 } // anonymous namespace
924 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const DitherData & data)925 void DitherShaderBlock::AddBlock(const KeyContext& keyContext,
926                                  PaintParamsKeyBuilder* builder,
927                                  PipelineDataGatherer* gatherer,
928                                  const DitherData& data) {
929     add_dither_uniform_data(keyContext.dict(), data, gatherer);
930 
931     SkASSERT(data.fLUTProxy || !keyContext.recorder());
932     gatherer->add(data.fLUTProxy, {SkFilterMode::kNearest, SkTileMode::kRepeat});
933 
934     builder->addBlock(BuiltInCodeSnippetID::kDitherShader);
935 }
936 
937 //--------------------------------------------------------------------------------------------------
938 
939 namespace {
940 
add_perlin_noise_uniform_data(const ShaderCodeDictionary * dict,const PerlinNoiseShaderBlock::PerlinNoiseData & noiseData,PipelineDataGatherer * gatherer)941 void add_perlin_noise_uniform_data(const ShaderCodeDictionary* dict,
942                                    const PerlinNoiseShaderBlock::PerlinNoiseData& noiseData,
943                                    PipelineDataGatherer* gatherer) {
944     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kPerlinNoiseShader)
945 
946     gatherer->write(noiseData.fBaseFrequency);
947     gatherer->write(noiseData.fStitchData);
948     gatherer->write(static_cast<int>(noiseData.fType));
949     gatherer->write(noiseData.fNumOctaves);
950     gatherer->write(static_cast<int>(noiseData.stitching()));
951 
952     static const std::pair<SkTileMode, SkTileMode> kRepeatXTileModes =
953             { SkTileMode::kRepeat, SkTileMode::kClamp };
954     gatherer->add(noiseData.fPermutationsProxy, {SkFilterMode::kNearest, kRepeatXTileModes});
955     gatherer->add(noiseData.fNoiseProxy, {SkFilterMode::kNearest, kRepeatXTileModes});
956 }
957 
958 } // anonymous namespace
959 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const PerlinNoiseData & noiseData)960 void PerlinNoiseShaderBlock::AddBlock(const KeyContext& keyContext,
961                                       PaintParamsKeyBuilder* builder,
962                                       PipelineDataGatherer* gatherer,
963                                       const PerlinNoiseData& noiseData) {
964     add_perlin_noise_uniform_data(keyContext.dict(), noiseData, gatherer);
965 
966     builder->addBlock(BuiltInCodeSnippetID::kPerlinNoiseShader);
967 }
968 
969 //--------------------------------------------------------------------------------------------------
970 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)971 void BlendComposeBlock::BeginBlock(const KeyContext& keyContext,
972                                   PaintParamsKeyBuilder* builder,
973                                   PipelineDataGatherer* gatherer) {
974     BEGIN_WRITE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kBlendCompose)
975 
976     builder->beginBlock(BuiltInCodeSnippetID::kBlendCompose);
977 }
978 
979 //--------------------------------------------------------------------------------------------------
980 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkSpan<const float> coeffs)981 void PorterDuffBlenderBlock::AddBlock(const KeyContext& keyContext,
982                                       PaintParamsKeyBuilder* builder,
983                                       PipelineDataGatherer* gatherer,
984                                       SkSpan<const float> coeffs) {
985     BEGIN_WRITE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kPorterDuffBlender)
986     SkASSERT(coeffs.size() == 4);
987     gatherer->writeHalf(SkV4{coeffs[0], coeffs[1], coeffs[2], coeffs[3]});
988 
989     builder->addBlock(BuiltInCodeSnippetID::kPorterDuffBlender);
990 }
991 
992 //--------------------------------------------------------------------------------------------------
993 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkSpan<const float> coeffs)994 void HSLCBlenderBlock::AddBlock(const KeyContext& keyContext,
995                                  PaintParamsKeyBuilder* builder,
996                                  PipelineDataGatherer* gatherer,
997                                  SkSpan<const float> coeffs) {
998     BEGIN_WRITE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kHSLCBlender)
999     SkASSERT(coeffs.size() == 2);
1000     gatherer->writeHalf(SkV2{coeffs[0], coeffs[1]});
1001 
1002     builder->addBlock(BuiltInCodeSnippetID::kHSLCBlender);
1003 }
1004 
1005 //--------------------------------------------------------------------------------------------------
1006 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)1007 void ComposeBlock::BeginBlock(const KeyContext& keyContext,
1008                               PaintParamsKeyBuilder* builder,
1009                               PipelineDataGatherer* gatherer) {
1010     builder->beginBlock(BuiltInCodeSnippetID::kCompose);
1011 }
1012 
1013 //--------------------------------------------------------------------------------------------------
1014 
1015 namespace {
1016 
add_matrix_colorfilter_uniform_data(const ShaderCodeDictionary * dict,const MatrixColorFilterBlock::MatrixColorFilterData & data,PipelineDataGatherer * gatherer)1017 void add_matrix_colorfilter_uniform_data(const ShaderCodeDictionary* dict,
1018                                          const MatrixColorFilterBlock::MatrixColorFilterData& data,
1019                                          PipelineDataGatherer* gatherer) {
1020     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kMatrixColorFilter)
1021     gatherer->write(data.fMatrix);
1022     gatherer->write(data.fTranslate);
1023     gatherer->write(static_cast<int>(data.fInHSLA));
1024     gatherer->write(static_cast<int>(data.fClamp));
1025 }
1026 
1027 } // anonymous namespace
1028 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const MatrixColorFilterData & matrixCFData)1029 void MatrixColorFilterBlock::AddBlock(const KeyContext& keyContext,
1030                                       PaintParamsKeyBuilder* builder,
1031                                       PipelineDataGatherer* gatherer,
1032                                       const MatrixColorFilterData& matrixCFData) {
1033 
1034     add_matrix_colorfilter_uniform_data(keyContext.dict(), matrixCFData, gatherer);
1035 
1036     builder->addBlock(BuiltInCodeSnippetID::kMatrixColorFilter);
1037 }
1038 
1039 //--------------------------------------------------------------------------------------------------
1040 
1041 namespace {
1042 
add_table_colorfilter_uniform_data(const ShaderCodeDictionary * dict,const TableColorFilterBlock::TableColorFilterData & data,PipelineDataGatherer * gatherer)1043 void add_table_colorfilter_uniform_data(const ShaderCodeDictionary* dict,
1044                                         const TableColorFilterBlock::TableColorFilterData& data,
1045                                         PipelineDataGatherer* gatherer) {
1046     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kTableColorFilter)
1047 
1048     gatherer->add(data.fTextureProxy, {SkFilterMode::kNearest, SkTileMode::kClamp});
1049 }
1050 
1051 } // anonymous namespace
1052 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const TableColorFilterData & data)1053 void TableColorFilterBlock::AddBlock(const KeyContext& keyContext,
1054                                      PaintParamsKeyBuilder* builder,
1055                                      PipelineDataGatherer* gatherer,
1056                                      const TableColorFilterData& data) {
1057     SkASSERT(data.fTextureProxy || !keyContext.recorder());
1058 
1059     add_table_colorfilter_uniform_data(keyContext.dict(), data, gatherer);
1060 
1061     builder->addBlock(BuiltInCodeSnippetID::kTableColorFilter);
1062 }
1063 
1064 //--------------------------------------------------------------------------------------------------
1065 namespace {
1066 
add_color_space_xform_uniform_data(const ShaderCodeDictionary * dict,const ColorSpaceTransformBlock::ColorSpaceTransformData & data,PipelineDataGatherer * gatherer)1067 void add_color_space_xform_uniform_data(
1068         const ShaderCodeDictionary* dict,
1069         const ColorSpaceTransformBlock::ColorSpaceTransformData& data,
1070         PipelineDataGatherer* gatherer) {
1071     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kColorSpaceXformColorFilter)
1072     add_color_space_uniforms(data.fSteps, data.fReadSwizzle, gatherer);
1073 }
1074 
add_color_space_xform_premul_uniform_data(const ShaderCodeDictionary * dict,const ColorSpaceTransformBlock::ColorSpaceTransformData & data,PipelineDataGatherer * gatherer)1075 void add_color_space_xform_premul_uniform_data(
1076         const ShaderCodeDictionary* dict,
1077         const ColorSpaceTransformBlock::ColorSpaceTransformData& data,
1078         PipelineDataGatherer* gatherer) {
1079     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kColorSpaceXformPremul)
1080 
1081     // If either of these asserts would fail, we can't correctly use this specialized shader for
1082     // the given transform.
1083     SkASSERT(data.fReadSwizzle == ReadSwizzle::kRGBA || data.fReadSwizzle == ReadSwizzle::kRGB1);
1084     // If these are both true, that implies there's a color space transfer or gamut transform.
1085     SkASSERT(!(data.fSteps.fFlags.unpremul && data.fSteps.fFlags.premul));
1086 
1087     // This shader can either do nothing, or perform one of three actions. These four possibilities
1088     // are encoded in a half2 argument as:
1089     // - identity: {0, 1}
1090     // - do unpremul: {-1, 1}
1091     // - do premul: {0, 0}
1092     // - make opaque: {1, 1}
1093     const bool opaque = data.fReadSwizzle == ReadSwizzle::kRGB1;
1094     const float x = data.fSteps.fFlags.unpremul ? -1.f :
1095                     opaque ? 1.f
1096                            : 0.f;
1097     const float y = data.fSteps.fFlags.premul ? 0.f : 1.f;
1098     gatherer->writeHalf(SkV2{x, y});
1099 }
1100 
add_color_space_xform_srgb_uniform_data(const ShaderCodeDictionary * dict,const ColorSpaceTransformBlock::ColorSpaceTransformData & data,PipelineDataGatherer * gatherer)1101 void add_color_space_xform_srgb_uniform_data(
1102         const ShaderCodeDictionary* dict,
1103         const ColorSpaceTransformBlock::ColorSpaceTransformData& data,
1104         PipelineDataGatherer* gatherer) {
1105     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kColorSpaceXformSRGB)
1106     add_color_space_uniforms(data.fSteps, data.fReadSwizzle, gatherer);
1107 }
1108 
1109 }  // anonymous namespace
1110 
ColorSpaceTransformData(const SkColorSpace * src,SkAlphaType srcAT,const SkColorSpace * dst,SkAlphaType dstAT)1111 ColorSpaceTransformBlock::ColorSpaceTransformData::ColorSpaceTransformData(const SkColorSpace* src,
1112                                                                            SkAlphaType srcAT,
1113                                                                            const SkColorSpace* dst,
1114                                                                            SkAlphaType dstAT)
1115         : fSteps(src, srcAT, dst, dstAT) {}
1116 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ColorSpaceTransformData & data)1117 void ColorSpaceTransformBlock::AddBlock(const KeyContext& keyContext,
1118                                         PaintParamsKeyBuilder* builder,
1119                                         PipelineDataGatherer* gatherer,
1120                                         const ColorSpaceTransformData& data) {
1121     const bool xformNeedsGamutOrXferFn = data.fSteps.fFlags.linearize || data.fSteps.fFlags.encode ||
1122                                          data.fSteps.fFlags.gamut_transform;
1123     const bool swizzleNeedsGamutTransform = !(data.fReadSwizzle == ReadSwizzle::kRGBA ||
1124                                               data.fReadSwizzle == ReadSwizzle::kRGB1);
1125 
1126     // Use a specialized shader if we don't need transfer function or gamut transforms.
1127     if (!(xformNeedsGamutOrXferFn || swizzleNeedsGamutTransform)) {
1128         add_color_space_xform_premul_uniform_data(keyContext.dict(), data, gatherer);
1129         builder->addBlock(BuiltInCodeSnippetID::kColorSpaceXformPremul);
1130         return;
1131     }
1132 
1133     // Use a specialized shader if we're transferring to and from sRGB-ish color spaces.
1134     if (data.fSteps.fFlags.linearize && data.fSteps.fFlags.encode &&
1135         skcms_TransferFunction_isSRGBish(&data.fSteps.fSrcTF) &&
1136         skcms_TransferFunction_isSRGBish(&data.fSteps.fDstTFInv)) {
1137         add_color_space_xform_srgb_uniform_data(keyContext.dict(), data, gatherer);
1138         builder->addBlock(BuiltInCodeSnippetID::kColorSpaceXformSRGB);
1139         return;
1140     }
1141 
1142     // Use the most general color space transform shader if no specializations can be used.
1143     add_color_space_xform_uniform_data(keyContext.dict(), data, gatherer);
1144     builder->addBlock(BuiltInCodeSnippetID::kColorSpaceXformColorFilter);
1145 }
1146 
1147 //--------------------------------------------------------------------------------------------------
1148 namespace {
1149 
add_analytic_clip_data(const ShaderCodeDictionary * dict,const NonMSAAClipBlock::NonMSAAClipData & data,PipelineDataGatherer * gatherer)1150 void add_analytic_clip_data(
1151         const ShaderCodeDictionary* dict,
1152         const NonMSAAClipBlock::NonMSAAClipData& data,
1153         PipelineDataGatherer* gatherer) {
1154     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAnalyticClip)
1155     gatherer->write(data.fRect);
1156     gatherer->write(data.fRadiusPlusHalf);
1157     gatherer->writeHalf(data.fEdgeSelect);
1158 }
1159 
add_analytic_and_atlas_clip_data(const ShaderCodeDictionary * dict,const NonMSAAClipBlock::NonMSAAClipData & data,PipelineDataGatherer * gatherer)1160 void add_analytic_and_atlas_clip_data(
1161         const ShaderCodeDictionary* dict,
1162         const NonMSAAClipBlock::NonMSAAClipData& data,
1163         PipelineDataGatherer* gatherer) {
1164     BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAnalyticAndAtlasClip)
1165     gatherer->write(data.fRect);
1166     gatherer->write(data.fRadiusPlusHalf);
1167     gatherer->writeHalf(data.fEdgeSelect);
1168     gatherer->write(data.fTexCoordOffset);
1169     gatherer->write(data.fMaskBounds);
1170     if (data.fAtlasTexture) {
1171         gatherer->write(SkSize::Make(1.f/data.fAtlasTexture->dimensions().width(),
1172                                      1.f/data.fAtlasTexture->dimensions().height()));
1173     } else {
1174         gatherer->write(SkSize::Make(0, 0));
1175     }
1176 }
1177 
1178 }  // anonymous namespace
1179 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const NonMSAAClipData & data)1180 void NonMSAAClipBlock::AddBlock(const KeyContext& keyContext,
1181                                 PaintParamsKeyBuilder* builder,
1182                                 PipelineDataGatherer* gatherer,
1183                                 const NonMSAAClipData& data) {
1184     if (data.fAtlasTexture) {
1185         add_analytic_and_atlas_clip_data(keyContext.dict(), data, gatherer);
1186         builder->beginBlock(BuiltInCodeSnippetID::kAnalyticAndAtlasClip);
1187 
1188         const Caps* caps = keyContext.caps();
1189         ImmutableSamplerInfo info =
1190                 caps->getImmutableSamplerInfo(data.fAtlasTexture->textureInfo());
1191         SamplerDesc samplerDesc {SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone),
1192                                  {SkTileMode::kClamp, SkTileMode::kClamp},
1193                                  info};
1194         gatherer->add(data.fAtlasTexture, samplerDesc);
1195 
1196         builder->endBlock();
1197     } else {
1198         add_analytic_clip_data(keyContext.dict(), data, gatherer);
1199         builder->addBlock(BuiltInCodeSnippetID::kAnalyticClip);
1200     }
1201 }
1202 
1203 //--------------------------------------------------------------------------------------------------
1204 
AddPrimitiveColor(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorSpace * primitiveColorSpace)1205 void AddPrimitiveColor(const KeyContext& keyContext,
1206                        PaintParamsKeyBuilder* builder,
1207                        PipelineDataGatherer* gatherer,
1208                        const SkColorSpace* primitiveColorSpace) {
1209     ColorSpaceTransformBlock::ColorSpaceTransformData toDst(primitiveColorSpace,
1210                                                             kPremul_SkAlphaType,
1211                                                             keyContext.dstColorInfo().colorSpace(),
1212                                                             keyContext.dstColorInfo().alphaType());
1213     Compose(keyContext, builder, gatherer,
1214             /* addInnerToKey= */ [&]() -> void {
1215                 builder->addBlock(BuiltInCodeSnippetID::kPrimitiveColor);
1216             },
1217             /* addOuterToKey= */ [&]() -> void {
1218                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, toDst);
1219             });
1220 }
1221 
1222 //--------------------------------------------------------------------------------------------------
1223 
AddBlendModeColorFilter(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm,const SkPMColor4f & srcColor)1224 void AddBlendModeColorFilter(const KeyContext& keyContext,
1225                              PaintParamsKeyBuilder* builder,
1226                              PipelineDataGatherer* gatherer,
1227                              SkBlendMode bm,
1228                              const SkPMColor4f& srcColor) {
1229     Blend(keyContext, builder, gatherer,
1230           /* addBlendToKey= */ [&] () -> void {
1231               AddBlendMode(keyContext, builder, gatherer, bm);
1232           },
1233           /* addSrcToKey= */ [&]() -> void {
1234               SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, srcColor);
1235           },
1236           /* addDstToKey= */ [&]() -> void {
1237               builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1238           });
1239 }
1240 
ShaderData(sk_sp<const SkRuntimeEffect> effect)1241 RuntimeEffectBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect)
1242         : fEffect(std::move(effect)) {}
1243 
ShaderData(sk_sp<const SkRuntimeEffect> effect,sk_sp<const SkData> uniforms)1244 RuntimeEffectBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect,
1245                                            sk_sp<const SkData> uniforms)
1246         : fEffect(std::move(effect))
1247         , fUniforms(std::move(uniforms)) {}
1248 
skdata_matches(const SkData * a,const SkData * b)1249 static bool skdata_matches(const SkData* a, const SkData* b) {
1250     // Returns true if both SkData objects hold the same contents, or if they are both null.
1251     // (SkData::equals supports passing null, and returns false.)
1252     return a ? a->equals(b) : (a == b);
1253 }
1254 
operator ==(const ShaderData & rhs) const1255 bool RuntimeEffectBlock::ShaderData::operator==(const ShaderData& rhs) const {
1256     return fEffect == rhs.fEffect && skdata_matches(fUniforms.get(), rhs.fUniforms.get());
1257 }
1258 
gather_runtime_effect_uniforms(const KeyContext & keyContext,const SkRuntimeEffect * effect,SkSpan<const Uniform> graphiteUniforms,const SkData * uniformData,PipelineDataGatherer * gatherer)1259 static void gather_runtime_effect_uniforms(const KeyContext& keyContext,
1260                                            const SkRuntimeEffect* effect,
1261                                            SkSpan<const Uniform> graphiteUniforms,
1262                                            const SkData* uniformData,
1263                                            PipelineDataGatherer* gatherer) {
1264     if (!uniformData) {
1265         return;  // precompiling
1266     }
1267 
1268     SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, graphiteUniforms);)
1269 
1270     SkSpan<const SkRuntimeEffect::Uniform> rtsUniforms = effect->uniforms();
1271 
1272     if (!rtsUniforms.empty() && uniformData) {
1273         // Collect all the other uniforms from the provided SkData.
1274         const uint8_t* uniformBase = uniformData->bytes();
1275         for (size_t index = 0; index < rtsUniforms.size(); ++index) {
1276             const Uniform& uniform = graphiteUniforms[index];
1277             // Get a pointer to the offset in our data for this uniform.
1278             const uint8_t* uniformPtr = uniformBase + rtsUniforms[index].offset;
1279             // Pass the uniform data to the gatherer.
1280             gatherer->write(uniform, uniformPtr);
1281         }
1282     }
1283 }
1284 
1285 // TODO(robertphillips): when BeginBlock fails we should mark the 'builder' as having failed
1286 // and explicitly handle the failure in ShaderCodeDictionary::findOrCreate.
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ShaderData & shaderData)1287 bool RuntimeEffectBlock::BeginBlock(const KeyContext& keyContext,
1288                                     PaintParamsKeyBuilder* builder,
1289                                     PipelineDataGatherer* gatherer,
1290                                     const ShaderData& shaderData) {
1291     ShaderCodeDictionary* dict = keyContext.dict();
1292     int codeSnippetID = dict->findOrCreateRuntimeEffectSnippet(shaderData.fEffect.get());
1293 
1294     if (codeSnippetID < 0) {
1295         return false;
1296     }
1297 
1298     if (SkKnownRuntimeEffects::IsUserDefinedRuntimeEffect(codeSnippetID)) {
1299         keyContext.rtEffectDict()->set(codeSnippetID, shaderData.fEffect);
1300     }
1301 
1302     const ShaderSnippet* entry = dict->getEntry(codeSnippetID);
1303     if (!entry) {
1304         return false;
1305     }
1306 
1307     gather_runtime_effect_uniforms(keyContext,
1308                                    shaderData.fEffect.get(),
1309                                    entry->fUniforms,
1310                                    shaderData.fUniforms.get(),
1311                                    gatherer);
1312 
1313     builder->beginBlock(codeSnippetID);
1314     return true;
1315 }
1316 
1317 // TODO(robertphillips): fuse this with the similar code in add_children_to_key and
1318 // PrecompileRTEffect::addToKey.
AddNoOpEffect(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkRuntimeEffect * effect)1319 void RuntimeEffectBlock::AddNoOpEffect(const KeyContext& keyContext,
1320                                        PaintParamsKeyBuilder* builder,
1321                                        PipelineDataGatherer* gatherer,
1322                                        SkRuntimeEffect* effect) {
1323     if (effect->allowShader()) {
1324         // A missing shader returns transparent black
1325         SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer,
1326                                         SK_PMColor4fTRANSPARENT);
1327     } else if (effect->allowColorFilter()) {
1328         // A "passthrough" color filter returns the input color as-is.
1329         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1330     } else {
1331         SkASSERT(effect->allowBlender());
1332         // A "passthrough" blender performs `blend_src_over(src, dest)`.
1333         AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kSrcOver);
1334     }
1335 }
1336 
1337 // ==================================================================
1338 
1339 namespace {
1340 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlendModeBlender * blender)1341 void add_to_key(const KeyContext& keyContext,
1342                 PaintParamsKeyBuilder* builder,
1343                 PipelineDataGatherer* gatherer,
1344                 const SkBlendModeBlender* blender) {
1345     SkASSERT(blender);
1346 
1347     AddBlendMode(keyContext, builder, gatherer, blender->mode());
1348 }
1349 
1350 // Be sure to keep this function in sync w/ the code in PrecompileRTEffect::addToKey
add_children_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkSpan<const SkRuntimeEffect::ChildPtr> children,const SkRuntimeEffect * effect)1351 void add_children_to_key(const KeyContext& keyContext,
1352                          PaintParamsKeyBuilder* builder,
1353                          PipelineDataGatherer* gatherer,
1354                          SkSpan<const SkRuntimeEffect::ChildPtr> children,
1355                          const SkRuntimeEffect* effect) {
1356     SkSpan<const SkRuntimeEffect::Child> childInfo = effect->children();
1357     SkASSERT(children.size() == childInfo.size());
1358 
1359     using ChildType = SkRuntimeEffect::ChildType;
1360 
1361     KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1362     for (size_t index = 0; index < children.size(); ++index) {
1363         const SkRuntimeEffect::ChildPtr& child = children[index];
1364         std::optional<ChildType> type = child.type();
1365         if (type == ChildType::kShader) {
1366             AddToKey(childContext, builder, gatherer, child.shader());
1367         } else if (type == ChildType::kColorFilter) {
1368             AddToKey(childContext, builder, gatherer, child.colorFilter());
1369         } else if (type == ChildType::kBlender) {
1370             AddToKey(childContext, builder, gatherer, child.blender());
1371         } else {
1372             // We don't have a child effect. Substitute in a no-op effect.
1373             switch (childInfo[index].type) {
1374                 case ChildType::kShader:
1375                     // A missing shader returns transparent black
1376                     SolidColorShaderBlock::AddBlock(childContext, builder, gatherer,
1377                                                     SK_PMColor4fTRANSPARENT);
1378                     break;
1379 
1380                 case ChildType::kColorFilter:
1381                     // A "passthrough" color filter returns the input color as-is.
1382                     builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1383                     break;
1384 
1385                 case ChildType::kBlender:
1386                     // A "passthrough" blender performs `blend_src_over(src, dest)`.
1387                     AddFixedBlendMode(childContext, builder, gatherer, SkBlendMode::kSrcOver);
1388                     break;
1389             }
1390         }
1391     }
1392 
1393     // Runtime effects that reference color transform intrinsics have two extra children that
1394     // are bound to the colorspace xform snippet with values to go to and from the linear srgb
1395     // to the current working/dst color space.
1396     if (SkRuntimeEffectPriv::UsesColorTransform(effect)) {
1397         SkColorSpace* dstCS = keyContext.dstColorInfo().colorSpace();
1398         if (!dstCS) {
1399             dstCS = sk_srgb_linear_singleton(); // turn colorspace conversion into a noop
1400         }
1401 
1402         // TODO(b/332565302): If the runtime shader only uses one of these transforms, we could
1403         // upload only one set of uniforms.
1404 
1405         // NOTE: This must be kept in sync with the logic used to generate the toLinearSrgb() and
1406         // fromLinearSrgb() expressions for each runtime effect. toLinearSrgb() is assumed to be
1407         // the second to last child, and fromLinearSrgb() is assumed to be the last.
1408         ColorSpaceTransformBlock::ColorSpaceTransformData dstToLinear(dstCS,
1409                                                                       kUnpremul_SkAlphaType,
1410                                                                       sk_srgb_linear_singleton(),
1411                                                                       kUnpremul_SkAlphaType);
1412         ColorSpaceTransformBlock::ColorSpaceTransformData linearToDst(sk_srgb_linear_singleton(),
1413                                                                       kUnpremul_SkAlphaType,
1414                                                                       dstCS,
1415                                                                       kUnpremul_SkAlphaType);
1416 
1417         ColorSpaceTransformBlock::AddBlock(childContext, builder, gatherer, dstToLinear);
1418         ColorSpaceTransformBlock::AddBlock(childContext, builder, gatherer, linearToDst);
1419     }
1420 }
1421 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRuntimeBlender * blender)1422 void add_to_key(const KeyContext& keyContext,
1423                 PaintParamsKeyBuilder* builder,
1424                 PipelineDataGatherer* gatherer,
1425                 const SkRuntimeBlender* blender) {
1426     SkASSERT(blender);
1427     sk_sp<SkRuntimeEffect> effect = blender->effect();
1428     SkASSERT(effect);
1429     sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
1430             effect->uniforms(),
1431             blender->uniforms(),
1432             keyContext.dstColorInfo().colorSpace());
1433     SkASSERT(uniforms);
1434 
1435     if (!RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
1436                                         { effect, std::move(uniforms) })) {
1437         RuntimeEffectBlock::AddNoOpEffect(keyContext, builder, gatherer, effect.get());
1438         return;
1439     }
1440 
1441     add_children_to_key(keyContext, builder, gatherer,
1442                         blender->children(), effect.get());
1443 
1444     builder->endBlock();
1445 }
1446 
notify_in_use(Recorder * recorder,DrawContext * drawContext,SkSpan<const SkRuntimeEffect::ChildPtr> children)1447 void notify_in_use(Recorder* recorder,
1448                    DrawContext* drawContext,
1449                    SkSpan<const SkRuntimeEffect::ChildPtr> children) {
1450     for (const auto& child : children) {
1451         if (child.type().has_value()) {
1452             switch (*child.type()) {
1453                 case SkRuntimeEffect::ChildType::kShader:
1454                     NotifyImagesInUse(recorder, drawContext, child.shader());
1455                     break;
1456                 case SkRuntimeEffect::ChildType::kColorFilter:
1457                     NotifyImagesInUse(recorder, drawContext, child.colorFilter());
1458                     break;
1459                 case SkRuntimeEffect::ChildType::kBlender:
1460                     NotifyImagesInUse(recorder, drawContext, child.blender());
1461                     break;
1462             }
1463         } // else a null child is a no-op, so cannot sample an image
1464     }
1465 }
1466 
1467 } // anonymous namespace
1468 
AddToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlender * blender)1469 void AddToKey(const KeyContext& keyContext,
1470               PaintParamsKeyBuilder* builder,
1471               PipelineDataGatherer* gatherer,
1472               const SkBlender* blender) {
1473     if (!blender) {
1474         // Calling code assumes a block will be appended. Add a fixed block to preserve shader
1475         // and PaintParamsKey structure in release builds but assert since this should either not
1476         // happen or should be changing high-level logic within PaintParams::toKey().
1477         SkASSERT(false);
1478         AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kSrcOver);
1479         return;
1480     }
1481     switch (as_BB(blender)->type()) {
1482 #define M(type)                                                     \
1483     case SkBlenderBase::BlenderType::k##type:                       \
1484         add_to_key(keyContext,                                      \
1485                    builder,                                         \
1486                    gatherer,                                        \
1487                    static_cast<const Sk##type##Blender*>(blender)); \
1488         return;
1489         SK_ALL_BLENDERS(M)
1490 #undef M
1491     }
1492     SkUNREACHABLE;
1493 }
1494 
NotifyImagesInUse(Recorder * recorder,DrawContext * drawContext,const SkBlender * blender)1495 void NotifyImagesInUse(Recorder* recorder, DrawContext* drawContext, const SkBlender* blender) {
1496     if (!blender) {
1497         return;
1498     }
1499     if (as_BB(blender)->type() == SkBlenderBase::BlenderType::kRuntime) {
1500         const auto* rbb = static_cast<const SkRuntimeBlender*>(blender);
1501         notify_in_use(recorder, drawContext, rbb->children());
1502     } // else blend mode doesn't reference images
1503 }
1504 
1505 //--------------------------------------------------------------------------------------------------
1506 //--------------------------------------------------------------------------------------------------
map_color(const SkColor4f & c,SkColorSpace * src,SkColorSpace * dst)1507 static SkPMColor4f map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
1508     SkPMColor4f color = {c.fR, c.fG, c.fB, c.fA};
1509     SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, dst, kPremul_SkAlphaType).apply(color.vec());
1510     return color;
1511 }
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlendModeColorFilter * filter)1512 static void add_to_key(const KeyContext& keyContext,
1513                        PaintParamsKeyBuilder* builder,
1514                        PipelineDataGatherer* gatherer,
1515                        const SkBlendModeColorFilter* filter) {
1516     SkASSERT(filter);
1517 
1518     SkPMColor4f color = map_color(filter->color(), sk_srgb_singleton(),
1519                                   keyContext.dstColorInfo().colorSpace());
1520 
1521     AddBlendModeColorFilter(keyContext, builder, gatherer, filter->mode(), color);
1522 }
1523 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorSpaceXformColorFilter * filter)1524 static void add_to_key(const KeyContext& keyContext,
1525                        PaintParamsKeyBuilder* builder,
1526                        PipelineDataGatherer* gatherer,
1527                        const SkColorSpaceXformColorFilter* filter) {
1528     SkASSERT(filter);
1529 
1530     constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType;
1531     ColorSpaceTransformBlock::ColorSpaceTransformData csData(filter->src().get(), kAlphaType,
1532                                                              filter->dst().get(), kAlphaType);
1533     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData);
1534 }
1535 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,const SkComposeColorFilter * filter)1536 static void add_to_key(const KeyContext& keyContext,
1537                        PaintParamsKeyBuilder* keyBuilder,
1538                        PipelineDataGatherer* gatherer,
1539                        const SkComposeColorFilter* filter) {
1540     SkASSERT(filter);
1541 
1542     Compose(keyContext, keyBuilder, gatherer,
1543             /* addInnerToKey= */ [&]() -> void {
1544                 AddToKey(keyContext, keyBuilder, gatherer, filter->inner().get());
1545             },
1546             /* addOuterToKey= */ [&]() -> void {
1547                 AddToKey(keyContext, keyBuilder, gatherer, filter->outer().get());
1548             });
1549 }
1550 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkGaussianColorFilter *)1551 static void add_to_key(const KeyContext& keyContext,
1552                        PaintParamsKeyBuilder* builder,
1553                        PipelineDataGatherer* gatherer,
1554                        const SkGaussianColorFilter*) {
1555     builder->addBlock(BuiltInCodeSnippetID::kGaussianColorFilter);
1556 }
1557 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkMatrixColorFilter * filter)1558 static void add_to_key(const KeyContext& keyContext,
1559                        PaintParamsKeyBuilder* builder,
1560                        PipelineDataGatherer* gatherer,
1561                        const SkMatrixColorFilter* filter) {
1562     SkASSERT(filter);
1563 
1564     bool inHSLA = filter->domain() == SkMatrixColorFilter::Domain::kHSLA;
1565     bool clamp = filter->clamp() == SkMatrixColorFilter::Clamp::kYes;
1566     MatrixColorFilterBlock::MatrixColorFilterData matrixCFData(filter->matrix(), inHSLA, clamp);
1567 
1568     MatrixColorFilterBlock::AddBlock(keyContext, builder, gatherer, matrixCFData);
1569 }
1570 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRuntimeColorFilter * filter)1571 static void add_to_key(const KeyContext& keyContext,
1572                        PaintParamsKeyBuilder* builder,
1573                        PipelineDataGatherer* gatherer,
1574                        const SkRuntimeColorFilter* filter) {
1575     SkASSERT(filter);
1576 
1577     sk_sp<SkRuntimeEffect> effect = filter->effect();
1578     sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
1579             effect->uniforms(), filter->uniforms(), keyContext.dstColorInfo().colorSpace());
1580     SkASSERT(uniforms);
1581 
1582     if (!RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
1583                                         { effect, std::move(uniforms) })) {
1584         RuntimeEffectBlock::AddNoOpEffect(keyContext, builder, gatherer, effect.get());
1585         return;
1586     }
1587 
1588     add_children_to_key(keyContext, builder, gatherer,
1589                         filter->children(), effect.get());
1590 
1591     builder->endBlock();
1592 }
1593 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkTableColorFilter * filter)1594 static void add_to_key(const KeyContext& keyContext,
1595                        PaintParamsKeyBuilder* builder,
1596                        PipelineDataGatherer* gatherer,
1597                        const SkTableColorFilter* filter) {
1598     SkASSERT(filter);
1599 
1600     sk_sp<TextureProxy> proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(),
1601                                                                 filter->bitmap(),
1602                                                                 "TableColorFilterTexture");
1603     if (!proxy) {
1604         SKGPU_LOG_W("Couldn't create TableColorFilter's table");
1605 
1606         // Return the input color as-is.
1607         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1608         return;
1609     }
1610 
1611     TableColorFilterBlock::TableColorFilterData data(std::move(proxy));
1612 
1613     TableColorFilterBlock::AddBlock(keyContext, builder, gatherer, data);
1614 }
1615 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkWorkingFormatColorFilter * filter)1616 static void add_to_key(const KeyContext& keyContext,
1617                        PaintParamsKeyBuilder* builder,
1618                        PipelineDataGatherer* gatherer,
1619                        const SkWorkingFormatColorFilter* filter) {
1620     SkASSERT(filter);
1621 
1622     const SkColorInfo& dstInfo = keyContext.dstColorInfo();
1623     const SkAlphaType dstAT = dstInfo.alphaType();
1624     sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace();
1625     if (!dstCS) {
1626         dstCS = SkColorSpace::MakeSRGB();
1627     }
1628 
1629     SkAlphaType workingAT;
1630     sk_sp<SkColorSpace> workingCS = filter->workingFormat(dstCS, &workingAT);
1631     SkColorInfo workingInfo(dstInfo.colorType(), workingAT, workingCS);
1632     KeyContextWithColorInfo workingContext(keyContext, workingInfo);
1633 
1634     // Use two nested compose blocks to chain (dst->working), child, and (working->dst) together
1635     // while appearing as one block to the parent node.
1636     Compose(keyContext, builder, gatherer,
1637             /* addInnerToKey= */ [&]() -> void {
1638                 // Inner compose
1639                 Compose(keyContext, builder, gatherer,
1640                         /* addInnerToKey= */ [&]() -> void {
1641                             // Innermost (inner of inner compose)
1642                             ColorSpaceTransformBlock::ColorSpaceTransformData data1(
1643                                     dstCS.get(), dstAT, workingCS.get(), workingAT);
1644                             ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer,
1645                                                                data1);
1646                         },
1647                         /* addOuterToKey= */ [&]() -> void {
1648                             // Middle (outer of inner compose)
1649                             AddToKey(workingContext, builder, gatherer, filter->child().get());
1650                         });
1651             },
1652             /* addOuterToKey= */ [&]() -> void {
1653                 // Outermost (outer of outer compose)
1654                 ColorSpaceTransformBlock::ColorSpaceTransformData data2(
1655                         workingCS.get(), workingAT, dstCS.get(), dstAT);
1656                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data2);
1657             });
1658 }
1659 
AddToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorFilter * filter)1660 void AddToKey(const KeyContext& keyContext,
1661               PaintParamsKeyBuilder* builder,
1662               PipelineDataGatherer* gatherer,
1663               const SkColorFilter* filter) {
1664     if (!filter) {
1665         // Calling code assumes a block will be appended. Add a fixed block to preserve shader
1666         // and PaintParamsKey structure in release builds but assert since this should either not
1667         // happen or should be changing high-level logic within PaintParams::toKey().
1668         SkASSERT(false);
1669         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1670         return;
1671     }
1672     switch (as_CFB(filter)->type()) {
1673     case SkColorFilterBase::Type::kNoop:
1674         // Return the input color as-is.
1675         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1676         return;
1677 #define M(type)                                                        \
1678     case SkColorFilterBase::Type::k##type:                             \
1679         add_to_key(keyContext,                                         \
1680                    builder,                                            \
1681                    gatherer,                                           \
1682                    static_cast<const Sk##type##ColorFilter*>(filter)); \
1683         return;
1684         SK_ALL_COLOR_FILTERS(M)
1685 #undef M
1686     }
1687     SkUNREACHABLE;
1688 }
1689 
NotifyImagesInUse(Recorder * recorder,DrawContext * drawContext,const SkColorFilter * filter)1690 void NotifyImagesInUse(Recorder* recorder, DrawContext* drawContext, const SkColorFilter* filter) {
1691     if (!filter) {
1692         return;
1693     }
1694     if (as_CFB(filter)->type() == SkColorFilterBase::Type::kCompose) {
1695         // Recurse to two children
1696         const auto* cf = static_cast<const SkComposeColorFilter*>(filter);
1697         NotifyImagesInUse(recorder, drawContext, cf->inner().get());
1698         NotifyImagesInUse(recorder, drawContext, cf->outer().get());
1699     } else if (as_CFB(filter)->type() == SkColorFilterBase::Type::kWorkingFormat) {
1700         // Recurse to one child
1701         const auto* wfcf = static_cast<const SkWorkingFormatColorFilter*>(filter);
1702         NotifyImagesInUse(recorder, drawContext, wfcf->child().get());
1703     } else if (as_CFB(filter)->type() == SkColorFilterBase::Type::kRuntime) {
1704         // Recurse to all children
1705         const auto* rcf = static_cast<const SkRuntimeColorFilter*>(filter);
1706         notify_in_use(recorder, drawContext, rcf->children());
1707     } // else other color filters do not rely on SkImages
1708 }
1709 
1710 // ==================================================================
1711 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlendShader * shader)1712 static void add_to_key(const KeyContext& keyContext,
1713                        PaintParamsKeyBuilder* builder,
1714                        PipelineDataGatherer* gatherer,
1715                        const SkBlendShader* shader) {
1716     SkASSERT(shader);
1717 
1718     Blend(keyContext, builder, gatherer,
1719             /* addBlendToKey= */ [&] () -> void {
1720                 AddBlendMode(keyContext, builder, gatherer, shader->mode());
1721             },
1722             /* addSrcToKey= */ [&]() -> void {
1723                 AddToKey(keyContext, builder, gatherer, shader->src().get());
1724             },
1725             /* addDstToKey= */ [&]() -> void {
1726                 AddToKey(keyContext, builder, gatherer, shader->dst().get());
1727             });
1728 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkBlendShader * shader)1729 static void notify_in_use(Recorder* recorder,
1730                           DrawContext* drawContext,
1731                           const SkBlendShader* shader) {
1732     // SkBlendShader uses a fixed blend mode, so there's no blender to recurse through
1733     NotifyImagesInUse(recorder, drawContext, shader->src().get());
1734     NotifyImagesInUse(recorder, drawContext, shader->dst().get());
1735 }
1736 
matrix_invert_or_identity(const SkMatrix & matrix)1737 static SkMatrix matrix_invert_or_identity(const SkMatrix& matrix) {
1738     SkMatrix inverseMatrix;
1739     if (!matrix.invert(&inverseMatrix)) {
1740         inverseMatrix.setIdentity();
1741     }
1742 
1743     return inverseMatrix;
1744 }
1745 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkCTMShader * shader)1746 static void add_to_key(const KeyContext& keyContext,
1747                        PaintParamsKeyBuilder* builder,
1748                        PipelineDataGatherer* gatherer,
1749                        const SkCTMShader* shader) {
1750     // CTM shaders are always given device coordinates, so we don't have to modify the CTM itself
1751     // with keyContext's local transform.
1752 
1753     SkMatrix lmInverse = matrix_invert_or_identity(shader->ctm());
1754     LocalMatrixShaderBlock::LMShaderData lmShaderData(lmInverse);
1755 
1756     KeyContextWithLocalMatrix newContext(keyContext, shader->ctm());
1757 
1758     LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
1759 
1760     AddToKey(newContext, builder, gatherer, shader->proxyShader().get());
1761 
1762     builder->endBlock();
1763 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkCTMShader * shader)1764 static void notify_in_use(Recorder* recorder, DrawContext* drawContext, const SkCTMShader* shader) {
1765     NotifyImagesInUse(recorder, drawContext, shader->proxyShader().get());
1766 }
1767 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorShader * shader)1768 static void add_to_key(const KeyContext& keyContext,
1769                        PaintParamsKeyBuilder* builder,
1770                        PipelineDataGatherer* gatherer,
1771                        const SkColorShader* shader) {
1772     SkASSERT(shader);
1773 
1774     SkPMColor4f color = map_color(shader->color(), sk_srgb_singleton(),
1775                                   keyContext.dstColorInfo().colorSpace());
1776 
1777     SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, color);
1778 }
notify_in_use(Recorder *,DrawContext *,const SkColorShader *)1779 static void notify_in_use(Recorder*, DrawContext*, const SkColorShader*) {
1780     // No-op
1781 }
1782 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorFilterShader * shader)1783 static void add_to_key(const KeyContext& keyContext,
1784                        PaintParamsKeyBuilder* builder,
1785                        PipelineDataGatherer* gatherer,
1786                        const SkColorFilterShader* shader) {
1787     SkASSERT(shader);
1788 
1789     Compose(keyContext, builder, gatherer,
1790             /* addInnerToKey= */ [&]() -> void {
1791                 AddToKey(keyContext, builder, gatherer, shader->shader().get());
1792             },
1793             /* addOuterToKey= */ [&]() -> void {
1794                 AddToKey(keyContext, builder, gatherer, shader->filter().get());
1795             });
1796 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkColorFilterShader * shader)1797 static void notify_in_use(Recorder* recorder,
1798                           DrawContext* drawContext,
1799                           const SkColorFilterShader* shader) {
1800     NotifyImagesInUse(recorder, drawContext, shader->shader().get());
1801     NotifyImagesInUse(recorder, drawContext, shader->filter().get());
1802 }
1803 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkCoordClampShader * shader)1804 static void add_to_key(const KeyContext& keyContext,
1805                        PaintParamsKeyBuilder* builder,
1806                        PipelineDataGatherer* gatherer,
1807                        const SkCoordClampShader* shader) {
1808     SkASSERT(shader);
1809 
1810     CoordClampShaderBlock::CoordClampData data(shader->subset());
1811 
1812     KeyContextWithCoordClamp childContext(keyContext);
1813     CoordClampShaderBlock::BeginBlock(keyContext, builder, gatherer, data);
1814     AddToKey(childContext, builder, gatherer, shader->shader().get());
1815     builder->endBlock();
1816 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkCoordClampShader * shader)1817 static void notify_in_use(Recorder* recorder,
1818                           DrawContext* drawContext,
1819                           const SkCoordClampShader* shader) {
1820     NotifyImagesInUse(recorder, drawContext, shader->shader().get());
1821 }
1822 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkEmptyShader *)1823 static void add_to_key(const KeyContext& keyContext,
1824                        PaintParamsKeyBuilder* builder,
1825                        PipelineDataGatherer* gatherer,
1826                        const SkEmptyShader*) {
1827     builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1828 }
notify_in_use(Recorder *,DrawContext *,const SkEmptyShader *)1829 static void notify_in_use(Recorder*, DrawContext*, const SkEmptyShader*) {
1830     // No-op
1831 }
1832 
add_yuv_image_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkImageShader * origShader,sk_sp<const SkImage> imageToDraw,SkSamplingOptions sampling)1833 static void add_yuv_image_to_key(const KeyContext& keyContext,
1834                                  PaintParamsKeyBuilder* builder,
1835                                  PipelineDataGatherer* gatherer,
1836                                  const SkImageShader* origShader,
1837                                  sk_sp<const SkImage> imageToDraw,
1838                                  SkSamplingOptions sampling) {
1839     SkASSERT(!imageToDraw->isAlphaOnly());
1840 
1841     const Image_YUVA* yuvaImage = static_cast<const Image_YUVA*>(imageToDraw.get());
1842     const SkYUVAInfo& yuvaInfo = yuvaImage->yuvaInfo();
1843     // We would want to add a translation to the local matrix to handle other sitings.
1844     SkASSERT(yuvaInfo.sitingX() == SkYUVAInfo::Siting::kCentered);
1845     SkASSERT(yuvaInfo.sitingY() == SkYUVAInfo::Siting::kCentered);
1846     YUVImageShaderBlock::ImageData imgData(sampling,
1847                                            origShader->tileModeX(),
1848                                            origShader->tileModeY(),
1849                                            imageToDraw->dimensions(),
1850                                            origShader->subset());
1851     for (int locIndex = 0; locIndex < SkYUVAInfo::kYUVAChannelCount; ++locIndex) {
1852         const TextureProxyView& view = yuvaImage->proxyView(locIndex);
1853         if (view) {
1854             imgData.fTextureProxies[locIndex] = view.refProxy();
1855             // The view's swizzle has the data channel for the YUVA location in all slots, so read
1856             // the 0th slot to determine fChannelSelect
1857             switch(view.swizzle()[0]) {
1858                 case 'r': imgData.fChannelSelect[locIndex] = {1.f, 0.f, 0.f, 0.f}; break;
1859                 case 'g': imgData.fChannelSelect[locIndex] = {0.f, 1.f, 0.f, 0.f}; break;
1860                 case 'b': imgData.fChannelSelect[locIndex] = {0.f, 0.f, 1.f, 0.f}; break;
1861                 case 'a': imgData.fChannelSelect[locIndex] = {0.f, 0.f, 0.f, 1.f}; break;
1862                 default:
1863                     imgData.fChannelSelect[locIndex] = {0.f, 0.f, 0.f, 0.f};
1864                     SkDEBUGFAILF("Unexpected swizzle for YUVA data: %c", view.swizzle()[0]);
1865                     break;
1866             }
1867         } else {
1868             // Only the A proxy view should be null, in which case we bind the Y proxy view to
1869             // pass validation and send all 1s for the channel selection to signal opaque alpha.
1870             SkASSERT(locIndex == 3);
1871             imgData.fTextureProxies[locIndex] = yuvaImage->proxyView(SkYUVAInfo::kY).refProxy();
1872             imgData.fChannelSelect[locIndex] = {1.f, 1.f, 1.f, 1.f};
1873             // For the hardcoded sampling no-swizzle case, we use this to set constant alpha
1874             imgData.fAlphaParam = 1;
1875         }
1876     }
1877 
1878     auto [ssx, ssy] = yuvaImage->uvSubsampleFactors();
1879     if (ssx > 1 || ssy > 1) {
1880         // We need to adjust the image size we use for sampling to reflect the actual image size of
1881         // the UV planes. However, since our coordinates are in Y's texel space we need to scale
1882         // accordingly.
1883         const TextureProxyView& view = yuvaImage->proxyView(SkYUVAInfo::kU);
1884         imgData.fImgSizeUV = {view.dimensions().width()*ssx, view.dimensions().height()*ssy};
1885         // This promotion of nearest to linear filtering for UV planes exists to mimic
1886         // libjpeg[-turbo]'s do_fancy_upsampling option. We will filter the subsampled plane,
1887         // however we want to filter at a fixed point for each logical image pixel to simulate
1888         // nearest neighbor. In the shader we detect that the UV filtermode doesn't match the Y
1889         // filtermode, and snap to Y pixel centers.
1890         if (imgData.fSampling.filter == SkFilterMode::kNearest) {
1891             imgData.fSamplingUV = SkSamplingOptions(SkFilterMode::kLinear,
1892                                                     imgData.fSampling.mipmap);
1893             // Consider a logical image pixel at the edge of the subset. When computing the logical
1894             // pixel color value we should use a blend of two values from the subsampled plane.
1895             // Depending on where the subset edge falls in actual subsampled plane, one of those
1896             // values may come from outside the subset. Hence, we will use the default inset
1897             // in Y texel space of 1/2. This applies the wrap mode to the subset but allows
1898             // linear filtering to read pixels that are just outside the subset.
1899             imgData.fLinearFilterUVInset.fX = 0.5f;
1900             imgData.fLinearFilterUVInset.fY = 0.5f;
1901         } else if (imgData.fSampling.filter == SkFilterMode::kLinear) {
1902             // We need to inset so that we aren't sampling outside the subset, but no farther.
1903             // Start by mapping the subset to UV texel space
1904             float scaleX = 1.f/ssx;
1905             float scaleY = 1.f/ssy;
1906             SkRect subsetUV = {imgData.fSubset.fLeft  *scaleX,
1907                                imgData.fSubset.fTop   *scaleY,
1908                                imgData.fSubset.fRight *scaleX,
1909                                imgData.fSubset.fBottom*scaleY};
1910             // Round to UV texel borders
1911             SkIRect iSubsetUV = subsetUV.roundOut();
1912             // Inset in UV and map back to Y texel space. This gives us the largest possible
1913             // inset rectangle that will not sample outside of the subset texels in UV space.
1914             SkRect insetRectUV = {(iSubsetUV.fLeft  +0.5f)*ssx,
1915                                   (iSubsetUV.fTop   +0.5f)*ssy,
1916                                   (iSubsetUV.fRight -0.5f)*ssx,
1917                                   (iSubsetUV.fBottom-0.5f)*ssy};
1918             // Compute intersection with original inset
1919             SkRect insetRect = imgData.fSubset.makeOutset(-0.5f, -0.5f);
1920             (void) insetRect.intersect(insetRectUV);
1921             // Compute max inset values to ensure we always remain within the subset.
1922             imgData.fLinearFilterUVInset = {std::max(insetRect.fLeft - imgData.fSubset.fLeft,
1923                                                      imgData.fSubset.fRight - insetRect.fRight),
1924                                             std::max(insetRect.fTop - imgData.fSubset.fTop,
1925                                                      imgData.fSubset.fBottom - insetRect.fBottom)};
1926         }
1927     }
1928 
1929     float yuvM[20];
1930     SkColorMatrix_YUV2RGB(yuvaInfo.yuvColorSpace(), yuvM);
1931     // We drop the fourth column entirely since the transformation
1932     // should not depend on alpha. The fifth column is sent as a separate
1933     // vector. The fourth row is also dropped entirely because alpha should
1934     // never be modified.
1935     SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
1936     SkASSERT(yuvM[15] == 0 && yuvM[16] == 0 && yuvM[17] == 0 && yuvM[19] == 0);
1937     imgData.fYUVtoRGBMatrix.setAll(
1938         yuvM[ 0], yuvM[ 1], yuvM[ 2],
1939         yuvM[ 5], yuvM[ 6], yuvM[ 7],
1940         yuvM[10], yuvM[11], yuvM[12]
1941     );
1942     imgData.fYUVtoRGBTranslate = {yuvM[4], yuvM[9], yuvM[14]};
1943 
1944     SkColorSpaceXformSteps steps;
1945     SkASSERT(steps.fFlags.mask() == 0);   // By default, the colorspace should have no effect
1946 
1947     // The actual output from the YUV image shader for non-opaque images is unpremul so
1948     // we need to correct for the fact that the Image_YUVA_Graphite's alpha type is premul.
1949     SkAlphaType srcAT = imageToDraw->alphaType() == kPremul_SkAlphaType
1950                                 ? kUnpremul_SkAlphaType
1951                                 : imageToDraw->alphaType();
1952     if (origShader->isRaw()) {
1953         // Because we've avoided the premul alpha step in the YUV shader, we need to make sure
1954         // it happens when drawing unpremul (i.e., non-opaque) images.
1955         steps = SkColorSpaceXformSteps(imageToDraw->colorSpace(),
1956                                        srcAT,
1957                                        imageToDraw->colorSpace(),
1958                                        imageToDraw->alphaType());
1959     } else {
1960         SkAlphaType dstAT = keyContext.dstColorInfo().alphaType();
1961         // Setting the dst alphaType up this way is necessary because otherwise the constructor
1962         // for SkColorSpaceXformSteps will set dstAT = srcAT when dstAT == kOpaque, and the
1963         // premul step needed for non-opaque images won't occur.
1964         if (dstAT == kOpaque_SkAlphaType && srcAT == kUnpremul_SkAlphaType) {
1965             dstAT = kPremul_SkAlphaType;
1966         }
1967         steps = SkColorSpaceXformSteps(imageToDraw->colorSpace(),
1968                                        srcAT,
1969                                        keyContext.dstColorInfo().colorSpace(),
1970                                        dstAT);
1971     }
1972     ColorSpaceTransformBlock::ColorSpaceTransformData data(steps);
1973 
1974     Compose(keyContext, builder, gatherer,
1975             /* addInnerToKey= */ [&]() -> void {
1976                 YUVImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
1977             },
1978             /* addOuterToKey= */ [&]() -> void {
1979                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
1980             });
1981 }
1982 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkImageShader * shader)1983 static void add_to_key(const KeyContext& keyContext,
1984                        PaintParamsKeyBuilder* builder,
1985                        PipelineDataGatherer* gatherer,
1986                        const SkImageShader* shader) {
1987     SkASSERT(shader);
1988 
1989     auto [ imageToDraw, newSampling ] = GetGraphiteBacked(keyContext.recorder(),
1990                                                           shader->image().get(),
1991                                                           shader->sampling());
1992     if (!imageToDraw) {
1993         SKGPU_LOG_W("Couldn't convert ImageShader's image to a Graphite-backed image");
1994         builder->addBlock(BuiltInCodeSnippetID::kError);
1995         return;
1996     }
1997     if (!as_IB(shader->image())->isGraphiteBacked()) {
1998         // GetGraphiteBacked() created a new image (or fetched a cached image) from the client
1999         // image provider. This image was not available when NotifyInUse() visited the shader tree,
2000         // so call notify again. These images shouldn't really be producing new tasks since it's
2001         // unlikely that a client will be fulfilling with a dynamic image that wraps a long-lived
2002         // SkSurface. However, the images can be linked to a surface that rendered the initial
2003         // content and not calling notifyInUse() prevents unlinking the image from the Device.
2004         // If the client image provider then holds on to many of these images, the leaked Device and
2005         // DrawContext memory can be surprisingly high. b/338453542.
2006         // TODO (b/330864257): Once paint keys are extracted at draw time, AddToKey() will be
2007         // fully responsible for notifyInUse() calls and then we can simply always call this on
2008         // `imageToDraw`. The DrawContext that samples the image will also be available to AddToKey
2009         // so we won't have to pass in nullptr.
2010         SkASSERT(as_IB(imageToDraw)->isGraphiteBacked());
2011         static_cast<Image_Base*>(imageToDraw.get())->notifyInUse(keyContext.recorder(),
2012                                                                  /*drawContext=*/nullptr);
2013     }
2014     if (as_IB(imageToDraw)->isYUVA()) {
2015         return add_yuv_image_to_key(keyContext,
2016                                       builder,
2017                                       gatherer,
2018                                       shader,
2019                                       std::move(imageToDraw),
2020                                       newSampling);
2021     }
2022 
2023     auto view = AsView(imageToDraw.get());
2024     SkASSERT(newSampling.mipmap == SkMipmapMode::kNone || view.mipmapped() == Mipmapped::kYes);
2025 
2026     ImageShaderBlock::ImageData imgData(shader->sampling(),
2027                                         shader->tileModeX(),
2028                                         shader->tileModeY(),
2029                                         view.proxy()->dimensions(),
2030                                         shader->subset());
2031 
2032     // Here we detect pixel aligned blit-like image draws. Some devices have low precision filtering
2033     // and will produce degraded (blurry) images unexpectedly for sequential exact pixel blits when
2034     // not using nearest filtering. This is common for canvas scrolling implementations. Forcing
2035     // nearest filtering when possible can also be a minor perf/power optimization depending on the
2036     // hardware.
2037     bool samplingHasNoEffect = false;
2038     // Cubic sampling is will not filter the same as nearest even when pixel aligned.
2039     if (keyContext.optimizeSampling() == KeyContext::OptimizeSampling::kYes &&
2040         !newSampling.useCubic) {
2041         SkMatrix totalM = keyContext.local2Dev().asM33();
2042         if (keyContext.localMatrix()) {
2043             totalM.preConcat(*keyContext.localMatrix());
2044         }
2045         totalM.normalizePerspective();
2046         // The matrix should be translation with only pixel aligned 2d translation.
2047         samplingHasNoEffect = totalM.isTranslate() && SkScalarIsInt(totalM.getTranslateX()) &&
2048                               SkScalarIsInt(totalM.getTranslateY());
2049     }
2050 
2051     imgData.fSampling = samplingHasNoEffect ? SkFilterMode::kNearest : newSampling;
2052     imgData.fTextureProxy = view.refProxy();
2053     skgpu::Swizzle readSwizzle = view.swizzle();
2054     // If the color type is alpha-only, propagate the alpha value to the other channels.
2055     if (imageToDraw->isAlphaOnly()) {
2056         readSwizzle = skgpu::Swizzle::Concat(readSwizzle, skgpu::Swizzle("000a"));
2057     }
2058     ColorSpaceTransformBlock::ColorSpaceTransformData colorXformData(
2059             SwizzleClassToReadEnum(readSwizzle));
2060 
2061     if (!shader->isRaw()) {
2062         colorXformData.fSteps = SkColorSpaceXformSteps(imageToDraw->colorSpace(),
2063                                                        imageToDraw->alphaType(),
2064                                                        keyContext.dstColorInfo().colorSpace(),
2065                                                        keyContext.dstColorInfo().alphaType());
2066 
2067         if (imageToDraw->isAlphaOnly() && keyContext.scope() != KeyContext::Scope::kRuntimeEffect) {
2068             Blend(keyContext, builder, gatherer,
2069                   /* addBlendToKey= */ [&] () -> void {
2070                       AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kDstIn);
2071                   },
2072                   /* addSrcToKey= */ [&] () -> void {
2073                       Compose(keyContext, builder, gatherer,
2074                               /* addInnerToKey= */ [&]() -> void {
2075                                   ImageShaderBlock::AddBlock(keyContext, builder, gatherer,
2076                                                              imgData);
2077                               },
2078                               /* addOuterToKey= */ [&]() -> void {
2079                                   ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer,
2080                                                                      colorXformData);
2081                               });
2082                   },
2083                   /* addDstToKey= */ [&]() -> void {
2084                       RGBPaintColorBlock::AddBlock(keyContext, builder, gatherer);
2085                   });
2086             return;
2087         }
2088     }
2089 
2090     Compose(keyContext, builder, gatherer,
2091             /* addInnerToKey= */ [&]() -> void {
2092                 ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
2093             },
2094             /* addOuterToKey= */ [&]() -> void {
2095                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, colorXformData);
2096             });
2097 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkImageShader * shader)2098 static void notify_in_use(Recorder* recorder,
2099                           DrawContext* drawContext,
2100                           const SkImageShader* shader) {
2101     auto image = as_IB(shader->image());
2102     if (!image->isGraphiteBacked()) {
2103         // If it's not graphite-backed, there's no pending graphite work.
2104         return;
2105     }
2106 
2107     static_cast<Image_Base*>(image)->notifyInUse(recorder, drawContext);
2108 }
2109 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkLocalMatrixShader * shader)2110 static void add_to_key(const KeyContext& keyContext,
2111                        PaintParamsKeyBuilder* builder,
2112                        PipelineDataGatherer* gatherer,
2113                        const SkLocalMatrixShader* shader) {
2114     SkASSERT(shader);
2115     auto wrappedShader = shader->wrappedShader().get();
2116 
2117     // Fold the texture's origin flip into the local matrix so that the image shader doesn't need
2118     // additional state.
2119     SkMatrix matrix;
2120 
2121     SkShaderBase* wrappedShaderBase = as_SB(wrappedShader);
2122     if (wrappedShaderBase->type() == SkShaderBase::ShaderType::kImage) {
2123         auto imgShader = static_cast<const SkImageShader*>(wrappedShader);
2124         // If the image is not graphite backed then we can assume the origin will be TopLeft as we
2125         // require that in the ImageProvider utility. Also Graphite YUV images are assumed to be
2126         // TopLeft origin.
2127         auto imgBase = as_IB(imgShader->image());
2128         if (imgBase->isGraphiteBacked()) {
2129             // The YUV formats can encode their own origin including reflection and rotation,
2130             // so we need to concat that to the local matrix transform.
2131             if (imgBase->isYUVA()) {
2132                 auto imgYUVA = static_cast<const Image_YUVA*>(imgBase);
2133                 SkASSERT(imgYUVA);
2134                 matrix = matrix_invert_or_identity(imgYUVA->yuvaInfo().originMatrix());
2135             } else {
2136                 auto imgGraphite = static_cast<Image*>(imgBase);
2137                 SkASSERT(imgGraphite);
2138                 const auto& view = imgGraphite->textureProxyView();
2139                 if (view.origin() == Origin::kBottomLeft) {
2140                     matrix.setScaleY(-1);
2141                     matrix.setTranslateY(view.height());
2142                 }
2143             }
2144 
2145         }
2146     } else if (wrappedShaderBase->type() == SkShaderBase::ShaderType::kGradientBase) {
2147         auto gradShader = static_cast<const SkGradientBaseShader*>(wrappedShader);
2148         matrix = gradShader->getGradientMatrix();
2149 
2150         // Override the conical gradient matrix since graphite uses a different algorithm
2151         // than the ganesh and raster backends.
2152         if (gradShader->asGradient() == SkShaderBase::GradientType::kConical) {
2153             auto conicalShader = static_cast<const SkConicalGradient*>(gradShader);
2154 
2155             SkMatrix conicalMatrix;
2156             if (conicalShader->getType() == SkConicalGradient::Type::kRadial) {
2157                 SkPoint center = conicalShader->getStartCenter();
2158                 conicalMatrix.postTranslate(-center.fX, -center.fY);
2159 
2160                 float scale = sk_ieee_float_divide(1, conicalShader->getDiffRadius());
2161                 conicalMatrix.postScale(scale, scale);
2162             } else {
2163                 SkAssertResult(SkConicalGradient::MapToUnitX(conicalShader->getStartCenter(),
2164                                                              conicalShader->getEndCenter(),
2165                                                              &conicalMatrix));
2166             }
2167             matrix = conicalMatrix;
2168         }
2169     }
2170 
2171     SkMatrix lmInverse = matrix_invert_or_identity(shader->localMatrix());
2172     lmInverse.postConcat(matrix);
2173 
2174     LocalMatrixShaderBlock::LMShaderData lmShaderData(lmInverse);
2175 
2176     KeyContextWithLocalMatrix newContext(keyContext, shader->localMatrix());
2177 
2178     LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
2179 
2180     AddToKey(newContext, builder, gatherer, wrappedShader);
2181 
2182     builder->endBlock();
2183 }
2184 
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkLocalMatrixShader * shader)2185 static void notify_in_use(Recorder* recorder,
2186                           DrawContext* drawContext,
2187                           const SkLocalMatrixShader* shader) {
2188     NotifyImagesInUse(recorder, drawContext, shader->wrappedShader().get());
2189 }
2190 
2191 // If either of these change then the corresponding change must also be made in the SkSL
2192 // perlin_noise_shader function.
2193 static_assert((int)SkPerlinNoiseShaderType::kFractalNoise ==
2194               (int)PerlinNoiseShaderBlock::Type::kFractalNoise);
2195 static_assert((int)SkPerlinNoiseShaderType::kTurbulence ==
2196               (int)PerlinNoiseShaderBlock::Type::kTurbulence);
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkPerlinNoiseShader * shader)2197 static void add_to_key(const KeyContext& keyContext,
2198                        PaintParamsKeyBuilder* builder,
2199                        PipelineDataGatherer* gatherer,
2200                        const SkPerlinNoiseShader* shader) {
2201     SkASSERT(shader);
2202     SkASSERT(shader->numOctaves());
2203 
2204     std::unique_ptr<SkPerlinNoiseShader::PaintingData> paintingData = shader->getPaintingData();
2205     paintingData->generateBitmaps();
2206 
2207     sk_sp<TextureProxy> perm =
2208             RecorderPriv::CreateCachedProxy(keyContext.recorder(),
2209                                             paintingData->getPermutationsBitmap(),
2210                                             "PerlinNoisePermTable");
2211 
2212     sk_sp<TextureProxy> noise =
2213             RecorderPriv::CreateCachedProxy(keyContext.recorder(), paintingData->getNoiseBitmap(),
2214                                             "PerlinNoiseNoiseTable");
2215 
2216     if (!perm || !noise) {
2217         SKGPU_LOG_W("Couldn't create tables for PerlinNoiseShader");
2218         builder->addBlock(BuiltInCodeSnippetID::kError);
2219         return;
2220     }
2221 
2222     PerlinNoiseShaderBlock::PerlinNoiseData perlinData(
2223             static_cast<PerlinNoiseShaderBlock::Type>(shader->noiseType()),
2224             paintingData->fBaseFrequency,
2225             shader->numOctaves(),
2226             {paintingData->fStitchDataInit.fWidth, paintingData->fStitchDataInit.fHeight});
2227 
2228     perlinData.fPermutationsProxy = std::move(perm);
2229     perlinData.fNoiseProxy = std::move(noise);
2230 
2231     PerlinNoiseShaderBlock::AddBlock(keyContext, builder, gatherer, perlinData);
2232 }
notify_in_use(Recorder *,DrawContext *,const SkPerlinNoiseShader *)2233 static void notify_in_use(Recorder*, DrawContext*, const SkPerlinNoiseShader*) {
2234     // No-op, perlin noise has no children.
2235 }
2236 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkPictureShader * shader)2237 static void add_to_key(const KeyContext& keyContext,
2238                        PaintParamsKeyBuilder* builder,
2239                        PipelineDataGatherer* gatherer,
2240                        const SkPictureShader* shader) {
2241     SkASSERT(shader);
2242 
2243     Recorder* recorder = keyContext.recorder();
2244     const Caps* caps = recorder->priv().caps();
2245 
2246     // TODO: We'll need additional plumbing to get the correct props from our callers. In
2247     // particular we'll need to expand the keyContext to have the surfaceProps.
2248     SkSurfaceProps props{};
2249 
2250     SkMatrix totalM = keyContext.local2Dev().asM33();
2251     if (keyContext.localMatrix()) {
2252         totalM.preConcat(*keyContext.localMatrix());
2253     }
2254     auto info = SkPictureShader::CachedImageInfo::Make(shader->tile(),
2255                                                        totalM,
2256                                                        keyContext.dstColorInfo().colorType(),
2257                                                        keyContext.dstColorInfo().colorSpace(),
2258                                                        caps->maxTextureSize(),
2259                                                        props);
2260     if (!info.success) {
2261         SKGPU_LOG_W("Couldn't access PictureShaders' Image info");
2262         builder->addBlock(BuiltInCodeSnippetID::kError);
2263         return;
2264     }
2265 
2266     // NOTE: While this is intended to be a "scratch" surface, we don't use MakeScratch() because
2267     // the SkPicture could contain arbitrary operations that rely on the Recorder's atlases, which
2268     // means the Surface's device has to participate in flushing when the atlas fills up.
2269     // TODO: Can this be an approx-fit image that's generated?
2270     // TODO: right now we're explicitly not caching here. We could expand the ImageProvider
2271     // API to include already Graphite-backed images, add a Recorder-local cache or add
2272     // rendered-picture images to the global cache.
2273     sk_sp<Surface> surface = Surface::Make(recorder,
2274                                            info.imageInfo,
2275                                            "PictureShaderTexture",
2276                                            Budgeted::kYes,
2277                                            Mipmapped::kNo,
2278                                            SkBackingFit::kExact,
2279                                            &info.props);
2280     if (!surface) {
2281         SKGPU_LOG_W("Could not create surface to render PictureShader");
2282         builder->addBlock(BuiltInCodeSnippetID::kError);
2283         return;
2284     }
2285 
2286     // NOTE: Don't call CachedImageInfo::makeImage() since that uses the legacy makeImageSnapshot()
2287     // API, which results in an extra texture copy on a Graphite Surface.
2288     surface->getCanvas()->concat(info.matrixForDraw);
2289     surface->getCanvas()->drawPicture(shader->picture().get());
2290     sk_sp<SkImage> img = SkSurfaces::AsImage(std::move(surface));
2291     // TODO: 'img' did not exist when notify_in_use() was called, but ideally the DrawTask to render
2292     // into 'surface' would be a child of the current device. While we push all tasks to the root
2293     // list this works out okay, but will need to be addressed before we move off that system.
2294     if (!img) {
2295         SKGPU_LOG_W("Couldn't create SkImage for PictureShader");
2296         builder->addBlock(BuiltInCodeSnippetID::kError);
2297         return;
2298     }
2299 
2300     const auto shaderLM = SkMatrix::Scale(1.f/info.tileScale.width(), 1.f/info.tileScale.height());
2301     sk_sp<SkShader> imgShader = img->makeShader(shader->tileModeX(), shader->tileModeY(),
2302                                                 SkSamplingOptions(shader->filter()), &shaderLM);
2303     if (!imgShader) {
2304         SKGPU_LOG_W("Couldn't create SkImageShader for PictureShader");
2305         builder->addBlock(BuiltInCodeSnippetID::kError);
2306         return;
2307     }
2308 
2309     AddToKey(keyContext, builder, gatherer, imgShader.get());
2310 }
notify_in_use(Recorder *,DrawContext *,const SkPictureShader *)2311 static void notify_in_use(Recorder*, DrawContext*, const SkPictureShader*) {
2312     // While the SkPicture the shader points to, may have Graphite-backed shaders that need to be
2313     // notified, that will happen when the picture is rendered into an image in add_to_key
2314 }
2315 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRuntimeShader * shader)2316 static void add_to_key(const KeyContext& keyContext,
2317                        PaintParamsKeyBuilder* builder,
2318                        PipelineDataGatherer* gatherer,
2319                        const SkRuntimeShader* shader) {
2320     SkASSERT(shader);
2321     sk_sp<SkRuntimeEffect> effect = shader->effect();
2322     sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
2323             effect->uniforms(),
2324             shader->uniformData(keyContext.dstColorInfo().colorSpace()),
2325             keyContext.dstColorInfo().colorSpace());
2326     SkASSERT(uniforms);
2327 
2328     if (!RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
2329                                         { effect, std::move(uniforms) })) {
2330         RuntimeEffectBlock::AddNoOpEffect(keyContext, builder, gatherer, effect.get());
2331         return;
2332     }
2333 
2334     add_children_to_key(keyContext, builder, gatherer,
2335                         shader->children(), effect.get());
2336 
2337     builder->endBlock();
2338 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkRuntimeShader * shader)2339 static void notify_in_use(Recorder* recorder,
2340                           DrawContext* drawContext,
2341                           const SkRuntimeShader* shader) {
2342     notify_in_use(recorder, drawContext, shader->children());
2343 }
2344 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkTransformShader * shader)2345 static void add_to_key(const KeyContext& keyContext,
2346                        PaintParamsKeyBuilder* builder,
2347                        PipelineDataGatherer* gatherer,
2348                        const SkTransformShader* shader) {
2349     SKGPU_LOG_W("Raster-only SkShader (SkTransformShader) encountered");
2350     builder->addBlock(BuiltInCodeSnippetID::kError);
2351 }
notify_in_use(Recorder *,DrawContext *,const SkTransformShader *)2352 static void notify_in_use(Recorder*, DrawContext*, const SkTransformShader*) {
2353     // no-op
2354 }
2355 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkTriColorShader * shader)2356 static void add_to_key(const KeyContext& keyContext,
2357                        PaintParamsKeyBuilder* builder,
2358                        PipelineDataGatherer* gatherer,
2359                        const SkTriColorShader* shader) {
2360     SKGPU_LOG_W("Raster-only SkShader (SkTriColorShader) encountered");
2361     builder->addBlock(BuiltInCodeSnippetID::kError);
2362 }
notify_in_use(Recorder *,DrawContext *,const SkTriColorShader *)2363 static void notify_in_use(Recorder*, DrawContext*, const SkTriColorShader*) {
2364     // no-op
2365 }
2366 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkWorkingColorSpaceShader * shader)2367 static void add_to_key(const KeyContext& keyContext,
2368                        PaintParamsKeyBuilder* builder,
2369                        PipelineDataGatherer* gatherer,
2370                        const SkWorkingColorSpaceShader* shader) {
2371     SkASSERT(shader);
2372 
2373     const SkColorInfo& dstInfo = keyContext.dstColorInfo();
2374     const SkAlphaType dstAT = dstInfo.alphaType();
2375     sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace();
2376     if (!dstCS) {
2377         dstCS = SkColorSpace::MakeSRGB();
2378     }
2379 
2380     sk_sp<SkColorSpace> workingCS = shader->workingSpace();
2381     SkColorInfo workingInfo(dstInfo.colorType(), dstAT, workingCS);
2382     KeyContextWithColorInfo workingContext(keyContext, workingInfo);
2383 
2384     // Compose the inner shader (in the working space) with a (working->dst) transform:
2385     Compose(keyContext, builder, gatherer,
2386         /* addInnerToKey= */ [&]() -> void {
2387             AddToKey(workingContext, builder, gatherer, shader->shader().get());
2388         },
2389         /* addOuterToKey= */ [&]() -> void {
2390             ColorSpaceTransformBlock::ColorSpaceTransformData data(
2391                     workingCS.get(), dstAT, dstCS.get(), dstAT);
2392             ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
2393         });
2394 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkWorkingColorSpaceShader * shader)2395 static void notify_in_use(Recorder* recorder,
2396                           DrawContext* drawContext,
2397                           const SkWorkingColorSpaceShader* shader) {
2398     NotifyImagesInUse(recorder, drawContext, shader->shader().get());
2399 }
2400 
create_color_and_offset_bitmap(int numStops,const SkPMColor4f * colors,const float * offsets)2401 static SkBitmap create_color_and_offset_bitmap(int numStops,
2402                                                const SkPMColor4f* colors,
2403                                                const float* offsets) {
2404     SkBitmap colorsAndOffsetsBitmap;
2405 
2406     colorsAndOffsetsBitmap.allocPixels(
2407             SkImageInfo::Make(numStops, 2, kRGBA_F16_SkColorType, kPremul_SkAlphaType));
2408 
2409     for (int i = 0; i < numStops; i++) {
2410         // TODO: there should be a way to directly set a premul pixel in a bitmap with
2411         // a premul color.
2412         SkColor4f unpremulColor = colors[i].unpremul();
2413         colorsAndOffsetsBitmap.erase(unpremulColor, SkIRect::MakeXYWH(i, 0, 1, 1));
2414 
2415         float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops - 1);
2416         SkASSERT(offset >= 0.0f && offset <= 1.0f);
2417 
2418         int exponent;
2419         float mantissa = frexp(offset, &exponent);
2420 
2421         SkHalf halfE = SkFloatToHalf(exponent);
2422         if ((int)SkHalfToFloat(halfE) != exponent) {
2423             SKGPU_LOG_W("Encoding gradient to f16 failed");
2424             return {};
2425         }
2426 
2427 #if defined(SK_DEBUG)
2428         SkHalf halfM = SkFloatToHalf(mantissa);
2429 
2430         float restored = ldexp(SkHalfToFloat(halfM), (int)SkHalfToFloat(halfE));
2431         float error = abs(restored - offset);
2432         SkASSERT(error < 0.001f);
2433 #endif
2434 
2435         // TODO: we're only using 2 of the f16s here. The encoding could be altered to better
2436         // preserve precision. This encoding yields < 0.001f error for 2^20 evenly spaced stops.
2437         colorsAndOffsetsBitmap.erase(SkColor4f{mantissa, (float)exponent, 0, 1},
2438                                      SkIRect::MakeXYWH(i, 1, 1, 1));
2439     }
2440 
2441     return colorsAndOffsetsBitmap;
2442 }
2443 
2444 // Please see GrGradientShader.cpp::make_interpolated_to_dst for substantial comments
2445 // as to why this code is structured this way.
make_interpolated_to_dst(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const GradientShaderBlocks::GradientData & gradData,const SkGradientShader::Interpolation & interp,SkColorSpace * intermediateCS)2446 static void make_interpolated_to_dst(const KeyContext& keyContext,
2447                                      PaintParamsKeyBuilder* builder,
2448                                      PipelineDataGatherer* gatherer,
2449                                      const GradientShaderBlocks::GradientData& gradData,
2450                                      const SkGradientShader::Interpolation& interp,
2451                                      SkColorSpace* intermediateCS) {
2452     using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
2453 
2454     bool inputPremul = static_cast<bool>(interp.fInPremul);
2455 
2456     switch (interp.fColorSpace) {
2457         case ColorSpace::kLab:
2458         case ColorSpace::kOKLab:
2459         case ColorSpace::kOKLabGamutMap:
2460         case ColorSpace::kLCH:
2461         case ColorSpace::kOKLCH:
2462         case ColorSpace::kOKLCHGamutMap:
2463         case ColorSpace::kHSL:
2464         case ColorSpace::kHWB:
2465             inputPremul = false;
2466             break;
2467         default:
2468             break;
2469     }
2470 
2471     const SkColorInfo& dstColorInfo = keyContext.dstColorInfo();
2472 
2473     SkColorSpace* dstColorSpace =
2474             dstColorInfo.colorSpace() ? dstColorInfo.colorSpace() : sk_srgb_singleton();
2475 
2476     SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
2477 
2478     ColorSpaceTransformBlock::ColorSpaceTransformData data(
2479             intermediateCS, intermediateAlphaType, dstColorSpace, dstColorInfo.alphaType());
2480 
2481     // The gradient block and colorSpace conversion block need to be combined
2482     // (via the Compose block) so that the localMatrix block can treat them as
2483     // one child.
2484     Compose(keyContext, builder, gatherer,
2485             /* addInnerToKey= */ [&]() -> void {
2486                 GradientShaderBlocks::AddBlock(keyContext, builder, gatherer, gradData);
2487             },
2488             /* addOuterToKey= */ [&]() -> void {
2489                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
2490             });
2491 }
2492 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkGradientBaseShader * shader,SkPoint point0,SkPoint point1,float radius0,float radius1,float bias,float scale)2493 static void add_gradient_to_key(const KeyContext& keyContext,
2494                                 PaintParamsKeyBuilder* builder,
2495                                 PipelineDataGatherer* gatherer,
2496                                 const SkGradientBaseShader* shader,
2497                                 SkPoint point0,
2498                                 SkPoint point1,
2499                                 float radius0,
2500                                 float radius1,
2501                                 float bias,
2502                                 float scale) {
2503     SkColor4fXformer xformedColors(shader, keyContext.dstColorInfo().colorSpace());
2504     const SkPMColor4f* colors = xformedColors.fColors.begin();
2505     const float* positions = xformedColors.fPositions;
2506     const int colorCount = xformedColors.fColors.size();
2507 
2508     sk_sp<TextureProxy> proxy;
2509 
2510     bool useStorageBuffer = keyContext.caps()->gradientBufferSupport();
2511     if (colorCount > GradientShaderBlocks::GradientData::kNumInternalStorageStops
2512             && !useStorageBuffer) {
2513         if (shader->cachedBitmap().empty()) {
2514             SkBitmap colorsAndOffsetsBitmap =
2515                     create_color_and_offset_bitmap(colorCount, colors, positions);
2516             if (colorsAndOffsetsBitmap.empty()) {
2517                 SKGPU_LOG_W("Couldn't create GradientShader's color and offset bitmap");
2518                 builder->addBlock(BuiltInCodeSnippetID::kError);
2519                 return;
2520             }
2521             shader->setCachedBitmap(colorsAndOffsetsBitmap);
2522         }
2523 
2524         proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), shader->cachedBitmap(),
2525                                                 "GradientTexture");
2526         if (!proxy) {
2527             SKGPU_LOG_W("Couldn't create GradientShader's color and offset bitmap proxy");
2528             builder->addBlock(BuiltInCodeSnippetID::kError);
2529             return;
2530         }
2531     }
2532 
2533     GradientShaderBlocks::GradientData data(shader->asGradient(),
2534                                             point0,
2535                                             point1,
2536                                             radius0,
2537                                             radius1,
2538                                             bias,
2539                                             scale,
2540                                             shader->getTileMode(),
2541                                             colorCount,
2542                                             colors,
2543                                             positions,
2544                                             shader,
2545                                             std::move(proxy),
2546                                             useStorageBuffer,
2547                                             shader->getInterpolation());
2548 
2549     make_interpolated_to_dst(keyContext,
2550                              builder,
2551                              gatherer,
2552                              data,
2553                              shader->getInterpolation(),
2554                              xformedColors.fIntermediateColorSpace.get());
2555 }
2556 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkConicalGradient * shader)2557 static void add_gradient_to_key(const KeyContext& keyContext,
2558                                 PaintParamsKeyBuilder* builder,
2559                                 PipelineDataGatherer* gatherer,
2560                                 const SkConicalGradient* shader) {
2561     SkScalar r0 = shader->getStartRadius();
2562     SkScalar r1 = shader->getEndRadius();
2563 
2564     if (shader->getType() != SkConicalGradient::Type::kRadial) {
2565         // Since we map the centers to be (0,0) and (1,0) in the gradient matrix,
2566         // there is a scale of 1/distance-between-centers that has to be applied to the radii.
2567         r0 /= shader->getCenterX1();
2568         r1 /= shader->getCenterX1();
2569     } else {
2570         r0 /= shader->getDiffRadius();
2571         r1 /= shader->getDiffRadius();
2572     }
2573 
2574     add_gradient_to_key(keyContext,
2575                         builder,
2576                         gatherer,
2577                         shader,
2578                         shader->getStartCenter(),
2579                         shader->getEndCenter(),
2580                         r0,
2581                         r1,
2582                         0.0f,
2583                         0.0f);
2584 }
2585 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkLinearGradient * shader)2586 static void add_gradient_to_key(const KeyContext& keyContext,
2587                                 PaintParamsKeyBuilder* builder,
2588                                 PipelineDataGatherer* gatherer,
2589                                 const SkLinearGradient* shader) {
2590     add_gradient_to_key(keyContext,
2591                         builder,
2592                         gatherer,
2593                         shader,
2594                         shader->start(),
2595                         shader->end(),
2596                         0.0f,
2597                         0.0f,
2598                         0.0f,
2599                         0.0f);
2600 }
2601 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRadialGradient * shader)2602 static void add_gradient_to_key(const KeyContext& keyContext,
2603                                 PaintParamsKeyBuilder* builder,
2604                                 PipelineDataGatherer* gatherer,
2605                                 const SkRadialGradient* shader) {
2606     add_gradient_to_key(keyContext,
2607                         builder,
2608                         gatherer,
2609                         shader,
2610                         shader->center(),
2611                         { 0.0f, 0.0f },
2612                         shader->radius(),
2613                         0.0f,
2614                         0.0f,
2615                         0.0f);
2616 }
2617 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkSweepGradient * shader)2618 static void add_gradient_to_key(const KeyContext& keyContext,
2619                                 PaintParamsKeyBuilder* builder,
2620                                 PipelineDataGatherer* gatherer,
2621                                 const SkSweepGradient* shader) {
2622     add_gradient_to_key(keyContext,
2623                         builder,
2624                         gatherer,
2625                         shader,
2626                         shader->center(),
2627                         { 0.0f, 0.0f },
2628                         0.0f,
2629                         0.0f,
2630                         shader->tBias(),
2631                         shader->tScale());
2632 }
2633 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkGradientBaseShader * shader)2634 static void add_to_key(const KeyContext& keyContext,
2635                        PaintParamsKeyBuilder* builder,
2636                        PipelineDataGatherer* gatherer,
2637                        const SkGradientBaseShader* shader) {
2638     SkASSERT(shader);
2639     switch (shader->asGradient()) {
2640 #define M(type)                                                               \
2641     case SkShaderBase::GradientType::k##type:                                 \
2642         add_gradient_to_key(keyContext,                                       \
2643                             builder,                                          \
2644                             gatherer,                                         \
2645                             static_cast<const Sk##type##Gradient*>(shader));  \
2646         return;
2647         SK_ALL_GRADIENTS(M)
2648 #undef M
2649         case SkShaderBase::GradientType::kNone:
2650             SkDEBUGFAIL("Gradient shader says its type is none");
2651             return;
2652     }
2653     SkUNREACHABLE;
2654 }
notify_in_use(Recorder *,DrawContext *,const SkGradientBaseShader *)2655 static void notify_in_use(Recorder*, DrawContext*, const SkGradientBaseShader*) {
2656     // Gradients do not have children, so no images to notify
2657 }
2658 
AddToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkShader * shader)2659 void AddToKey(const KeyContext& keyContext,
2660               PaintParamsKeyBuilder* builder,
2661               PipelineDataGatherer* gatherer,
2662               const SkShader* shader) {
2663     if (!shader) {
2664         // Calling code assumes a block will be appended. Add a fixed block to preserve shader
2665         // and PaintParamsKey structure in release builds but assert since this should either not
2666         // happen or should be changing high-level logic within PaintParams::toKey().
2667         SkASSERT(false);
2668         SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, SK_PMColor4fTRANSPARENT);
2669         return;
2670     }
2671     switch (as_SB(shader)->type()) {
2672 #define M(type)                                                        \
2673     case SkShaderBase::ShaderType::k##type:                            \
2674         add_to_key(keyContext,                                         \
2675                    builder,                                            \
2676                    gatherer,                                           \
2677                    static_cast<const Sk##type##Shader*>(shader)); \
2678         return;
2679         SK_ALL_SHADERS(M)
2680 #undef M
2681     }
2682     SkUNREACHABLE;
2683 }
2684 
NotifyImagesInUse(Recorder * recorder,DrawContext * drawContext,const SkShader * shader)2685 void NotifyImagesInUse(Recorder* recorder,
2686                        DrawContext* drawContext,
2687                        const SkShader* shader) {
2688     if (!shader) {
2689         return;
2690     }
2691     switch (as_SB(shader)->type()) {
2692 #define M(type)                                                      \
2693     case SkShaderBase::ShaderType::k##type:                          \
2694         notify_in_use(recorder,                                      \
2695                       drawContext,                                   \
2696                       static_cast<const Sk##type##Shader*>(shader)); \
2697         return;
2698         SK_ALL_SHADERS(M)
2699 #undef M
2700     }
2701     SkUNREACHABLE;
2702 }
2703 
2704 
2705 } // namespace skgpu::graphite
2706