• 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/SkGradientBaseShader.h"
79 #include "src/shaders/gradients/SkLinearGradient.h"
80 #include "src/shaders/gradients/SkRadialGradient.h"
81 #include "src/shaders/gradients/SkSweepGradient.h"
82 
83 using namespace skia_private;
84 
85 #define VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID) \
86     SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, dict->getUniforms(codeSnippetID));)
87 
88 namespace skgpu::graphite {
89 
90 //--------------------------------------------------------------------------------------------------
91 
92 namespace {
93 
add_solid_uniform_data(const ShaderCodeDictionary * dict,const SkPMColor4f & premulColor,PipelineDataGatherer * gatherer)94 void add_solid_uniform_data(const ShaderCodeDictionary* dict,
95                             const SkPMColor4f& premulColor,
96                             PipelineDataGatherer* gatherer) {
97     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kSolidColorShader)
98     gatherer->write(premulColor);
99 }
100 
101 } // anonymous namespace
102 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkPMColor4f & premulColor)103 void SolidColorShaderBlock::AddBlock(const KeyContext& keyContext,
104                                      PaintParamsKeyBuilder* builder,
105                                      PipelineDataGatherer* gatherer,
106                                      const SkPMColor4f& premulColor) {
107     add_solid_uniform_data(keyContext.dict(), premulColor, gatherer);
108 
109     builder->addBlock(BuiltInCodeSnippetID::kSolidColorShader);
110 }
111 
112 //--------------------------------------------------------------------------------------------------
113 
114 namespace {
115 
add_rgb_paint_color_uniform_data(const ShaderCodeDictionary * dict,const SkPMColor4f & premulColor,PipelineDataGatherer * gatherer)116 void add_rgb_paint_color_uniform_data(const ShaderCodeDictionary* dict,
117                                       const SkPMColor4f& premulColor,
118                                       PipelineDataGatherer* gatherer) {
119     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kRGBPaintColor)
120     gatherer->writePaintColor(premulColor);
121 }
122 
add_alpha_only_paint_color_uniform_data(const ShaderCodeDictionary * dict,const SkPMColor4f & premulColor,PipelineDataGatherer * gatherer)123 void add_alpha_only_paint_color_uniform_data(const ShaderCodeDictionary* dict,
124                                              const SkPMColor4f& premulColor,
125                                              PipelineDataGatherer* gatherer) {
126     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAlphaOnlyPaintColor)
127     gatherer->writePaintColor(premulColor);
128 }
129 
130 } // anonymous namespace
131 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)132 void RGBPaintColorBlock::AddBlock(const KeyContext& keyContext,
133                                   PaintParamsKeyBuilder* builder,
134                                   PipelineDataGatherer* gatherer) {
135     add_rgb_paint_color_uniform_data(keyContext.dict(), keyContext.paintColor(), gatherer);
136 
137     builder->addBlock(BuiltInCodeSnippetID::kRGBPaintColor);
138 }
139 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)140 void AlphaOnlyPaintColorBlock::AddBlock(const KeyContext& keyContext,
141                                         PaintParamsKeyBuilder* builder,
142                                         PipelineDataGatherer* gatherer) {
143     add_alpha_only_paint_color_uniform_data(keyContext.dict(), keyContext.paintColor(), gatherer);
144 
145     builder->addBlock(BuiltInCodeSnippetID::kAlphaOnlyPaintColor);
146 }
147 
148 //--------------------------------------------------------------------------------------------------
149 
150 namespace {
151 
add_dst_read_sample_uniform_data(const ShaderCodeDictionary * dict,PipelineDataGatherer * gatherer,sk_sp<TextureProxy> dstTexture,SkIPoint dstOffset)152 void add_dst_read_sample_uniform_data(const ShaderCodeDictionary* dict,
153                                       PipelineDataGatherer* gatherer,
154                                       sk_sp<TextureProxy> dstTexture,
155                                       SkIPoint dstOffset) {
156     static const SkTileMode kTileModes[2] = {SkTileMode::kClamp, SkTileMode::kClamp};
157     gatherer->add(SkSamplingOptions(), kTileModes, dstTexture);
158 
159     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kDstReadSample)
160 
161     SkV4 coords{static_cast<float>(dstOffset.x()),
162                 static_cast<float>(dstOffset.y()),
163                 dstTexture ? 1.0f / dstTexture->dimensions().width()  : 1.0f,
164                 dstTexture ? 1.0f / dstTexture->dimensions().height() : 1.0f };
165     gatherer->write(coords);
166 }
167 
168 } // anonymous namespace
169 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,sk_sp<TextureProxy> dstTexture,SkIPoint dstOffset)170 void DstReadSampleBlock::AddBlock(const KeyContext& keyContext,
171                                   PaintParamsKeyBuilder* builder,
172                                   PipelineDataGatherer* gatherer,
173                                   sk_sp<TextureProxy> dstTexture,
174                                   SkIPoint dstOffset) {
175     add_dst_read_sample_uniform_data(keyContext.dict(), gatherer, std::move(dstTexture), dstOffset);
176 
177     builder->addBlock(BuiltInCodeSnippetID::kDstReadSample);
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     VALIDATE_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     VALIDATE_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     VALIDATE_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     VALIDATE_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,PipelineDataGatherer * gatherer)314 static int write_color_and_offset_bufdata(int numStops,
315                                            const SkPMColor4f* colors,
316                                            const float* offsets,
317                                            PipelineDataGatherer* gatherer) {
318     auto [dstData, bufferOffset] = gatherer->allocateGradientData(numStops * 5);
319     for (int i = 0; i < numStops; i++) {
320         SkColor4f unpremulColor = colors[i].unpremul();
321 
322         float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops - 1);
323         SkASSERT(offset >= 0.0f && offset <= 1.0f);
324 
325         int dataIndex = i * 5;
326         dstData[dataIndex] = offset;
327         dstData[dataIndex + 1] = unpremulColor.fR;
328         dstData[dataIndex + 2] = unpremulColor.fG;
329         dstData[dataIndex + 3] = unpremulColor.fB;
330         dstData[dataIndex + 4] = unpremulColor.fA;
331     }
332 
333     return bufferOffset;
334 }
335 
GradientData(SkShaderBase::GradientType type,int numStops)336 GradientShaderBlocks::GradientData::GradientData(SkShaderBase::GradientType type, int numStops)
337         : fType(type)
338         , fPoints{{0.0f, 0.0f}, {0.0f, 0.0f}}
339         , fRadii{0.0f, 0.0f}
340         , fBias(0.0f)
341         , fScale(0.0f)
342         , fTM(SkTileMode::kClamp)
343         , fNumStops(numStops) {
344     sk_bzero(fColors, sizeof(fColors));
345     sk_bzero(fOffsets, sizeof(fOffsets));
346 }
347 
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,sk_sp<TextureProxy> colorsAndOffsetsProxy,bool useStorageBuffer,const SkGradientShader::Interpolation & interp)348 GradientShaderBlocks::GradientData::GradientData(SkShaderBase::GradientType type,
349                                                  SkPoint point0, SkPoint point1,
350                                                  float radius0, float radius1,
351                                                  float bias, float scale,
352                                                  SkTileMode tm,
353                                                  int numStops,
354                                                  const SkPMColor4f* colors,
355                                                  const float* offsets,
356                                                  sk_sp<TextureProxy> colorsAndOffsetsProxy,
357                                                  bool useStorageBuffer,
358                                                  const SkGradientShader::Interpolation& interp)
359         : fType(type)
360         , fBias(bias)
361         , fScale(scale)
362         , fTM(tm)
363         , fNumStops(numStops)
364         , fUseStorageBuffer(useStorageBuffer)
365         , fSrcColors(colors)
366         , fSrcOffsets(offsets)
367         , fInterpolation(interp) {
368     SkASSERT(fNumStops >= 1);
369 
370     fPoints[0] = point0;
371     fPoints[1] = point1;
372     fRadii[0] = radius0;
373     fRadii[1] = radius1;
374 
375     if (fNumStops <= kNumInternalStorageStops) {
376         memcpy(fColors, colors, fNumStops * sizeof(SkColor4f));
377         float* rawOffsets = fOffsets[0].ptr();
378         if (offsets) {
379             memcpy(rawOffsets, offsets, fNumStops * sizeof(float));
380         } else {
381             for (int i = 0; i < fNumStops; ++i) {
382                 rawOffsets[i] = SkIntToFloat(i) / (fNumStops-1);
383             }
384         }
385 
386         // Extend the colors and offset, if necessary, to fill out the arrays.
387         // The unrolled binary search implementation assumes excess stops match the last real value.
388         for (int i = fNumStops; i < kNumInternalStorageStops; ++i) {
389             fColors[i] = fColors[fNumStops-1];
390             rawOffsets[i] = rawOffsets[fNumStops-1];
391         }
392     } else {
393         if (!fUseStorageBuffer) {
394             fColorsAndOffsetsProxy = std::move(colorsAndOffsetsProxy);
395             SkASSERT(fColorsAndOffsetsProxy);
396         }
397     }
398 }
399 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const GradientData & gradData)400 void GradientShaderBlocks::AddBlock(const KeyContext& keyContext,
401                                     PaintParamsKeyBuilder* builder,
402                                     PipelineDataGatherer* gatherer,
403                                     const GradientData& gradData) {
404     auto dict = keyContext.dict();
405 
406     int bufferOffset = 0;
407     if (gradData.fNumStops > GradientData::kNumInternalStorageStops && gatherer) {
408         if (gradData.fUseStorageBuffer) {
409             bufferOffset = write_color_and_offset_bufdata(gradData.fNumStops,
410                                                           gradData.fSrcColors,
411                                                           gradData.fSrcOffsets,
412                                                           gatherer);
413         } else {
414             SkASSERT(gradData.fColorsAndOffsetsProxy);
415 
416             static constexpr SkTileMode kClampTiling[2] = {SkTileMode::kClamp, SkTileMode::kClamp};
417             gatherer->add(SkFilterMode::kNearest, kClampTiling, gradData.fColorsAndOffsetsProxy);
418         }
419     }
420 
421     BuiltInCodeSnippetID codeSnippetID = BuiltInCodeSnippetID::kSolidColorShader;
422     switch (gradData.fType) {
423         case SkShaderBase::GradientType::kLinear:
424             codeSnippetID =
425                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kLinearGradientShader4
426                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kLinearGradientShader8
427                         : gradData.fUseStorageBuffer
428                             ? BuiltInCodeSnippetID::kLinearGradientShaderBuffer
429                             : BuiltInCodeSnippetID::kLinearGradientShaderTexture;
430             add_linear_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
431             break;
432         case SkShaderBase::GradientType::kRadial:
433             codeSnippetID =
434                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kRadialGradientShader4
435                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kRadialGradientShader8
436                         : gradData.fUseStorageBuffer
437                             ? BuiltInCodeSnippetID::kRadialGradientShaderBuffer
438                             : BuiltInCodeSnippetID::kRadialGradientShaderTexture;
439             add_radial_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
440             break;
441         case SkShaderBase::GradientType::kSweep:
442             codeSnippetID =
443                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kSweepGradientShader4
444                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kSweepGradientShader8
445                         : gradData.fUseStorageBuffer
446                             ? BuiltInCodeSnippetID::kSweepGradientShaderBuffer
447                             : BuiltInCodeSnippetID::kSweepGradientShaderTexture;
448             add_sweep_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
449             break;
450         case SkShaderBase::GradientType::kConical:
451             codeSnippetID =
452                     gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kConicalGradientShader4
453                     : gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kConicalGradientShader8
454                         : gradData.fUseStorageBuffer
455                             ? BuiltInCodeSnippetID::kConicalGradientShaderBuffer
456                             : BuiltInCodeSnippetID::kConicalGradientShaderTexture;
457             add_conical_gradient_uniform_data(dict, codeSnippetID, gradData, bufferOffset, gatherer);
458             break;
459         case SkShaderBase::GradientType::kNone:
460         default:
461             SkDEBUGFAIL("Expected a gradient shader, but it wasn't one.");
462             break;
463     }
464 
465     builder->addBlock(codeSnippetID);
466 }
467 
468 //--------------------------------------------------------------------------------------------------
469 
470 namespace {
471 
add_localmatrixshader_uniform_data(const ShaderCodeDictionary * dict,const SkM44 & localMatrix,PipelineDataGatherer * gatherer)472 void add_localmatrixshader_uniform_data(const ShaderCodeDictionary* dict,
473                                         const SkM44& localMatrix,
474                                         PipelineDataGatherer* gatherer) {
475     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kLocalMatrixShader)
476 
477     SkM44 lmInverse;
478     bool wasInverted = localMatrix.invert(&lmInverse);  // TODO: handle failure up stack
479     if (!wasInverted) {
480         lmInverse.setIdentity();
481     }
482 
483     gatherer->write(lmInverse);
484 }
485 
486 } // anonymous namespace
487 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const LMShaderData & lmShaderData)488 void LocalMatrixShaderBlock::BeginBlock(const KeyContext& keyContext,
489                                         PaintParamsKeyBuilder* builder,
490                                         PipelineDataGatherer* gatherer,
491                                         const LMShaderData& lmShaderData) {
492 
493     add_localmatrixshader_uniform_data(keyContext.dict(), lmShaderData.fLocalMatrix, gatherer);
494 
495     builder->beginBlock(BuiltInCodeSnippetID::kLocalMatrixShader);
496 }
497 
498 //--------------------------------------------------------------------------------------------------
499 
500 namespace {
501 
502 static constexpr int kColorSpaceXformFlagAlphaSwizzle = 0x20;
503 
add_color_space_uniforms(const SkColorSpaceXformSteps & steps,ReadSwizzle readSwizzle,PipelineDataGatherer * gatherer)504 void add_color_space_uniforms(const SkColorSpaceXformSteps& steps,
505                               ReadSwizzle readSwizzle,
506                               PipelineDataGatherer* gatherer) {
507     // We have 7 source coefficients and 7 destination coefficients. We pass them via a 4x4 matrix;
508     // the first two columns hold the source values, and the second two hold the destination.
509     // (The final value of each 8-element group is ignored.)
510     // In std140, this arrangement is much more efficient than a simple array of scalars.
511     SkM44 coeffs;
512 
513     int colorXformFlags = SkTo<int>(steps.flags.mask());
514     if (readSwizzle != ReadSwizzle::kRGBA) {
515         // Ensure that we do the gamut step
516         SkColorSpaceXformSteps gamutSteps;
517         gamutSteps.flags.gamut_transform = true;
518         colorXformFlags |= SkTo<int>(gamutSteps.flags.mask());
519         if (readSwizzle != ReadSwizzle::kBGRA) {
520             // TODO: Maybe add a fullMask() method to XformSteps?
521             SkASSERT(colorXformFlags < kColorSpaceXformFlagAlphaSwizzle);
522             colorXformFlags |= kColorSpaceXformFlagAlphaSwizzle;
523         }
524     }
525     gatherer->write(colorXformFlags);
526 
527     if (steps.flags.linearize) {
528         gatherer->write(SkTo<int>(skcms_TransferFunction_getType(&steps.srcTF)));
529         coeffs.setCol(0, {steps.srcTF.g, steps.srcTF.a, steps.srcTF.b, steps.srcTF.c});
530         coeffs.setCol(1, {steps.srcTF.d, steps.srcTF.e, steps.srcTF.f, 0.0f});
531     } else {
532         gatherer->write(SkTo<int>(skcms_TFType::skcms_TFType_Invalid));
533     }
534 
535     SkMatrix gamutTransform;
536     const float identity[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
537     // TODO: it seems odd to copy this into an SkMatrix just to write it to the gatherer
538     // src_to_dst_matrix is column-major, SkMatrix is row-major.
539     const float* m = steps.flags.gamut_transform ? steps.src_to_dst_matrix : identity;
540     if (readSwizzle == ReadSwizzle::kRRR1) {
541         gamutTransform.setAll(m[0] + m[3] + m[6], 0, 0,
542                               m[1] + m[4] + m[7], 0, 0,
543                               m[2] + m[5] + m[8], 0, 0);
544     } else if (readSwizzle == ReadSwizzle::kBGRA) {
545         gamutTransform.setAll(m[6], m[3], m[0],
546                               m[7], m[4], m[1],
547                               m[8], m[5], m[2]);
548     } else if (readSwizzle == ReadSwizzle::k000R) {
549         gamutTransform.setAll(0, 0, 0,
550                               0, 0, 0,
551                               0, 0, 0);
552     } else if (steps.flags.gamut_transform) {
553         gamutTransform.setAll(m[0], m[3], m[6],
554                               m[1], m[4], m[7],
555                               m[2], m[5], m[8]);
556     }
557     gatherer->writeHalf(gamutTransform);
558 
559     if (steps.flags.encode) {
560         gatherer->write(SkTo<int>(skcms_TransferFunction_getType(&steps.dstTFInv)));
561         coeffs.setCol(2, {steps.dstTFInv.g, steps.dstTFInv.a, steps.dstTFInv.b, steps.dstTFInv.c});
562         coeffs.setCol(3, {steps.dstTFInv.d, steps.dstTFInv.e, steps.dstTFInv.f, 0.0f});
563     } else {
564         gatherer->write(SkTo<int>(skcms_TFType::skcms_TFType_Invalid));
565     }
566 
567     // Pack alpha swizzle in the unused coeff entries.
568     switch (readSwizzle) {
569         case ReadSwizzle::k000R:
570             coeffs.setRC(3, 1, 1.f);
571             coeffs.setRC(3, 3, 0.f);
572             break;
573         case ReadSwizzle::kRGB1:
574         case ReadSwizzle::kRRR1:
575             coeffs.setRC(3, 1, 0.f);
576             coeffs.setRC(3, 3, 1.f);
577             break;
578         default:
579             coeffs.setRC(3, 1, 0.f);
580             coeffs.setRC(3, 3, 0.f);
581             break;
582     }
583 
584     gatherer->writeHalf(coeffs);
585 }
586 
add_image_uniform_data(const ShaderCodeDictionary * dict,const ImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)587 void add_image_uniform_data(const ShaderCodeDictionary* dict,
588                             const ImageShaderBlock::ImageData& imgData,
589                             PipelineDataGatherer* gatherer) {
590     SkASSERT(!imgData.fSampling.useCubic);
591     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kImageShader)
592 
593     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
594     gatherer->write(imgData.fSubset);
595     gatherer->write(SkTo<int>(imgData.fTileModes[0]));
596     gatherer->write(SkTo<int>(imgData.fTileModes[1]));
597     gatherer->write(SkTo<int>(imgData.fSampling.filter));
598 
599     add_color_space_uniforms(imgData.fSteps, imgData.fReadSwizzle, gatherer);
600 }
601 
add_cubic_image_uniform_data(const ShaderCodeDictionary * dict,const ImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)602 void add_cubic_image_uniform_data(const ShaderCodeDictionary* dict,
603                                   const ImageShaderBlock::ImageData& imgData,
604                                   PipelineDataGatherer* gatherer) {
605     SkASSERT(imgData.fSampling.useCubic);
606     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCubicImageShader)
607 
608     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
609     gatherer->write(imgData.fSubset);
610     gatherer->write(SkTo<int>(imgData.fTileModes[0]));
611     gatherer->write(SkTo<int>(imgData.fTileModes[1]));
612     const SkCubicResampler& cubic = imgData.fSampling.cubic;
613     gatherer->writeHalf(SkImageShader::CubicResamplerMatrix(cubic.B, cubic.C));
614 
615     add_color_space_uniforms(imgData.fSteps, imgData.fReadSwizzle, gatherer);
616 }
617 
add_hw_image_uniform_data(const ShaderCodeDictionary * dict,const ImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)618 void add_hw_image_uniform_data(const ShaderCodeDictionary* dict,
619                                const ImageShaderBlock::ImageData& imgData,
620                                PipelineDataGatherer* gatherer) {
621     SkASSERT(!imgData.fSampling.useCubic);
622     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kHWImageShader)
623 
624     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
625 
626     add_color_space_uniforms(imgData.fSteps, imgData.fReadSwizzle, gatherer);
627 }
628 
629 } // anonymous namespace
630 
ImageData(const SkSamplingOptions & sampling,SkTileMode tileModeX,SkTileMode tileModeY,SkISize imgSize,SkRect subset,ReadSwizzle readSwizzle)631 ImageShaderBlock::ImageData::ImageData(const SkSamplingOptions& sampling,
632                                        SkTileMode tileModeX,
633                                        SkTileMode tileModeY,
634                                        SkISize imgSize,
635                                        SkRect subset,
636                                        ReadSwizzle readSwizzle)
637         : fSampling(sampling)
638         , fTileModes{tileModeX, tileModeY}
639         , fImgSize(imgSize)
640         , fSubset(subset)
641         , fReadSwizzle(readSwizzle) {
642     SkASSERT(fSteps.flags.mask() == 0);   // By default, the colorspace should have no effect
643 }
644 
can_do_tiling_in_hw(const Caps * caps,const ImageShaderBlock::ImageData & imgData)645 static bool can_do_tiling_in_hw(const Caps* caps, const ImageShaderBlock::ImageData& imgData) {
646     if (!caps->clampToBorderSupport() && (imgData.fTileModes[0] == SkTileMode::kDecal ||
647                                           imgData.fTileModes[1] == SkTileMode::kDecal)) {
648         return false;
649     }
650     return imgData.fSubset.contains(SkRect::Make(imgData.fImgSize));
651 }
652 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ImageData & imgData)653 void ImageShaderBlock::AddBlock(const KeyContext& keyContext,
654                                 PaintParamsKeyBuilder* builder,
655                                 PipelineDataGatherer* gatherer,
656                                 const ImageData& imgData) {
657 
658     if (keyContext.recorder() && !imgData.fTextureProxy) {
659         builder->addBlock(BuiltInCodeSnippetID::kError);
660         return;
661     }
662 
663     const Caps* caps = keyContext.caps();
664     const bool doTilingInHw = !imgData.fSampling.useCubic && can_do_tiling_in_hw(caps, imgData);
665 
666     static constexpr SkTileMode kDefaultTileModes[2] = {SkTileMode::kClamp, SkTileMode::kClamp};
667     gatherer->add(imgData.fSampling,
668                   doTilingInHw ? imgData.fTileModes : kDefaultTileModes,
669                   imgData.fTextureProxy);
670 
671     if (doTilingInHw) {
672         add_hw_image_uniform_data(keyContext.dict(), imgData, gatherer);
673         builder->addBlock(BuiltInCodeSnippetID::kHWImageShader);
674     } else if (imgData.fSampling.useCubic) {
675         add_cubic_image_uniform_data(keyContext.dict(), imgData, gatherer);
676         builder->addBlock(BuiltInCodeSnippetID::kCubicImageShader);
677     } else {
678         add_image_uniform_data(keyContext.dict(), imgData, gatherer);
679         builder->addBlock(BuiltInCodeSnippetID::kImageShader);
680     }
681 }
682 
683 //--------------------------------------------------------------------------------------------------
684 
685 // makes use of ImageShader functions, above
686 namespace {
687 
add_yuv_image_uniform_data(const ShaderCodeDictionary * dict,const YUVImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)688 void add_yuv_image_uniform_data(const ShaderCodeDictionary* dict,
689                                 const YUVImageShaderBlock::ImageData& imgData,
690                                 PipelineDataGatherer* gatherer) {
691     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kYUVImageShader)
692 
693     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
694     gatherer->write(SkSize::Make(1.f/imgData.fImgSizeUV.width(), 1.f/imgData.fImgSizeUV.height()));
695     gatherer->write(imgData.fSubset);
696     gatherer->write(imgData.fLinearFilterUVInset);
697     gatherer->write(SkTo<int>(imgData.fTileModes[0]));
698     gatherer->write(SkTo<int>(imgData.fTileModes[1]));
699     gatherer->write(SkTo<int>(imgData.fSampling.filter));
700     gatherer->write(SkTo<int>(imgData.fSamplingUV.filter));
701 
702     for (int i = 0; i < 4; ++i) {
703         gatherer->writeHalf(imgData.fChannelSelect[i]);
704     }
705     gatherer->writeHalf(imgData.fYUVtoRGBMatrix);
706     gatherer->write(imgData.fYUVtoRGBTranslate);
707 }
708 
add_cubic_yuv_image_uniform_data(const ShaderCodeDictionary * dict,const YUVImageShaderBlock::ImageData & imgData,PipelineDataGatherer * gatherer)709 void add_cubic_yuv_image_uniform_data(const ShaderCodeDictionary* dict,
710                                       const YUVImageShaderBlock::ImageData& imgData,
711                                       PipelineDataGatherer* gatherer) {
712     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCubicYUVImageShader)
713 
714     gatherer->write(SkSize::Make(1.f/imgData.fImgSize.width(), 1.f/imgData.fImgSize.height()));
715     gatherer->write(SkSize::Make(1.f/imgData.fImgSizeUV.width(), 1.f/imgData.fImgSizeUV.height()));
716     gatherer->write(imgData.fSubset);
717     gatherer->write(SkTo<int>(imgData.fTileModes[0]));
718     gatherer->write(SkTo<int>(imgData.fTileModes[1]));
719     const SkCubicResampler& cubic = imgData.fSampling.cubic;
720     gatherer->writeHalf(SkImageShader::CubicResamplerMatrix(cubic.B, cubic.C));
721 
722     for (int i = 0; i < 4; ++i) {
723         gatherer->writeHalf(imgData.fChannelSelect[i]);
724     }
725     gatherer->writeHalf(imgData.fYUVtoRGBMatrix);
726     gatherer->write(imgData.fYUVtoRGBTranslate);
727 }
728 
729 } // anonymous namespace
730 
ImageData(const SkSamplingOptions & sampling,SkTileMode tileModeX,SkTileMode tileModeY,SkISize imgSize,SkRect subset)731 YUVImageShaderBlock::ImageData::ImageData(const SkSamplingOptions& sampling,
732                                           SkTileMode tileModeX,
733                                           SkTileMode tileModeY,
734                                           SkISize imgSize,
735                                           SkRect subset)
736         : fSampling(sampling)
737         , fSamplingUV(sampling)
738         , fTileModes{tileModeX, tileModeY}
739         , fImgSize(imgSize)
740         , fImgSizeUV(imgSize)
741         , fSubset(subset) {
742 }
743 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ImageData & imgData)744 void YUVImageShaderBlock::AddBlock(const KeyContext& keyContext,
745                                    PaintParamsKeyBuilder* builder,
746                                    PipelineDataGatherer* gatherer,
747                                    const ImageData& imgData) {
748     if (keyContext.recorder() &&
749         (!imgData.fTextureProxies[0] || !imgData.fTextureProxies[1] ||
750          !imgData.fTextureProxies[2] || !imgData.fTextureProxies[3])) {
751         builder->addBlock(BuiltInCodeSnippetID::kError);
752         return;
753     }
754 
755     SkTileMode uvTileModes[2] = { imgData.fTileModes[0] == SkTileMode::kDecal
756                                           ? SkTileMode::kClamp : imgData.fTileModes[0],
757                                   imgData.fTileModes[1] == SkTileMode::kDecal
758                                           ? SkTileMode::kClamp : imgData.fTileModes[1] };
759     gatherer->add(imgData.fSampling, imgData.fTileModes, imgData.fTextureProxies[0]);
760     gatherer->add(imgData.fSamplingUV, uvTileModes, imgData.fTextureProxies[1]);
761     gatherer->add(imgData.fSamplingUV, uvTileModes, imgData.fTextureProxies[2]);
762     gatherer->add(imgData.fSampling, imgData.fTileModes, imgData.fTextureProxies[3]);
763 
764     if (imgData.fSampling.useCubic) {
765         add_cubic_yuv_image_uniform_data(keyContext.dict(), imgData, gatherer);
766         builder->addBlock(BuiltInCodeSnippetID::kCubicYUVImageShader);
767     } else {
768         add_yuv_image_uniform_data(keyContext.dict(), imgData, gatherer);
769         builder->addBlock(BuiltInCodeSnippetID::kYUVImageShader);
770     }
771 }
772 
773 //--------------------------------------------------------------------------------------------------
774 
775 namespace {
776 
add_coordclamp_uniform_data(const ShaderCodeDictionary * dict,const CoordClampShaderBlock::CoordClampData & clampData,PipelineDataGatherer * gatherer)777 void add_coordclamp_uniform_data(const ShaderCodeDictionary* dict,
778                                  const CoordClampShaderBlock::CoordClampData& clampData,
779                                  PipelineDataGatherer* gatherer) {
780     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCoordClampShader)
781 
782     gatherer->write(clampData.fSubset);
783 }
784 
785 } // anonymous namespace
786 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const CoordClampData & clampData)787 void CoordClampShaderBlock::BeginBlock(const KeyContext& keyContext,
788                                        PaintParamsKeyBuilder* builder,
789                                        PipelineDataGatherer* gatherer,
790                                        const CoordClampData& clampData) {
791     add_coordclamp_uniform_data(keyContext.dict(), clampData, gatherer);
792 
793     builder->beginBlock(BuiltInCodeSnippetID::kCoordClampShader);
794 }
795 
796 //--------------------------------------------------------------------------------------------------
797 
798 namespace {
799 
add_dither_uniform_data(const ShaderCodeDictionary * dict,const DitherShaderBlock::DitherData & ditherData,PipelineDataGatherer * gatherer)800 void add_dither_uniform_data(const ShaderCodeDictionary* dict,
801                              const DitherShaderBlock::DitherData& ditherData,
802                              PipelineDataGatherer* gatherer) {
803     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kDitherShader)
804 
805     gatherer->writeHalf(ditherData.fRange);
806 }
807 
808 } // anonymous namespace
809 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const DitherData & data)810 void DitherShaderBlock::AddBlock(const KeyContext& keyContext,
811                                  PaintParamsKeyBuilder* builder,
812                                  PipelineDataGatherer* gatherer,
813                                  const DitherData& data) {
814     add_dither_uniform_data(keyContext.dict(), data, gatherer);
815 
816     static constexpr SkSamplingOptions kNearest(SkFilterMode::kNearest, SkMipmapMode::kNone);
817     static constexpr SkTileMode kRepeatTiling[2] = { SkTileMode::kRepeat, SkTileMode::kRepeat };
818 
819     SkASSERT(data.fLUTProxy || !keyContext.recorder());
820     gatherer->add(kNearest, kRepeatTiling, data.fLUTProxy);
821 
822     builder->addBlock(BuiltInCodeSnippetID::kDitherShader);
823 }
824 
825 //--------------------------------------------------------------------------------------------------
826 
827 namespace {
828 
add_perlin_noise_uniform_data(const ShaderCodeDictionary * dict,const PerlinNoiseShaderBlock::PerlinNoiseData & noiseData,PipelineDataGatherer * gatherer)829 void add_perlin_noise_uniform_data(const ShaderCodeDictionary* dict,
830                                    const PerlinNoiseShaderBlock::PerlinNoiseData& noiseData,
831                                    PipelineDataGatherer* gatherer) {
832     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kPerlinNoiseShader)
833 
834     gatherer->write(noiseData.fBaseFrequency);
835     gatherer->write(noiseData.fStitchData);
836     gatherer->write(static_cast<int>(noiseData.fType));
837     gatherer->write(noiseData.fNumOctaves);
838     gatherer->write(static_cast<int>(noiseData.stitching()));
839 
840     static const SkTileMode kRepeatXTileModes[2] = { SkTileMode::kRepeat, SkTileMode::kClamp };
841     static const SkSamplingOptions kNearestSampling { SkFilterMode::kNearest };
842 
843     gatherer->add(kNearestSampling, kRepeatXTileModes, noiseData.fPermutationsProxy);
844     gatherer->add(kNearestSampling, kRepeatXTileModes, noiseData.fNoiseProxy);
845 }
846 
847 } // anonymous namespace
848 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const PerlinNoiseData & noiseData)849 void PerlinNoiseShaderBlock::AddBlock(const KeyContext& keyContext,
850                                       PaintParamsKeyBuilder* builder,
851                                       PipelineDataGatherer* gatherer,
852                                       const PerlinNoiseData& noiseData) {
853     add_perlin_noise_uniform_data(keyContext.dict(), noiseData, gatherer);
854 
855     builder->addBlock(BuiltInCodeSnippetID::kPerlinNoiseShader);
856 }
857 
858 //--------------------------------------------------------------------------------------------------
859 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)860 void BlendShaderBlock::BeginBlock(const KeyContext& keyContext,
861                                   PaintParamsKeyBuilder* builder,
862                                   PipelineDataGatherer* gatherer) {
863     VALIDATE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kBlendShader)
864 
865     builder->beginBlock(BuiltInCodeSnippetID::kBlendShader);
866 }
867 
868 //--------------------------------------------------------------------------------------------------
869 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode blendMode)870 void BlendModeBlenderBlock::AddBlock(const KeyContext& keyContext,
871                                      PaintParamsKeyBuilder* builder,
872                                      PipelineDataGatherer* gatherer,
873                                      SkBlendMode blendMode) {
874     VALIDATE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kBlendModeBlender)
875     gatherer->write(SkTo<int>(blendMode));
876 
877     builder->addBlock(BuiltInCodeSnippetID::kBlendModeBlender);
878 }
879 
880 //--------------------------------------------------------------------------------------------------
881 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkSpan<const float> coeffs)882 void CoeffBlenderBlock::AddBlock(const KeyContext& keyContext,
883                                  PaintParamsKeyBuilder* builder,
884                                  PipelineDataGatherer* gatherer,
885                                  SkSpan<const float> coeffs) {
886     VALIDATE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kCoeffBlender)
887     SkASSERT(coeffs.size() == 4);
888     gatherer->writeHalf(SkV4{coeffs[0], coeffs[1], coeffs[2], coeffs[3]});
889 
890     builder->addBlock(BuiltInCodeSnippetID::kCoeffBlender);
891 }
892 
893 //--------------------------------------------------------------------------------------------------
894 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)895 void ClipShaderBlock::BeginBlock(const KeyContext& keyContext,
896                                  PaintParamsKeyBuilder* builder,
897                                  PipelineDataGatherer* gatherer) {
898     VALIDATE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kClipShader)
899 
900     builder->beginBlock(BuiltInCodeSnippetID::kClipShader);
901 }
902 
903 //--------------------------------------------------------------------------------------------------
904 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer)905 void ComposeBlock::BeginBlock(const KeyContext& keyContext,
906                               PaintParamsKeyBuilder* builder,
907                               PipelineDataGatherer* gatherer) {
908     builder->beginBlock(BuiltInCodeSnippetID::kCompose);
909 }
910 
911 //--------------------------------------------------------------------------------------------------
912 
913 namespace {
914 
add_matrix_colorfilter_uniform_data(const ShaderCodeDictionary * dict,const MatrixColorFilterBlock::MatrixColorFilterData & data,PipelineDataGatherer * gatherer)915 void add_matrix_colorfilter_uniform_data(const ShaderCodeDictionary* dict,
916                                          const MatrixColorFilterBlock::MatrixColorFilterData& data,
917                                          PipelineDataGatherer* gatherer) {
918     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kMatrixColorFilter)
919     gatherer->write(data.fMatrix);
920     gatherer->write(data.fTranslate);
921     gatherer->write(static_cast<int>(data.fInHSLA));
922 }
923 
924 } // anonymous namespace
925 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const MatrixColorFilterData & matrixCFData)926 void MatrixColorFilterBlock::AddBlock(const KeyContext& keyContext,
927                                       PaintParamsKeyBuilder* builder,
928                                       PipelineDataGatherer* gatherer,
929                                       const MatrixColorFilterData& matrixCFData) {
930 
931     add_matrix_colorfilter_uniform_data(keyContext.dict(), matrixCFData, gatherer);
932 
933     builder->addBlock(BuiltInCodeSnippetID::kMatrixColorFilter);
934 }
935 
936 //--------------------------------------------------------------------------------------------------
937 
938 namespace {
939 
add_table_colorfilter_uniform_data(const ShaderCodeDictionary * dict,const TableColorFilterBlock::TableColorFilterData & data,PipelineDataGatherer * gatherer)940 void add_table_colorfilter_uniform_data(const ShaderCodeDictionary* dict,
941                                         const TableColorFilterBlock::TableColorFilterData& data,
942                                         PipelineDataGatherer* gatherer) {
943     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kTableColorFilter)
944 
945     static const SkTileMode kTileModes[2] = { SkTileMode::kClamp, SkTileMode::kClamp };
946     gatherer->add(SkSamplingOptions(), kTileModes, data.fTextureProxy);
947 }
948 
949 } // anonymous namespace
950 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const TableColorFilterData & data)951 void TableColorFilterBlock::AddBlock(const KeyContext& keyContext,
952                                      PaintParamsKeyBuilder* builder,
953                                      PipelineDataGatherer* gatherer,
954                                      const TableColorFilterData& data) {
955     SkASSERT(data.fTextureProxy || !keyContext.recorder());
956 
957     add_table_colorfilter_uniform_data(keyContext.dict(), data, gatherer);
958 
959     builder->addBlock(BuiltInCodeSnippetID::kTableColorFilter);
960 }
961 
962 //--------------------------------------------------------------------------------------------------
963 namespace {
964 
add_color_space_xform_uniform_data(const ShaderCodeDictionary * dict,const ColorSpaceTransformBlock::ColorSpaceTransformData & data,PipelineDataGatherer * gatherer)965 void add_color_space_xform_uniform_data(
966         const ShaderCodeDictionary* dict,
967         const ColorSpaceTransformBlock::ColorSpaceTransformData& data,
968         PipelineDataGatherer* gatherer) {
969 
970     VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kColorSpaceXformColorFilter)
971     add_color_space_uniforms(data.fSteps, ReadSwizzle::kRGBA, gatherer);
972 }
973 
974 }  // anonymous namespace
975 
ColorSpaceTransformData(const SkColorSpace * src,SkAlphaType srcAT,const SkColorSpace * dst,SkAlphaType dstAT)976 ColorSpaceTransformBlock::ColorSpaceTransformData::ColorSpaceTransformData(const SkColorSpace* src,
977                                                                            SkAlphaType srcAT,
978                                                                            const SkColorSpace* dst,
979                                                                            SkAlphaType dstAT)
980         : fSteps(src, srcAT, dst, dstAT) {}
981 
AddBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ColorSpaceTransformData & data)982 void ColorSpaceTransformBlock::AddBlock(const KeyContext& keyContext,
983                                         PaintParamsKeyBuilder* builder,
984                                         PipelineDataGatherer* gatherer,
985                                         const ColorSpaceTransformData& data) {
986     add_color_space_xform_uniform_data(keyContext.dict(), data, gatherer);
987     builder->addBlock(BuiltInCodeSnippetID::kColorSpaceXformColorFilter);
988 }
989 
990 //--------------------------------------------------------------------------------------------------
991 
AddBlendModeColorFilter(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm,const SkPMColor4f & srcColor)992 void AddBlendModeColorFilter(const KeyContext& keyContext,
993                              PaintParamsKeyBuilder* builder,
994                              PipelineDataGatherer* gatherer,
995                              SkBlendMode bm,
996                              const SkPMColor4f& srcColor) {
997     Blend(keyContext, builder, gatherer,
998           /* addBlendToKey= */ [&] () -> void {
999               // Note, we're playing a bit of a game here. By explicitly adding a
1000               // BlendModeBlenderBlock we're always forcing the SkSL to call 'sk_blend'
1001               // rather than allowing it to sometimes call 'blend_porter_duff'. This reduces
1002               // the number of shader combinations and allows the pre-compilation system to more
1003               // easily match the rendering path.
1004               BlendModeBlenderBlock::AddBlock(keyContext, builder, gatherer, bm);
1005           },
1006           /* addSrcToKey= */ [&]() -> void {
1007               SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, srcColor);
1008           },
1009           /* addDstToKey= */ [&]() -> void {
1010               builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1011           });
1012 }
1013 
ShaderData(sk_sp<const SkRuntimeEffect> effect)1014 RuntimeEffectBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect)
1015         : fEffect(std::move(effect)) {}
1016 
ShaderData(sk_sp<const SkRuntimeEffect> effect,sk_sp<const SkData> uniforms)1017 RuntimeEffectBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect,
1018                                            sk_sp<const SkData> uniforms)
1019         : fEffect(std::move(effect))
1020         , fUniforms(std::move(uniforms)) {}
1021 
skdata_matches(const SkData * a,const SkData * b)1022 static bool skdata_matches(const SkData* a, const SkData* b) {
1023     // Returns true if both SkData objects hold the same contents, or if they are both null.
1024     // (SkData::equals supports passing null, and returns false.)
1025     return a ? a->equals(b) : (a == b);
1026 }
1027 
operator ==(const ShaderData & rhs) const1028 bool RuntimeEffectBlock::ShaderData::operator==(const ShaderData& rhs) const {
1029     return fEffect == rhs.fEffect && skdata_matches(fUniforms.get(), rhs.fUniforms.get());
1030 }
1031 
gather_runtime_effect_uniforms(const KeyContext & keyContext,const SkRuntimeEffect * effect,SkSpan<const Uniform> graphiteUniforms,const SkData * uniformData,PipelineDataGatherer * gatherer)1032 static void gather_runtime_effect_uniforms(const KeyContext& keyContext,
1033                                            const SkRuntimeEffect* effect,
1034                                            SkSpan<const Uniform> graphiteUniforms,
1035                                            const SkData* uniformData,
1036                                            PipelineDataGatherer* gatherer) {
1037     if (!uniformData) {
1038         return;  // precompiling
1039     }
1040 
1041     SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, graphiteUniforms);)
1042 
1043     SkSpan<const SkRuntimeEffect::Uniform> rtsUniforms = effect->uniforms();
1044 
1045     if (!rtsUniforms.empty() && uniformData) {
1046         // Collect all the other uniforms from the provided SkData.
1047         const uint8_t* uniformBase = uniformData->bytes();
1048         for (size_t index = 0; index < rtsUniforms.size(); ++index) {
1049             const Uniform& uniform = graphiteUniforms[index];
1050             // Get a pointer to the offset in our data for this uniform.
1051             const uint8_t* uniformPtr = uniformBase + rtsUniforms[index].offset;
1052             // Pass the uniform data to the gatherer.
1053             gatherer->write(uniform, uniformPtr);
1054         }
1055     }
1056 
1057     if (SkRuntimeEffectPriv::UsesColorTransform(effect)) {
1058         SkColorSpace* dstCS = keyContext.dstColorInfo().colorSpace();
1059         if (!dstCS) {
1060             dstCS = sk_srgb_linear_singleton(); // turn colorspace conversion into a noop
1061         }
1062 
1063         // TODO(b/332565302): If the runtime shader only uses one of these
1064         // transforms, we could upload only one set of uniforms.
1065         ColorSpaceTransformBlock::ColorSpaceTransformData dstToLinear(dstCS,
1066                                                                       kUnpremul_SkAlphaType,
1067                                                                       sk_srgb_linear_singleton(),
1068                                                                       kUnpremul_SkAlphaType);
1069         ColorSpaceTransformBlock::ColorSpaceTransformData linearToDst(sk_srgb_linear_singleton(),
1070                                                                       kUnpremul_SkAlphaType,
1071                                                                       dstCS,
1072                                                                       kUnpremul_SkAlphaType);
1073 
1074         add_color_space_uniforms(dstToLinear.fSteps, ReadSwizzle::kRGBA, gatherer);
1075         add_color_space_uniforms(linearToDst.fSteps, ReadSwizzle::kRGBA, gatherer);
1076     }
1077 }
1078 
BeginBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const ShaderData & shaderData)1079 void RuntimeEffectBlock::BeginBlock(const KeyContext& keyContext,
1080                                     PaintParamsKeyBuilder* builder,
1081                                     PipelineDataGatherer* gatherer,
1082                                     const ShaderData& shaderData) {
1083     ShaderCodeDictionary* dict = keyContext.dict();
1084     int codeSnippetID = dict->findOrCreateRuntimeEffectSnippet(shaderData.fEffect.get());
1085 
1086     if (codeSnippetID >= SkKnownRuntimeEffects::kUnknownRuntimeEffectIDStart) {
1087         keyContext.rtEffectDict()->set(codeSnippetID, shaderData.fEffect);
1088     }
1089 
1090     const ShaderSnippet* entry = dict->getEntry(codeSnippetID);
1091     SkASSERT(entry);
1092 
1093     gather_runtime_effect_uniforms(keyContext,
1094                                    shaderData.fEffect.get(),
1095                                    entry->fUniforms,
1096                                    shaderData.fUniforms.get(),
1097                                    gatherer);
1098 
1099     builder->beginBlock(codeSnippetID);
1100 }
1101 
1102 // ==================================================================
1103 
1104 namespace {
1105 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlendModeBlender * blender)1106 void add_to_key(const KeyContext& keyContext,
1107                 PaintParamsKeyBuilder* builder,
1108                 PipelineDataGatherer* gatherer,
1109                 const SkBlendModeBlender* blender) {
1110     SkASSERT(blender);
1111 
1112     AddModeBlend(keyContext, builder, gatherer, blender->mode());
1113 }
1114 
1115 // Be sure to keep this function in sync w/ its correlate in FactoryFunctions.cpp
add_children_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkSpan<const SkRuntimeEffect::ChildPtr> children,SkSpan<const SkRuntimeEffect::Child> childInfo)1116 void add_children_to_key(const KeyContext& keyContext,
1117                          PaintParamsKeyBuilder* builder,
1118                          PipelineDataGatherer* gatherer,
1119                          SkSpan<const SkRuntimeEffect::ChildPtr> children,
1120                          SkSpan<const SkRuntimeEffect::Child> childInfo) {
1121     SkASSERT(children.size() == childInfo.size());
1122 
1123     using ChildType = SkRuntimeEffect::ChildType;
1124 
1125     KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
1126 
1127     for (size_t index = 0; index < children.size(); ++index) {
1128         const SkRuntimeEffect::ChildPtr& child = children[index];
1129         std::optional<ChildType> type = child.type();
1130         if (type == ChildType::kShader) {
1131             AddToKey(childContext, builder, gatherer, child.shader());
1132         } else if (type == ChildType::kColorFilter) {
1133             AddToKey(childContext, builder, gatherer, child.colorFilter());
1134         } else if (type == ChildType::kBlender) {
1135             AddToKey(childContext, builder, gatherer, child.blender());
1136         } else {
1137             // We don't have a child effect. Substitute in a no-op effect.
1138             switch (childInfo[index].type) {
1139                 case ChildType::kShader:
1140                     // A missing shader returns transparent black
1141                     SolidColorShaderBlock::AddBlock(childContext, builder, gatherer,
1142                                                     SK_PMColor4fTRANSPARENT);
1143                     break;
1144 
1145                 case ChildType::kColorFilter:
1146                     // A "passthrough" color filter returns the input color as-is.
1147                     builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1148                     break;
1149 
1150                 case ChildType::kBlender:
1151                     // A "passthrough" blender performs `blend_src_over(src, dest)`.
1152                     AddKnownModeBlend(childContext, builder, gatherer, SkBlendMode::kSrcOver);
1153                     break;
1154             }
1155         }
1156     }
1157 }
1158 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRuntimeBlender * blender)1159 void add_to_key(const KeyContext& keyContext,
1160                 PaintParamsKeyBuilder* builder,
1161                 PipelineDataGatherer* gatherer,
1162                 const SkRuntimeBlender* blender) {
1163     SkASSERT(blender);
1164     sk_sp<SkRuntimeEffect> effect = blender->effect();
1165     SkASSERT(effect);
1166     sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
1167             effect->uniforms(),
1168             blender->uniforms(),
1169             keyContext.dstColorInfo().colorSpace());
1170     SkASSERT(uniforms);
1171 
1172     RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
1173                                    { effect, std::move(uniforms) });
1174 
1175     add_children_to_key(keyContext, builder, gatherer,
1176                         blender->children(), effect->children());
1177 
1178     builder->endBlock();
1179 }
1180 
notify_in_use(Recorder * recorder,DrawContext * drawContext,SkSpan<const SkRuntimeEffect::ChildPtr> children)1181 void notify_in_use(Recorder* recorder,
1182                    DrawContext* drawContext,
1183                    SkSpan<const SkRuntimeEffect::ChildPtr> children) {
1184     for (const auto& child : children) {
1185         if (child.type().has_value()) {
1186             switch (*child.type()) {
1187                 case SkRuntimeEffect::ChildType::kShader:
1188                     NotifyImagesInUse(recorder, drawContext, child.shader());
1189                     break;
1190                 case SkRuntimeEffect::ChildType::kColorFilter:
1191                     NotifyImagesInUse(recorder, drawContext, child.colorFilter());
1192                     break;
1193                 case SkRuntimeEffect::ChildType::kBlender:
1194                     NotifyImagesInUse(recorder, drawContext, child.blender());
1195                     break;
1196             }
1197         } // else a null child is a no-op, so cannot sample an image
1198     }
1199 }
1200 
1201 } // anonymous namespace
1202 
AddToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlender * blender)1203 void AddToKey(const KeyContext& keyContext,
1204               PaintParamsKeyBuilder* builder,
1205               PipelineDataGatherer* gatherer,
1206               const SkBlender* blender) {
1207     if (!blender) {
1208         return;
1209     }
1210     switch (as_BB(blender)->type()) {
1211 #define M(type)                                                     \
1212     case SkBlenderBase::BlenderType::k##type:                       \
1213         add_to_key(keyContext,                                      \
1214                    builder,                                         \
1215                    gatherer,                                        \
1216                    static_cast<const Sk##type##Blender*>(blender)); \
1217         return;
1218         SK_ALL_BLENDERS(M)
1219 #undef M
1220     }
1221     SkUNREACHABLE;
1222 }
1223 
NotifyImagesInUse(Recorder * recorder,DrawContext * drawContext,const SkBlender * blender)1224 void NotifyImagesInUse(Recorder* recorder, DrawContext* drawContext, const SkBlender* blender) {
1225     if (!blender) {
1226         return;
1227     }
1228     if (as_BB(blender)->type() == SkBlenderBase::BlenderType::kRuntime) {
1229         const auto* rbb = static_cast<const SkRuntimeBlender*>(blender);
1230         notify_in_use(recorder, drawContext, rbb->children());
1231     } // else blend mode doesn't reference images
1232 }
1233 
1234 //--------------------------------------------------------------------------------------------------
1235 //--------------------------------------------------------------------------------------------------
map_color(const SkColor4f & c,SkColorSpace * src,SkColorSpace * dst)1236 static SkPMColor4f map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
1237     SkPMColor4f color = {c.fR, c.fG, c.fB, c.fA};
1238     SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, dst, kPremul_SkAlphaType).apply(color.vec());
1239     return color;
1240 }
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlendModeColorFilter * filter)1241 static void add_to_key(const KeyContext& keyContext,
1242                        PaintParamsKeyBuilder* builder,
1243                        PipelineDataGatherer* gatherer,
1244                        const SkBlendModeColorFilter* filter) {
1245     SkASSERT(filter);
1246 
1247     SkPMColor4f color = map_color(filter->color(), sk_srgb_singleton(),
1248                                   keyContext.dstColorInfo().colorSpace());
1249 
1250     AddBlendModeColorFilter(keyContext, builder, gatherer, filter->mode(), color);
1251 }
1252 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorSpaceXformColorFilter * filter)1253 static void add_to_key(const KeyContext& keyContext,
1254                        PaintParamsKeyBuilder* builder,
1255                        PipelineDataGatherer* gatherer,
1256                        const SkColorSpaceXformColorFilter* filter) {
1257     SkASSERT(filter);
1258 
1259     constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType;
1260     ColorSpaceTransformBlock::ColorSpaceTransformData csData(filter->src().get(), kAlphaType,
1261                                                              filter->dst().get(), kAlphaType);
1262     ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData);
1263 }
1264 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,const SkComposeColorFilter * filter)1265 static void add_to_key(const KeyContext& keyContext,
1266                        PaintParamsKeyBuilder* keyBuilder,
1267                        PipelineDataGatherer* gatherer,
1268                        const SkComposeColorFilter* filter) {
1269     SkASSERT(filter);
1270 
1271     Compose(keyContext, keyBuilder, gatherer,
1272             /* addInnerToKey= */ [&]() -> void {
1273                 AddToKey(keyContext, keyBuilder, gatherer, filter->inner().get());
1274             },
1275             /* addOuterToKey= */ [&]() -> void {
1276                 AddToKey(keyContext, keyBuilder, gatherer, filter->outer().get());
1277             });
1278 }
1279 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkGaussianColorFilter *)1280 static void add_to_key(const KeyContext& keyContext,
1281                        PaintParamsKeyBuilder* builder,
1282                        PipelineDataGatherer* gatherer,
1283                        const SkGaussianColorFilter*) {
1284     builder->addBlock(BuiltInCodeSnippetID::kGaussianColorFilter);
1285 }
1286 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkMatrixColorFilter * filter)1287 static void add_to_key(const KeyContext& keyContext,
1288                        PaintParamsKeyBuilder* builder,
1289                        PipelineDataGatherer* gatherer,
1290                        const SkMatrixColorFilter* filter) {
1291     SkASSERT(filter);
1292 
1293     bool inHSLA = filter->domain() == SkMatrixColorFilter::Domain::kHSLA;
1294     MatrixColorFilterBlock::MatrixColorFilterData matrixCFData(filter->matrix(), inHSLA);
1295 
1296     MatrixColorFilterBlock::AddBlock(keyContext, builder, gatherer, matrixCFData);
1297 }
1298 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRuntimeColorFilter * filter)1299 static void add_to_key(const KeyContext& keyContext,
1300                        PaintParamsKeyBuilder* builder,
1301                        PipelineDataGatherer* gatherer,
1302                        const SkRuntimeColorFilter* filter) {
1303     SkASSERT(filter);
1304 
1305     sk_sp<SkRuntimeEffect> effect = filter->effect();
1306     sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
1307             effect->uniforms(), filter->uniforms(), keyContext.dstColorInfo().colorSpace());
1308     SkASSERT(uniforms);
1309 
1310     RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, {effect, std::move(uniforms)});
1311 
1312     add_children_to_key(keyContext, builder, gatherer,
1313                         filter->children(), effect->children());
1314 
1315     builder->endBlock();
1316 }
1317 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkTableColorFilter * filter)1318 static void add_to_key(const KeyContext& keyContext,
1319                        PaintParamsKeyBuilder* builder,
1320                        PipelineDataGatherer* gatherer,
1321                        const SkTableColorFilter* filter) {
1322     SkASSERT(filter);
1323 
1324     sk_sp<TextureProxy> proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(),
1325                                                                 filter->bitmap(),
1326                                                                 "TableColorFilterTexture");
1327     if (!proxy) {
1328         SKGPU_LOG_W("Couldn't create TableColorFilter's table");
1329 
1330         // Return the input color as-is.
1331         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1332         return;
1333     }
1334 
1335     TableColorFilterBlock::TableColorFilterData data(std::move(proxy));
1336 
1337     TableColorFilterBlock::AddBlock(keyContext, builder, gatherer, data);
1338 }
1339 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkWorkingFormatColorFilter * filter)1340 static void add_to_key(const KeyContext& keyContext,
1341                        PaintParamsKeyBuilder* builder,
1342                        PipelineDataGatherer* gatherer,
1343                        const SkWorkingFormatColorFilter* filter) {
1344     SkASSERT(filter);
1345 
1346     const SkColorInfo& dstInfo = keyContext.dstColorInfo();
1347     const SkAlphaType dstAT = dstInfo.alphaType();
1348     sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace();
1349     if (!dstCS) {
1350         dstCS = SkColorSpace::MakeSRGB();
1351     }
1352 
1353     SkAlphaType workingAT;
1354     sk_sp<SkColorSpace> workingCS = filter->workingFormat(dstCS, &workingAT);
1355     SkColorInfo workingInfo(dstInfo.colorType(), workingAT, workingCS);
1356     KeyContextWithColorInfo workingContext(keyContext, workingInfo);
1357 
1358     // Use two nested compose blocks to chain (dst->working), child, and (working->dst) together
1359     // while appearing as one block to the parent node.
1360     Compose(keyContext, builder, gatherer,
1361             /* addInnerToKey= */ [&]() -> void {
1362                 // Inner compose
1363                 Compose(keyContext, builder, gatherer,
1364                         /* addInnerToKey= */ [&]() -> void {
1365                             // Innermost (inner of inner compose)
1366                             ColorSpaceTransformBlock::ColorSpaceTransformData data1(
1367                                     dstCS.get(), dstAT, workingCS.get(), workingAT);
1368                             ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer,
1369                                                                data1);
1370                         },
1371                         /* addOuterToKey= */ [&]() -> void {
1372                             // Middle (outer of inner compose)
1373                             AddToKey(workingContext, builder, gatherer, filter->child().get());
1374                         });
1375             },
1376             /* addOuterToKey= */ [&]() -> void {
1377                 // Outermost (outer of outer compose)
1378                 ColorSpaceTransformBlock::ColorSpaceTransformData data2(
1379                         workingCS.get(), workingAT, dstCS.get(), dstAT);
1380                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data2);
1381             });
1382 }
1383 
AddToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorFilter * filter)1384 void AddToKey(const KeyContext& keyContext,
1385               PaintParamsKeyBuilder* builder,
1386               PipelineDataGatherer* gatherer,
1387               const SkColorFilter* filter) {
1388     if (!filter) {
1389         return;
1390     }
1391     switch (as_CFB(filter)->type()) {
1392     case SkColorFilterBase::Type::kNoop:
1393         // Return the input color as-is.
1394         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1395         return;
1396 #define M(type)                                                        \
1397     case SkColorFilterBase::Type::k##type:                             \
1398         add_to_key(keyContext,                                         \
1399                    builder,                                            \
1400                    gatherer,                                           \
1401                    static_cast<const Sk##type##ColorFilter*>(filter)); \
1402         return;
1403         SK_ALL_COLOR_FILTERS(M)
1404 #undef M
1405     }
1406     SkUNREACHABLE;
1407 }
1408 
NotifyImagesInUse(Recorder * recorder,DrawContext * drawContext,const SkColorFilter * filter)1409 void NotifyImagesInUse(Recorder* recorder, DrawContext* drawContext, const SkColorFilter* filter) {
1410     if (!filter) {
1411         return;
1412     }
1413     if (as_CFB(filter)->type() == SkColorFilterBase::Type::kCompose) {
1414         // Recurse to two children
1415         const auto* cf = static_cast<const SkComposeColorFilter*>(filter);
1416         NotifyImagesInUse(recorder, drawContext, cf->inner().get());
1417         NotifyImagesInUse(recorder, drawContext, cf->outer().get());
1418     } else if (as_CFB(filter)->type() == SkColorFilterBase::Type::kWorkingFormat) {
1419         // Recurse to one child
1420         const auto* wfcf = static_cast<const SkWorkingFormatColorFilter*>(filter);
1421         NotifyImagesInUse(recorder, drawContext, wfcf->child().get());
1422     } else if (as_CFB(filter)->type() == SkColorFilterBase::Type::kRuntime) {
1423         // Recurse to all children
1424         const auto* rcf = static_cast<const SkRuntimeColorFilter*>(filter);
1425         notify_in_use(recorder, drawContext, rcf->children());
1426     } // else other color filters do not rely on SkImages
1427 }
1428 
1429 // ==================================================================
1430 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkBlendShader * shader)1431 static void add_to_key(const KeyContext& keyContext,
1432                        PaintParamsKeyBuilder* builder,
1433                        PipelineDataGatherer* gatherer,
1434                        const SkBlendShader* shader) {
1435     SkASSERT(shader);
1436 
1437     Blend(keyContext, builder, gatherer,
1438             /* addBlendToKey= */ [&] () -> void {
1439                 AddModeBlend(keyContext, builder, gatherer, shader->mode());
1440             },
1441             /* addSrcToKey= */ [&]() -> void {
1442                 AddToKey(keyContext, builder, gatherer, shader->src().get());
1443             },
1444             /* addDstToKey= */ [&]() -> void {
1445                 AddToKey(keyContext, builder, gatherer, shader->dst().get());
1446             });
1447 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkBlendShader * shader)1448 static void notify_in_use(Recorder* recorder,
1449                           DrawContext* drawContext,
1450                           const SkBlendShader* shader) {
1451     // SkBlendShader uses a fixed blend mode, so there's no blender to recurse through
1452     NotifyImagesInUse(recorder, drawContext, shader->src().get());
1453     NotifyImagesInUse(recorder, drawContext, shader->dst().get());
1454 }
1455 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkCTMShader * shader)1456 static void add_to_key(const KeyContext& keyContext,
1457                        PaintParamsKeyBuilder* builder,
1458                        PipelineDataGatherer* gatherer,
1459                        const SkCTMShader* shader) {
1460     // CTM shaders are always given device coordinates, so we don't have to modify the CTM itself
1461     // with keyContext's local transform.
1462     LocalMatrixShaderBlock::LMShaderData lmShaderData(shader->ctm());
1463 
1464     KeyContextWithLocalMatrix newContext(keyContext, shader->ctm());
1465 
1466     LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
1467 
1468         AddToKey(newContext, builder, gatherer, shader->proxyShader().get());
1469 
1470     builder->endBlock();
1471 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkCTMShader * shader)1472 static void notify_in_use(Recorder* recorder, DrawContext* drawContext, const SkCTMShader* shader) {
1473     NotifyImagesInUse(recorder, drawContext, shader->proxyShader().get());
1474 }
1475 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorShader * shader)1476 static void add_to_key(const KeyContext& keyContext,
1477                        PaintParamsKeyBuilder* builder,
1478                        PipelineDataGatherer* gatherer,
1479                        const SkColorShader* shader) {
1480     SkASSERT(shader);
1481 
1482     SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer,
1483                                     SkColor4f::FromColor(shader->color()).premul());
1484 }
notify_in_use(Recorder *,DrawContext *,const SkColorShader *)1485 static void notify_in_use(Recorder*, DrawContext*, const SkColorShader*) {
1486     // No-op
1487 }
1488 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColor4Shader * shader)1489 static void add_to_key(const KeyContext& keyContext,
1490                        PaintParamsKeyBuilder* builder,
1491                        PipelineDataGatherer* gatherer,
1492                        const SkColor4Shader* shader) {
1493     SkASSERT(shader);
1494 
1495     SkPMColor4f color = map_color(shader->color(), shader->colorSpace().get(),
1496                                   keyContext.dstColorInfo().colorSpace());
1497 
1498     SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, color);
1499 }
notify_in_use(Recorder *,DrawContext *,const SkColor4Shader *)1500 static void notify_in_use(Recorder*, DrawContext*, const SkColor4Shader*) {
1501     // No-op
1502 }
1503 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkColorFilterShader * shader)1504 static void add_to_key(const KeyContext& keyContext,
1505                        PaintParamsKeyBuilder* builder,
1506                        PipelineDataGatherer* gatherer,
1507                        const SkColorFilterShader* shader) {
1508     SkASSERT(shader);
1509 
1510     Compose(keyContext, builder, gatherer,
1511             /* addInnerToKey= */ [&]() -> void {
1512                 AddToKey(keyContext, builder, gatherer, shader->shader().get());
1513             },
1514             /* addOuterToKey= */ [&]() -> void {
1515                 AddToKey(keyContext, builder, gatherer, shader->filter().get());
1516             });
1517 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkColorFilterShader * shader)1518 static void notify_in_use(Recorder* recorder,
1519                           DrawContext* drawContext,
1520                           const SkColorFilterShader* shader) {
1521     NotifyImagesInUse(recorder, drawContext, shader->shader().get());
1522     NotifyImagesInUse(recorder, drawContext, shader->filter().get());
1523 }
1524 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkCoordClampShader * shader)1525 static void add_to_key(const KeyContext& keyContext,
1526                        PaintParamsKeyBuilder* builder,
1527                        PipelineDataGatherer* gatherer,
1528                        const SkCoordClampShader* shader) {
1529     SkASSERT(shader);
1530 
1531     CoordClampShaderBlock::CoordClampData data(shader->subset());
1532 
1533     KeyContextWithCoordClamp childContext(keyContext);
1534     CoordClampShaderBlock::BeginBlock(keyContext, builder, gatherer, data);
1535     AddToKey(childContext, builder, gatherer, shader->shader().get());
1536     builder->endBlock();
1537 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkCoordClampShader * shader)1538 static void notify_in_use(Recorder* recorder,
1539                           DrawContext* drawContext,
1540                           const SkCoordClampShader* shader) {
1541     NotifyImagesInUse(recorder, drawContext, shader->shader().get());
1542 }
1543 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkEmptyShader *)1544 static void add_to_key(const KeyContext& keyContext,
1545                        PaintParamsKeyBuilder* builder,
1546                        PipelineDataGatherer* gatherer,
1547                        const SkEmptyShader*) {
1548     builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
1549 }
notify_in_use(Recorder *,DrawContext *,const SkEmptyShader *)1550 static void notify_in_use(Recorder*, DrawContext*, const SkEmptyShader*) {
1551     // No-op
1552 }
1553 
add_yuv_image_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkImageShader * origShader,sk_sp<const SkImage> imageToDraw,SkSamplingOptions sampling)1554 static void add_yuv_image_to_key(const KeyContext& keyContext,
1555                                  PaintParamsKeyBuilder* builder,
1556                                  PipelineDataGatherer* gatherer,
1557                                  const SkImageShader* origShader,
1558                                  sk_sp<const SkImage> imageToDraw,
1559                                  SkSamplingOptions sampling) {
1560     SkASSERT(!imageToDraw->isAlphaOnly());
1561 
1562     const Image_YUVA* yuvaImage = static_cast<const Image_YUVA*>(imageToDraw.get());
1563     const SkYUVAInfo& yuvaInfo = yuvaImage->yuvaInfo();
1564     // We would want to add a translation to the local matrix to handle other sitings.
1565     SkASSERT(yuvaInfo.sitingX() == SkYUVAInfo::Siting::kCentered);
1566     SkASSERT(yuvaInfo.sitingY() == SkYUVAInfo::Siting::kCentered);
1567     YUVImageShaderBlock::ImageData imgData(sampling,
1568                                            origShader->tileModeX(),
1569                                            origShader->tileModeY(),
1570                                            imageToDraw->dimensions(),
1571                                            origShader->subset());
1572     for (int locIndex = 0; locIndex < SkYUVAInfo::kYUVAChannelCount; ++locIndex) {
1573         const TextureProxyView& view = yuvaImage->proxyView(locIndex);
1574         if (view) {
1575             imgData.fTextureProxies[locIndex] = view.refProxy();
1576             // The view's swizzle has the data channel for the YUVA location in all slots, so read
1577             // the 0th slot to determine fChannelSelect
1578             switch(view.swizzle()[0]) {
1579                 case 'r': imgData.fChannelSelect[locIndex] = {1.f, 0.f, 0.f, 0.f}; break;
1580                 case 'g': imgData.fChannelSelect[locIndex] = {0.f, 1.f, 0.f, 0.f}; break;
1581                 case 'b': imgData.fChannelSelect[locIndex] = {0.f, 0.f, 1.f, 0.f}; break;
1582                 case 'a': imgData.fChannelSelect[locIndex] = {0.f, 0.f, 0.f, 1.f}; break;
1583                 default:
1584                     imgData.fChannelSelect[locIndex] = {0.f, 0.f, 0.f, 0.f};
1585                     SkDEBUGFAILF("Unexpected swizzle for YUVA data: %c", view.swizzle()[0]);
1586                     break;
1587             }
1588         } else {
1589             // Only the A proxy view should be null, in which case we bind the Y proxy view to
1590             // pass validation and send all 1s for the channel selection to signal opaque alpha.
1591             SkASSERT(locIndex == 3);
1592             imgData.fTextureProxies[locIndex] = yuvaImage->proxyView(SkYUVAInfo::kY).refProxy();
1593             imgData.fChannelSelect[locIndex] = {1.f, 1.f, 1.f, 1.f};
1594         }
1595     }
1596 
1597     auto [ssx, ssy] = yuvaImage->uvSubsampleFactors();
1598     if (ssx > 1 || ssy > 1) {
1599         // We need to adjust the image size we use for sampling to reflect the actual image size of
1600         // the UV planes. However, since our coordinates are in Y's texel space we need to scale
1601         // accordingly.
1602         const TextureProxyView& view = yuvaImage->proxyView(SkYUVAInfo::kU);
1603         imgData.fImgSizeUV = {view.dimensions().width()*ssx, view.dimensions().height()*ssy};
1604         // This promotion of nearest to linear filtering for UV planes exists to mimic
1605         // libjpeg[-turbo]'s do_fancy_upsampling option. We will filter the subsampled plane,
1606         // however we want to filter at a fixed point for each logical image pixel to simulate
1607         // nearest neighbor. In the shader we detect that the UV filtermode doesn't match the Y
1608         // filtermode, and snap to Y pixel centers.
1609         if (imgData.fSampling.filter == SkFilterMode::kNearest) {
1610             imgData.fSamplingUV = SkSamplingOptions(SkFilterMode::kLinear,
1611                                                     imgData.fSampling.mipmap);
1612             // Consider a logical image pixel at the edge of the subset. When computing the logical
1613             // pixel color value we should use a blend of two values from the subsampled plane.
1614             // Depending on where the subset edge falls in actual subsampled plane, one of those
1615             // values may come from outside the subset. Hence, we will use the default inset
1616             // in Y texel space of 1/2. This applies the wrap mode to the subset but allows
1617             // linear filtering to read pixels that are just outside the subset.
1618             imgData.fLinearFilterUVInset.fX = 0.5f;
1619             imgData.fLinearFilterUVInset.fY = 0.5f;
1620         } else if (imgData.fSampling.filter == SkFilterMode::kLinear) {
1621             // We need to inset so that we aren't sampling outside the subset, but no farther.
1622             // Start by mapping the subset to UV texel space
1623             float scaleX = 1.f/ssx;
1624             float scaleY = 1.f/ssy;
1625             SkRect subsetUV = {imgData.fSubset.fLeft  *scaleX,
1626                                imgData.fSubset.fTop   *scaleY,
1627                                imgData.fSubset.fRight *scaleX,
1628                                imgData.fSubset.fBottom*scaleY};
1629             // Round to UV texel borders
1630             SkIRect iSubsetUV = subsetUV.roundOut();
1631             // Inset in UV and map back to Y texel space. This gives us the largest possible
1632             // inset rectangle that will not sample outside of the subset texels in UV space.
1633             SkRect insetRectUV = {(iSubsetUV.fLeft  +0.5f)*ssx,
1634                                   (iSubsetUV.fTop   +0.5f)*ssy,
1635                                   (iSubsetUV.fRight -0.5f)*ssx,
1636                                   (iSubsetUV.fBottom-0.5f)*ssy};
1637             // Compute intersection with original inset
1638             SkRect insetRect = imgData.fSubset.makeOutset(-0.5f, -0.5f);
1639             (void) insetRect.intersect(insetRectUV);
1640             // Compute max inset values to ensure we always remain within the subset.
1641             imgData.fLinearFilterUVInset = {std::max(insetRect.fLeft - imgData.fSubset.fLeft,
1642                                                      imgData.fSubset.fRight - insetRect.fRight),
1643                                             std::max(insetRect.fTop - imgData.fSubset.fTop,
1644                                                      imgData.fSubset.fBottom - insetRect.fBottom)};
1645         }
1646     }
1647 
1648     float yuvM[20];
1649     SkColorMatrix_YUV2RGB(yuvaInfo.yuvColorSpace(), yuvM);
1650     // We drop the fourth column entirely since the transformation
1651     // should not depend on alpha. The fifth column is sent as a separate
1652     // vector. The fourth row is also dropped entirely because alpha should
1653     // never be modified.
1654     SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
1655     SkASSERT(yuvM[15] == 0 && yuvM[16] == 0 && yuvM[17] == 0 && yuvM[19] == 0);
1656     imgData.fYUVtoRGBMatrix.setAll(
1657         yuvM[ 0], yuvM[ 1], yuvM[ 2],
1658         yuvM[ 5], yuvM[ 6], yuvM[ 7],
1659         yuvM[10], yuvM[11], yuvM[12]
1660     );
1661     imgData.fYUVtoRGBTranslate = {yuvM[4], yuvM[9], yuvM[14]};
1662 
1663     // The YUV formats can encode their own origin including reflection and rotation,
1664     // so we need to wrap our block in an additional local matrix transform.
1665     SkMatrix originMatrix = yuvaInfo.originMatrix();
1666     LocalMatrixShaderBlock::LMShaderData lmShaderData(originMatrix);
1667 
1668     KeyContextWithLocalMatrix newContext(keyContext, originMatrix);
1669 
1670     SkColorSpaceXformSteps steps;
1671     SkASSERT(steps.flags.mask() == 0);   // By default, the colorspace should have no effect
1672     if (!origShader->isRaw()) {
1673         steps = SkColorSpaceXformSteps(imageToDraw->colorSpace(),
1674                                        imageToDraw->alphaType(),
1675                                        keyContext.dstColorInfo().colorSpace(),
1676                                        keyContext.dstColorInfo().alphaType());
1677     }
1678     ColorSpaceTransformBlock::ColorSpaceTransformData data(steps);
1679 
1680     Compose(keyContext, builder, gatherer,
1681             /* addInnerToKey= */ [&]() -> void {
1682                 LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
1683                     YUVImageShaderBlock::AddBlock(newContext, builder, gatherer, imgData);
1684                 builder->endBlock();
1685             },
1686             /* addOuterToKey= */ [&]() -> void {
1687                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
1688             });
1689 }
1690 
swizzle_class_to_read_enum(const skgpu::Swizzle & swizzle)1691 static skgpu::graphite::ReadSwizzle swizzle_class_to_read_enum(const skgpu::Swizzle& swizzle) {
1692     if (swizzle == skgpu::Swizzle::RGBA()) {
1693         return skgpu::graphite::ReadSwizzle::kRGBA;
1694     } else if (swizzle == skgpu::Swizzle::RGB1()) {
1695         return skgpu::graphite::ReadSwizzle::kRGB1;
1696     } else if (swizzle == skgpu::Swizzle("rrr1")) {
1697         return skgpu::graphite::ReadSwizzle::kRRR1;
1698     } else if (swizzle == skgpu::Swizzle::BGRA()) {
1699         return skgpu::graphite::ReadSwizzle::kBGRA;
1700     } else if (swizzle == skgpu::Swizzle("000r")) {
1701         return skgpu::graphite::ReadSwizzle::k000R;
1702     } else {
1703         SKGPU_LOG_W("%s is an unsupported read swizzle. Defaulting to RGBA.\n",
1704                     swizzle.asString().data());
1705         return skgpu::graphite::ReadSwizzle::kRGBA;
1706     }
1707 }
1708 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkImageShader * shader)1709 static void add_to_key(const KeyContext& keyContext,
1710                        PaintParamsKeyBuilder* builder,
1711                        PipelineDataGatherer* gatherer,
1712                        const SkImageShader* shader) {
1713     SkASSERT(shader);
1714 
1715     auto [ imageToDraw, newSampling ] = GetGraphiteBacked(keyContext.recorder(),
1716                                                           shader->image().get(),
1717                                                           shader->sampling());
1718     if (!imageToDraw) {
1719         SKGPU_LOG_W("Couldn't convert ImageShader's image to a Graphite-backed image");
1720         builder->addBlock(BuiltInCodeSnippetID::kError);
1721         return;
1722     }
1723     if (!as_IB(shader->image())->isGraphiteBacked()) {
1724         // GetGraphiteBacked() created a new image (or fetched a cached image) from the client
1725         // image provider. This image was not available when NotifyInUse() visited the shader tree,
1726         // so call notify again. These images shouldn't really be producing new tasks since it's
1727         // unlikely that a client will be fulfilling with a dynamic image that wraps a long-lived
1728         // SkSurface. However, the images can be linked to a surface that rendered the initial
1729         // content and not calling notifyInUse() prevents unlinking the image from the Device.
1730         // If the client image provider then holds on to many of these images, the leaked Device and
1731         // DrawContext memory can be surprisingly high. b/338453542.
1732         // TODO (b/330864257): Once paint keys are extracted at draw time, AddToKey() will be
1733         // fully responsible for notifyInUse() calls and then we can simply always call this on
1734         // `imageToDraw`. The DrawContext that samples the image will also be available to AddToKey
1735         // so we won't have to pass in nullptr.
1736         SkASSERT(as_IB(imageToDraw)->isGraphiteBacked());
1737         static_cast<Image_Base*>(imageToDraw.get())->notifyInUse(keyContext.recorder(),
1738                                                                  /*drawContext=*/nullptr);
1739     }
1740     if (as_IB(imageToDraw)->isYUVA()) {
1741         return add_yuv_image_to_key(keyContext,
1742                                       builder,
1743                                       gatherer,
1744                                       shader,
1745                                       std::move(imageToDraw),
1746                                       newSampling);
1747     }
1748 
1749     auto view = AsView(imageToDraw.get());
1750     SkASSERT(newSampling.mipmap == SkMipmapMode::kNone || view.mipmapped() == Mipmapped::kYes);
1751 
1752     ImageShaderBlock::ImageData imgData(shader->sampling(),
1753                                         shader->tileModeX(),
1754                                         shader->tileModeY(),
1755                                         view.proxy()->dimensions(),
1756                                         shader->subset(),
1757                                         ReadSwizzle::kRGBA);
1758 
1759     // Here we detect pixel aligned blit-like image draws. Some devices have low precision filtering
1760     // and will produce degraded (blurry) images unexpectedly for sequential exact pixel blits when
1761     // not using nearest filtering. This is common for canvas scrolling implementations. Forcing
1762     // nearest filtering when possible can also be a minor perf/power optimization depending on the
1763     // hardware.
1764     bool samplingHasNoEffect = false;
1765     // Cubic sampling is will not filter the same as nearest even when pixel aligned.
1766     if (keyContext.optimizeSampling() == KeyContext::OptimizeSampling::kYes &&
1767         !newSampling.useCubic) {
1768         SkMatrix totalM = keyContext.local2Dev().asM33();
1769         if (keyContext.localMatrix()) {
1770             totalM.preConcat(*keyContext.localMatrix());
1771         }
1772         totalM.normalizePerspective();
1773         // The matrix should be translation with only pixel aligned 2d translation.
1774         samplingHasNoEffect = totalM.isTranslate() && SkScalarIsInt(totalM.getTranslateX()) &&
1775                               SkScalarIsInt(totalM.getTranslateY());
1776     }
1777 
1778     imgData.fSampling = samplingHasNoEffect ? SkFilterMode::kNearest : newSampling;
1779     imgData.fTextureProxy = view.refProxy();
1780     skgpu::Swizzle readSwizzle = view.swizzle();
1781     // If the color type is alpha-only, propagate the alpha value to the other channels.
1782     if (imageToDraw->isAlphaOnly()) {
1783         readSwizzle = skgpu::Swizzle::Concat(readSwizzle, skgpu::Swizzle("000a"));
1784     }
1785     imgData.fReadSwizzle = swizzle_class_to_read_enum(readSwizzle);
1786 
1787     if (!shader->isRaw()) {
1788         imgData.fSteps = SkColorSpaceXformSteps(imageToDraw->colorSpace(),
1789                                                 imageToDraw->alphaType(),
1790                                                 keyContext.dstColorInfo().colorSpace(),
1791                                                 keyContext.dstColorInfo().alphaType());
1792 
1793         if (imageToDraw->isAlphaOnly() && keyContext.scope() != KeyContext::Scope::kRuntimeEffect) {
1794             Blend(keyContext, builder, gatherer,
1795                   /* addBlendToKey= */ [&] () -> void {
1796                       AddKnownModeBlend(keyContext, builder, gatherer, SkBlendMode::kDstIn);
1797                   },
1798                   /* addSrcToKey= */ [&] () -> void {
1799                       ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
1800                   },
1801                   /* addDstToKey= */ [&]() -> void {
1802                       RGBPaintColorBlock::AddBlock(keyContext, builder, gatherer);
1803                   });
1804             return;
1805         }
1806     }
1807 
1808     ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
1809 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkImageShader * shader)1810 static void notify_in_use(Recorder* recorder,
1811                           DrawContext* drawContext,
1812                           const SkImageShader* shader) {
1813     auto image = as_IB(shader->image());
1814     if (!image->isGraphiteBacked()) {
1815         // If it's not graphite-backed, there's no pending graphite work.
1816         return;
1817     }
1818 
1819     static_cast<Image_Base*>(image)->notifyInUse(recorder, drawContext);
1820 }
1821 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkLocalMatrixShader * shader)1822 static void add_to_key(const KeyContext& keyContext,
1823                        PaintParamsKeyBuilder* builder,
1824                        PipelineDataGatherer* gatherer,
1825                        const SkLocalMatrixShader* shader) {
1826     SkASSERT(shader);
1827     auto wrappedShader = shader->wrappedShader().get();
1828 
1829     // Fold the texture's origin flip into the local matrix so that the image shader doesn't need
1830     // additional state
1831     SkMatrix matrix;
1832     SkShaderBase* wrappedShaderBase = as_SB(wrappedShader);
1833     if (wrappedShaderBase->type() == SkShaderBase::ShaderType::kImage) {
1834         auto imgShader = static_cast<const SkImageShader*>(wrappedShader);
1835         // If the image is not graphite backed then we can assume the origin will be TopLeft as we
1836         // require that in the ImageProvider utility. Also Graphite YUV images are assumed to be
1837         // TopLeft origin.
1838         // TODO (b/336788317): Fold YUVAImage's origin into this matrix as well.
1839         auto imgBase = as_IB(imgShader->image());
1840         if (imgBase->isGraphiteBacked() && !imgBase->isYUVA()) {
1841             auto imgGraphite = static_cast<Image*>(imgBase);
1842             SkASSERT(imgGraphite);
1843             const auto& view = imgGraphite->textureProxyView();
1844             if (view.origin() == Origin::kBottomLeft) {
1845                 matrix.setScaleY(-1);
1846                 matrix.setTranslateY(view.height());
1847             }
1848         }
1849     } else if (wrappedShaderBase->type() == SkShaderBase::ShaderType::kGradientBase) {
1850         auto gradShader = static_cast<const SkGradientBaseShader*>(wrappedShader);
1851         auto gradMatrix = gradShader->getGradientMatrix();
1852 
1853         // Override the conical gradient matrix since graphite uses a different algorithm
1854         // than the ganesh and raster backends.
1855         if (gradShader->asGradient() == SkShaderBase::GradientType::kConical) {
1856             auto conicalShader = static_cast<const SkConicalGradient*>(gradShader);
1857 
1858             SkMatrix conicalMatrix;
1859             if (conicalShader->getType() == SkConicalGradient::Type::kRadial) {
1860                 SkPoint center = conicalShader->getStartCenter();
1861                 conicalMatrix.postTranslate(-center.fX, -center.fY);
1862 
1863                 float scale = sk_ieee_float_divide(1, conicalShader->getDiffRadius());
1864                 conicalMatrix.postScale(scale, scale);
1865             } else {
1866                 SkAssertResult(SkConicalGradient::MapToUnitX(conicalShader->getStartCenter(),
1867                                                              conicalShader->getEndCenter(),
1868                                                              &conicalMatrix));
1869             }
1870             gradMatrix = conicalMatrix;
1871         }
1872 
1873         SkMatrix invGradMatrix;
1874         SkAssertResult(gradMatrix.invert(&invGradMatrix));
1875 
1876         matrix.postConcat(invGradMatrix);
1877     }
1878 
1879     matrix.postConcat(shader->localMatrix());
1880     LocalMatrixShaderBlock::LMShaderData lmShaderData(matrix);
1881 
1882     KeyContextWithLocalMatrix newContext(keyContext, matrix);
1883 
1884     LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
1885 
1886     AddToKey(newContext, builder, gatherer, wrappedShader);
1887 
1888     builder->endBlock();
1889 }
1890 
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkLocalMatrixShader * shader)1891 static void notify_in_use(Recorder* recorder,
1892                           DrawContext* drawContext,
1893                           const SkLocalMatrixShader* shader) {
1894     NotifyImagesInUse(recorder, drawContext, shader->wrappedShader().get());
1895 }
1896 
1897 // If either of these change then the corresponding change must also be made in the SkSL
1898 // perlin_noise_shader function.
1899 static_assert((int)SkPerlinNoiseShaderType::kFractalNoise ==
1900               (int)PerlinNoiseShaderBlock::Type::kFractalNoise);
1901 static_assert((int)SkPerlinNoiseShaderType::kTurbulence ==
1902               (int)PerlinNoiseShaderBlock::Type::kTurbulence);
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkPerlinNoiseShader * shader)1903 static void add_to_key(const KeyContext& keyContext,
1904                        PaintParamsKeyBuilder* builder,
1905                        PipelineDataGatherer* gatherer,
1906                        const SkPerlinNoiseShader* shader) {
1907     SkASSERT(shader);
1908     SkASSERT(shader->numOctaves());
1909 
1910     std::unique_ptr<SkPerlinNoiseShader::PaintingData> paintingData = shader->getPaintingData();
1911     paintingData->generateBitmaps();
1912 
1913     sk_sp<TextureProxy> perm =
1914             RecorderPriv::CreateCachedProxy(keyContext.recorder(),
1915                                             paintingData->getPermutationsBitmap(),
1916                                             "PerlinNoisePermTable");
1917 
1918     sk_sp<TextureProxy> noise =
1919             RecorderPriv::CreateCachedProxy(keyContext.recorder(), paintingData->getNoiseBitmap(),
1920                                             "PerlinNoiseNoiseTable");
1921 
1922     if (!perm || !noise) {
1923         SKGPU_LOG_W("Couldn't create tables for PerlinNoiseShader");
1924         builder->addBlock(BuiltInCodeSnippetID::kError);
1925         return;
1926     }
1927 
1928     PerlinNoiseShaderBlock::PerlinNoiseData perlinData(
1929             static_cast<PerlinNoiseShaderBlock::Type>(shader->noiseType()),
1930             paintingData->fBaseFrequency,
1931             shader->numOctaves(),
1932             {paintingData->fStitchDataInit.fWidth, paintingData->fStitchDataInit.fHeight});
1933 
1934     perlinData.fPermutationsProxy = std::move(perm);
1935     perlinData.fNoiseProxy = std::move(noise);
1936 
1937     PerlinNoiseShaderBlock::AddBlock(keyContext, builder, gatherer, perlinData);
1938 }
notify_in_use(Recorder *,DrawContext *,const SkPerlinNoiseShader *)1939 static void notify_in_use(Recorder*, DrawContext*, const SkPerlinNoiseShader*) {
1940     // No-op, perlin noise has no children.
1941 }
1942 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkPictureShader * shader)1943 static void add_to_key(const KeyContext& keyContext,
1944                        PaintParamsKeyBuilder* builder,
1945                        PipelineDataGatherer* gatherer,
1946                        const SkPictureShader* shader) {
1947     SkASSERT(shader);
1948 
1949     Recorder* recorder = keyContext.recorder();
1950     const Caps* caps = recorder->priv().caps();
1951 
1952     // TODO: We'll need additional plumbing to get the correct props from our callers. In
1953     // particular we'll need to expand the keyContext to have the surfaceProps.
1954     SkSurfaceProps props{};
1955 
1956     SkMatrix totalM = keyContext.local2Dev().asM33();
1957     if (keyContext.localMatrix()) {
1958         totalM.preConcat(*keyContext.localMatrix());
1959     }
1960     auto info = SkPictureShader::CachedImageInfo::Make(shader->tile(),
1961                                                        totalM,
1962                                                        keyContext.dstColorInfo().colorType(),
1963                                                        keyContext.dstColorInfo().colorSpace(),
1964                                                        caps->maxTextureSize(),
1965                                                        props);
1966     if (!info.success) {
1967         SKGPU_LOG_W("Couldn't access PictureShaders' Image info");
1968         builder->addBlock(BuiltInCodeSnippetID::kError);
1969         return;
1970     }
1971 
1972     // NOTE: While this is intended to be a "scratch" surface, we don't use MakeScratch() because
1973     // the SkPicture could contain arbitrary operations that rely on the Recorder's atlases, which
1974     // means the Surface's device has to participate in flushing when the atlas fills up.
1975     // TODO: Can this be an approx-fit image that's generated?
1976     // TODO: right now we're explicitly not caching here. We could expand the ImageProvider
1977     // API to include already Graphite-backed images, add a Recorder-local cache or add
1978     // rendered-picture images to the global cache.
1979     sk_sp<Surface> surface = Surface::Make(recorder,
1980                                            info.imageInfo,
1981                                            "PictureShaderTexture",
1982                                            Budgeted::kYes,
1983                                            Mipmapped::kNo,
1984                                            SkBackingFit::kExact,
1985                                            &info.props);
1986     if (!surface) {
1987         SKGPU_LOG_W("Could not create surface to render PictureShader");
1988         builder->addBlock(BuiltInCodeSnippetID::kError);
1989         return;
1990     }
1991 
1992     // NOTE: Don't call CachedImageInfo::makeImage() since that uses the legacy makeImageSnapshot()
1993     // API, which results in an extra texture copy on a Graphite Surface.
1994     surface->getCanvas()->concat(info.matrixForDraw);
1995     surface->getCanvas()->drawPicture(shader->picture().get());
1996     sk_sp<SkImage> img = SkSurfaces::AsImage(std::move(surface));
1997     // TODO: 'img' did not exist when notify_in_use() was called, but ideally the DrawTask to render
1998     // into 'surface' would be a child of the current device. While we push all tasks to the root
1999     // list this works out okay, but will need to be addressed before we move off that system.
2000     if (!img) {
2001         SKGPU_LOG_W("Couldn't create SkImage for PictureShader");
2002         builder->addBlock(BuiltInCodeSnippetID::kError);
2003         return;
2004     }
2005 
2006     const auto shaderLM = SkMatrix::Scale(1.f/info.tileScale.width(), 1.f/info.tileScale.height());
2007     sk_sp<SkShader> imgShader = img->makeShader(shader->tileModeX(), shader->tileModeY(),
2008                                                 SkSamplingOptions(shader->filter()), &shaderLM);
2009     if (!imgShader) {
2010         SKGPU_LOG_W("Couldn't create SkImageShader for PictureShader");
2011         builder->addBlock(BuiltInCodeSnippetID::kError);
2012         return;
2013     }
2014 
2015     AddToKey(keyContext, builder, gatherer, imgShader.get());
2016 }
notify_in_use(Recorder *,DrawContext *,const SkPictureShader *)2017 static void notify_in_use(Recorder*, DrawContext*, const SkPictureShader*) {
2018     // While the SkPicture the shader points to, may have Graphite-backed shaders that need to be
2019     // notified, that will happen when the picture is rendered into an image in add_to_key
2020 }
2021 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRuntimeShader * shader)2022 static void add_to_key(const KeyContext& keyContext,
2023                        PaintParamsKeyBuilder* builder,
2024                        PipelineDataGatherer* gatherer,
2025                        const SkRuntimeShader* shader) {
2026     SkASSERT(shader);
2027     sk_sp<SkRuntimeEffect> effect = shader->effect();
2028     sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
2029             effect->uniforms(),
2030             shader->uniformData(keyContext.dstColorInfo().colorSpace()),
2031             keyContext.dstColorInfo().colorSpace());
2032     SkASSERT(uniforms);
2033 
2034     RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
2035                                    {effect, std::move(uniforms)});
2036 
2037     add_children_to_key(keyContext, builder, gatherer,
2038                         shader->children(), effect->children());
2039 
2040     builder->endBlock();
2041 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkRuntimeShader * shader)2042 static void notify_in_use(Recorder* recorder,
2043                           DrawContext* drawContext,
2044                           const SkRuntimeShader* shader) {
2045     notify_in_use(recorder, drawContext, shader->children());
2046 }
2047 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkTransformShader * shader)2048 static void add_to_key(const KeyContext& keyContext,
2049                        PaintParamsKeyBuilder* builder,
2050                        PipelineDataGatherer* gatherer,
2051                        const SkTransformShader* shader) {
2052     SKGPU_LOG_W("Raster-only SkShader (SkTransformShader) encountered");
2053     builder->addBlock(BuiltInCodeSnippetID::kError);
2054 }
notify_in_use(Recorder *,DrawContext *,const SkTransformShader *)2055 static void notify_in_use(Recorder*, DrawContext*, const SkTransformShader*) {
2056     // no-op
2057 }
2058 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkTriColorShader * shader)2059 static void add_to_key(const KeyContext& keyContext,
2060                        PaintParamsKeyBuilder* builder,
2061                        PipelineDataGatherer* gatherer,
2062                        const SkTriColorShader* shader) {
2063     SKGPU_LOG_W("Raster-only SkShader (SkTriColorShader) encountered");
2064     builder->addBlock(BuiltInCodeSnippetID::kError);
2065 }
notify_in_use(Recorder *,DrawContext *,const SkTriColorShader *)2066 static void notify_in_use(Recorder*, DrawContext*, const SkTriColorShader*) {
2067     // no-op
2068 }
2069 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkWorkingColorSpaceShader * shader)2070 static void add_to_key(const KeyContext& keyContext,
2071                        PaintParamsKeyBuilder* builder,
2072                        PipelineDataGatherer* gatherer,
2073                        const SkWorkingColorSpaceShader* shader) {
2074     SkASSERT(shader);
2075 
2076     const SkColorInfo& dstInfo = keyContext.dstColorInfo();
2077     const SkAlphaType dstAT = dstInfo.alphaType();
2078     sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace();
2079     if (!dstCS) {
2080         dstCS = SkColorSpace::MakeSRGB();
2081     }
2082 
2083     sk_sp<SkColorSpace> workingCS = shader->workingSpace();
2084     SkColorInfo workingInfo(dstInfo.colorType(), dstAT, workingCS);
2085     KeyContextWithColorInfo workingContext(keyContext, workingInfo);
2086 
2087     // Compose the inner shader (in the working space) with a (working->dst) transform:
2088     Compose(keyContext, builder, gatherer,
2089         /* addInnerToKey= */ [&]() -> void {
2090             AddToKey(workingContext, builder, gatherer, shader->shader().get());
2091         },
2092         /* addOuterToKey= */ [&]() -> void {
2093             ColorSpaceTransformBlock::ColorSpaceTransformData data(
2094                     workingCS.get(), dstAT, dstCS.get(), dstAT);
2095             ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
2096         });
2097 }
notify_in_use(Recorder * recorder,DrawContext * drawContext,const SkWorkingColorSpaceShader * shader)2098 static void notify_in_use(Recorder* recorder,
2099                           DrawContext* drawContext,
2100                           const SkWorkingColorSpaceShader* shader) {
2101     NotifyImagesInUse(recorder, drawContext, shader->shader().get());
2102 }
2103 
create_color_and_offset_bitmap(int numStops,const SkPMColor4f * colors,const float * offsets)2104 static SkBitmap create_color_and_offset_bitmap(int numStops,
2105                                                const SkPMColor4f* colors,
2106                                                const float* offsets) {
2107     SkBitmap colorsAndOffsetsBitmap;
2108 
2109     colorsAndOffsetsBitmap.allocPixels(
2110             SkImageInfo::Make(numStops, 2, kRGBA_F16_SkColorType, kPremul_SkAlphaType));
2111 
2112     for (int i = 0; i < numStops; i++) {
2113         // TODO: there should be a way to directly set a premul pixel in a bitmap with
2114         // a premul color.
2115         SkColor4f unpremulColor = colors[i].unpremul();
2116         colorsAndOffsetsBitmap.erase(unpremulColor, SkIRect::MakeXYWH(i, 0, 1, 1));
2117 
2118         float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops - 1);
2119         SkASSERT(offset >= 0.0f && offset <= 1.0f);
2120 
2121         int exponent;
2122         float mantissa = frexp(offset, &exponent);
2123 
2124         SkHalf halfE = SkFloatToHalf(exponent);
2125         if ((int)SkHalfToFloat(halfE) != exponent) {
2126             SKGPU_LOG_W("Encoding gradient to f16 failed");
2127             return {};
2128         }
2129 
2130 #if defined(SK_DEBUG)
2131         SkHalf halfM = SkFloatToHalf(mantissa);
2132 
2133         float restored = ldexp(SkHalfToFloat(halfM), (int)SkHalfToFloat(halfE));
2134         float error = abs(restored - offset);
2135         SkASSERT(error < 0.001f);
2136 #endif
2137 
2138         // TODO: we're only using 2 of the f16s here. The encoding could be altered to better
2139         // preserve precision. This encoding yields < 0.001f error for 2^20 evenly spaced stops.
2140         colorsAndOffsetsBitmap.erase(SkColor4f{mantissa, (float)exponent, 0, 1},
2141                                      SkIRect::MakeXYWH(i, 1, 1, 1));
2142     }
2143 
2144     return colorsAndOffsetsBitmap;
2145 }
2146 
2147 // Please see GrGradientShader.cpp::make_interpolated_to_dst for substantial comments
2148 // 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)2149 static void make_interpolated_to_dst(const KeyContext& keyContext,
2150                                      PaintParamsKeyBuilder* builder,
2151                                      PipelineDataGatherer* gatherer,
2152                                      const GradientShaderBlocks::GradientData& gradData,
2153                                      const SkGradientShader::Interpolation& interp,
2154                                      SkColorSpace* intermediateCS) {
2155     using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
2156 
2157     bool inputPremul = static_cast<bool>(interp.fInPremul);
2158 
2159     switch (interp.fColorSpace) {
2160         case ColorSpace::kLab:
2161         case ColorSpace::kOKLab:
2162         case ColorSpace::kOKLabGamutMap:
2163         case ColorSpace::kLCH:
2164         case ColorSpace::kOKLCH:
2165         case ColorSpace::kOKLCHGamutMap:
2166         case ColorSpace::kHSL:
2167         case ColorSpace::kHWB:
2168             inputPremul = false;
2169             break;
2170         default:
2171             break;
2172     }
2173 
2174     const SkColorInfo& dstColorInfo = keyContext.dstColorInfo();
2175 
2176     SkColorSpace* dstColorSpace =
2177             dstColorInfo.colorSpace() ? dstColorInfo.colorSpace() : sk_srgb_singleton();
2178 
2179     SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
2180 
2181     ColorSpaceTransformBlock::ColorSpaceTransformData data(
2182             intermediateCS, intermediateAlphaType, dstColorSpace, dstColorInfo.alphaType());
2183 
2184     // The gradient block and colorSpace conversion block need to be combined
2185     // (via the Compose block) so that the localMatrix block can treat them as
2186     // one child.
2187     Compose(keyContext, builder, gatherer,
2188             /* addInnerToKey= */ [&]() -> void {
2189                 GradientShaderBlocks::AddBlock(keyContext, builder, gatherer, gradData);
2190             },
2191             /* addOuterToKey= */ [&]() -> void {
2192                 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
2193             });
2194 }
2195 
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)2196 static void add_gradient_to_key(const KeyContext& keyContext,
2197                                 PaintParamsKeyBuilder* builder,
2198                                 PipelineDataGatherer* gatherer,
2199                                 const SkGradientBaseShader* shader,
2200                                 SkPoint point0,
2201                                 SkPoint point1,
2202                                 float radius0,
2203                                 float radius1,
2204                                 float bias,
2205                                 float scale) {
2206     SkColor4fXformer xformedColors(shader, keyContext.dstColorInfo().colorSpace());
2207     const SkPMColor4f* colors = xformedColors.fColors.begin();
2208     const float* positions = xformedColors.fPositions;
2209     const int colorCount = xformedColors.fColors.size();
2210 
2211     sk_sp<TextureProxy> proxy;
2212 
2213     bool useStorageBuffer = keyContext.caps()->storageBufferSupport() &&
2214                                 keyContext.caps()->storageBufferPreferred();
2215     if (colorCount > GradientShaderBlocks::GradientData::kNumInternalStorageStops
2216             && !useStorageBuffer) {
2217         if (shader->cachedBitmap().empty()) {
2218             SkBitmap colorsAndOffsetsBitmap =
2219                     create_color_and_offset_bitmap(colorCount, colors, positions);
2220             if (colorsAndOffsetsBitmap.empty()) {
2221                 SKGPU_LOG_W("Couldn't create GradientShader's color and offset bitmap");
2222                 builder->addBlock(BuiltInCodeSnippetID::kError);
2223                 return;
2224             }
2225             shader->setCachedBitmap(colorsAndOffsetsBitmap);
2226         }
2227 
2228         proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), shader->cachedBitmap(),
2229                                                 "GradientTexture");
2230         if (!proxy) {
2231             SKGPU_LOG_W("Couldn't create GradientShader's color and offset bitmap proxy");
2232             builder->addBlock(BuiltInCodeSnippetID::kError);
2233             return;
2234         }
2235     }
2236 
2237     GradientShaderBlocks::GradientData data(shader->asGradient(),
2238                                             point0,
2239                                             point1,
2240                                             radius0,
2241                                             radius1,
2242                                             bias,
2243                                             scale,
2244                                             shader->getTileMode(),
2245                                             colorCount,
2246                                             colors,
2247                                             positions,
2248                                             std::move(proxy),
2249                                             useStorageBuffer,
2250                                             shader->getInterpolation());
2251 
2252     make_interpolated_to_dst(keyContext,
2253                              builder,
2254                              gatherer,
2255                              data,
2256                              shader->getInterpolation(),
2257                              xformedColors.fIntermediateColorSpace.get());
2258 }
2259 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkConicalGradient * shader)2260 static void add_gradient_to_key(const KeyContext& keyContext,
2261                                 PaintParamsKeyBuilder* builder,
2262                                 PipelineDataGatherer* gatherer,
2263                                 const SkConicalGradient* shader) {
2264     SkScalar r0 = shader->getStartRadius();
2265     SkScalar r1 = shader->getEndRadius();
2266 
2267     if (shader->getType() != SkConicalGradient::Type::kRadial) {
2268         // Since we map the centers to be (0,0) and (1,0) in the gradient matrix,
2269         // there is a scale of 1/distance-between-centers that has to be applied to the radii.
2270         r0 /= shader->getCenterX1();
2271         r1 /= shader->getCenterX1();
2272     } else {
2273         r0 /= shader->getDiffRadius();
2274         r1 /= shader->getDiffRadius();
2275     }
2276 
2277     add_gradient_to_key(keyContext,
2278                         builder,
2279                         gatherer,
2280                         shader,
2281                         shader->getStartCenter(),
2282                         shader->getEndCenter(),
2283                         r0,
2284                         r1,
2285                         0.0f,
2286                         0.0f);
2287 }
2288 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkLinearGradient * shader)2289 static void add_gradient_to_key(const KeyContext& keyContext,
2290                                 PaintParamsKeyBuilder* builder,
2291                                 PipelineDataGatherer* gatherer,
2292                                 const SkLinearGradient* shader) {
2293     add_gradient_to_key(keyContext,
2294                         builder,
2295                         gatherer,
2296                         shader,
2297                         shader->start(),
2298                         shader->end(),
2299                         0.0f,
2300                         0.0f,
2301                         0.0f,
2302                         0.0f);
2303 }
2304 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkRadialGradient * shader)2305 static void add_gradient_to_key(const KeyContext& keyContext,
2306                                 PaintParamsKeyBuilder* builder,
2307                                 PipelineDataGatherer* gatherer,
2308                                 const SkRadialGradient* shader) {
2309     add_gradient_to_key(keyContext,
2310                         builder,
2311                         gatherer,
2312                         shader,
2313                         shader->center(),
2314                         { 0.0f, 0.0f },
2315                         shader->radius(),
2316                         0.0f,
2317                         0.0f,
2318                         0.0f);
2319 }
2320 
add_gradient_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkSweepGradient * shader)2321 static void add_gradient_to_key(const KeyContext& keyContext,
2322                                 PaintParamsKeyBuilder* builder,
2323                                 PipelineDataGatherer* gatherer,
2324                                 const SkSweepGradient* shader) {
2325     add_gradient_to_key(keyContext,
2326                         builder,
2327                         gatherer,
2328                         shader,
2329                         shader->center(),
2330                         { 0.0f, 0.0f },
2331                         0.0f,
2332                         0.0f,
2333                         shader->tBias(),
2334                         shader->tScale());
2335 }
2336 
add_to_key(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkGradientBaseShader * shader)2337 static void add_to_key(const KeyContext& keyContext,
2338                        PaintParamsKeyBuilder* builder,
2339                        PipelineDataGatherer* gatherer,
2340                        const SkGradientBaseShader* shader) {
2341     SkASSERT(shader);
2342     switch (shader->asGradient()) {
2343 #define M(type)                                                               \
2344     case SkShaderBase::GradientType::k##type:                                 \
2345         add_gradient_to_key(keyContext,                                       \
2346                             builder,                                          \
2347                             gatherer,                                         \
2348                             static_cast<const Sk##type##Gradient*>(shader));  \
2349         return;
2350         SK_ALL_GRADIENTS(M)
2351 #undef M
2352         case SkShaderBase::GradientType::kNone:
2353             SkDEBUGFAIL("Gradient shader says its type is none");
2354             return;
2355     }
2356     SkUNREACHABLE;
2357 }
notify_in_use(Recorder *,DrawContext *,const SkGradientBaseShader *)2358 static void notify_in_use(Recorder*, DrawContext*, const SkGradientBaseShader*) {
2359     // Gradients do not have children, so no images to notify
2360 }
2361 
AddToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,const SkShader * shader)2362 void AddToKey(const KeyContext& keyContext,
2363               PaintParamsKeyBuilder* builder,
2364               PipelineDataGatherer* gatherer,
2365               const SkShader* shader) {
2366     if (!shader) {
2367         return;
2368     }
2369     switch (as_SB(shader)->type()) {
2370 #define M(type)                                                        \
2371     case SkShaderBase::ShaderType::k##type:                            \
2372         add_to_key(keyContext,                                         \
2373                    builder,                                            \
2374                    gatherer,                                           \
2375                    static_cast<const Sk##type##Shader*>(shader)); \
2376         return;
2377         SK_ALL_SHADERS(M)
2378 #undef M
2379     }
2380     SkUNREACHABLE;
2381 }
2382 
NotifyImagesInUse(Recorder * recorder,DrawContext * drawContext,const SkShader * shader)2383 void NotifyImagesInUse(Recorder* recorder,
2384                        DrawContext* drawContext,
2385                        const SkShader* shader) {
2386     if (!shader) {
2387         return;
2388     }
2389     switch (as_SB(shader)->type()) {
2390 #define M(type)                                                      \
2391     case SkShaderBase::ShaderType::k##type:                          \
2392         notify_in_use(recorder,                                      \
2393                       drawContext,                                   \
2394                       static_cast<const Sk##type##Shader*>(shader)); \
2395         return;
2396         SK_ALL_SHADERS(M)
2397 #undef M
2398     }
2399     SkUNREACHABLE;
2400 }
2401 
2402 
2403 } // namespace skgpu::graphite
2404