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