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