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