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