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