1 /*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #ifndef skgpu_graphite_KeyHelpers_DEFINED
9 #define skgpu_graphite_KeyHelpers_DEFINED
10
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkBlendMode.h"
13 #include "include/core/SkM44.h"
14 #include "include/core/SkPoint3.h"
15 #include "include/core/SkSamplingOptions.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkSpan.h"
18 #include "include/core/SkTileMode.h"
19 #include "include/effects/SkGradientShader.h"
20 #include "include/gpu/graphite/Context.h"
21 #include "include/private/base/SkTArray.h"
22 #include "src/core/SkColorData.h"
23 #include "src/core/SkColorSpaceXformSteps.h"
24 #include "src/gpu/graphite/PaintParamsKey.h"
25 #include "src/gpu/graphite/ReadSwizzle.h"
26 #include "src/gpu/graphite/TextureProxy.h"
27 #include "src/shaders/SkShaderBase.h"
28 #include "src/shaders/gradients/SkGradientBaseShader.h"
29
30 class SkColorFilter;
31 class SkData;
32 class SkRuntimeEffect;
33
34 namespace skgpu::graphite {
35
36 class DrawContext;
37 class KeyContext;
38 class PaintParamsKeyBuilder;
39 class PipelineDataGatherer;
40 class UniquePaintParamsID;
41 enum class ReadSwizzle;
42
43 // Types of logical "destinations" that a blender might blend with.
44 enum class DstColorType {
45 // A color read from the framebuffer.
46 kSurface,
47 // A color provided by geometry.
48 kPrimitive,
49 // A color evaluated by a child shader.
50 kChildOutput,
51 };
52
53 /**
54 * The KeyHelpers can be used to manually construct an SkPaintParamsKey.
55 *
56 * TODO: If we restructure how the keys are made, we can utilize a single block type for the
57 * different blend blocks outlined below. The different Src/Dst pairings could instead be encoded
58 * as parent-child relationships.
59 */
60
61 struct SolidColorShaderBlock {
62 static void AddBlock(const KeyContext&,
63 PaintParamsKeyBuilder*,
64 PipelineDataGatherer*,
65 const SkPMColor4f&);
66 };
67
68 struct RGBPaintColorBlock {
69 static void AddBlock(const KeyContext&,
70 PaintParamsKeyBuilder*,
71 PipelineDataGatherer*);
72 };
73
74 struct AlphaOnlyPaintColorBlock {
75 static void AddBlock(const KeyContext&,
76 PaintParamsKeyBuilder*,
77 PipelineDataGatherer*);
78 };
79
80 struct GradientShaderBlocks {
81 struct GradientData {
82 // The number of stops stored internal to this data structure before falling back to
83 // bitmap storage.
84 static constexpr int kNumInternalStorageStops = 8;
85
86 // This ctor is used during pre-compilation when we don't have enough information to
87 // extract uniform data. However, we must be able to provide enough data to make all the
88 // relevant decisions about which code snippets to use.
89 GradientData(SkShaderBase::GradientType, int numStops, bool useStorageBuffer);
90
91 // This ctor is used when extracting information from PaintParams. It must provide
92 // enough data to generate the uniform data the selected code snippet will require.
93 GradientData(SkShaderBase::GradientType,
94 SkPoint point0, SkPoint point1,
95 float radius0, float radius1,
96 float bias, float scale,
97 SkTileMode,
98 int numStops,
99 const SkPMColor4f* colors,
100 const float* offsets,
101 const SkGradientBaseShader* shader,
102 sk_sp<TextureProxy> colorsAndOffsetsProxy,
103 bool useStorageBuffer,
104 const SkGradientShader::Interpolation&);
105
106 bool operator==(const GradientData& rhs) const = delete;
107 bool operator!=(const GradientData& rhs) const = delete;
108
109 // Layout options.
110 SkShaderBase::GradientType fType;
111 SkPoint fPoints[2];
112 float fRadii[2];
113
114 // Layout options for sweep gradient.
115 float fBias;
116 float fScale;
117
118 SkTileMode fTM;
119 int fNumStops;
120 bool fUseStorageBuffer;
121
122 // For gradients w/ <= kNumInternalStorageStops stops we use fColors and fOffsets.
123 // The offsets are packed into a single float4 to save space when the layout is std140.
124 //
125 // Otherwise when storage buffers are preferred, we save the colors and offsets pointers
126 // to fSrcColors and fSrcOffsets so we can directly copy to the gatherer gradient buffer,
127 // else we pack the data into the fColorsAndOffsetsProxy texture.
128 SkPMColor4f fColors[kNumInternalStorageStops];
129 SkV4 fOffsets[kNumInternalStorageStops / 4];
130 sk_sp<TextureProxy> fColorsAndOffsetsProxy;
131 const SkPMColor4f* fSrcColors;
132 const float* fSrcOffsets;
133 const SkGradientBaseShader* fSrcShader;
134
135 SkGradientShader::Interpolation fInterpolation;
136 };
137
138 static void AddBlock(const KeyContext&,
139 PaintParamsKeyBuilder*,
140 PipelineDataGatherer*,
141 const GradientData&);
142 };
143
144 struct LocalMatrixShaderBlock {
145 struct LMShaderData {
LMShaderDataLocalMatrixShaderBlock::LMShaderData146 LMShaderData(const SkMatrix& localMatrix)
147 : fLocalMatrix(localMatrix)
148 , fHasPerspective(localMatrix.hasPerspective()) {}
149
150 const SkM44 fLocalMatrix;
151 const bool fHasPerspective;
152 };
153
154 static void BeginBlock(const KeyContext&,
155 PaintParamsKeyBuilder*,
156 PipelineDataGatherer*,
157 const LMShaderData&);
158 };
159
160 struct ImageShaderBlock {
161 struct ImageData {
162 ImageData(const SkSamplingOptions& sampling,
163 SkTileMode tileModeX,
164 SkTileMode tileModeY,
165 SkISize imgSize,
166 SkRect subset);
167 SkSamplingOptions fSampling;
168 std::pair<SkTileMode, SkTileMode> fTileModes;
169 SkISize fImgSize;
170 SkRect fSubset;
171
172 // TODO: Currently this is only filled in when we're generating the key from an actual
173 // SkImageShader. In the pre-compile case we will need to create a Graphite promise
174 // image which holds the appropriate data.
175 sk_sp<TextureProxy> fTextureProxy;
176 };
177
178 static void AddBlock(const KeyContext&,
179 PaintParamsKeyBuilder*,
180 PipelineDataGatherer*,
181 const ImageData&);
182 };
183
184 struct YUVImageShaderBlock {
185 struct ImageData {
186 ImageData(const SkSamplingOptions& sampling,
187 SkTileMode tileModeX,
188 SkTileMode tileModeY,
189 SkISize imgSize,
190 SkRect subset);
191
192 SkSamplingOptions fSampling;
193 SkSamplingOptions fSamplingUV;
194 std::pair<SkTileMode, SkTileMode> fTileModes;
195 SkISize fImgSize;
196 SkISize fImgSizeUV; // Size of UV planes relative to Y's texel space
197 SkRect fSubset;
198 SkPoint fLinearFilterUVInset = { 0.50001f, 0.50001f };
199 SkV4 fChannelSelect[4];
200 float fAlphaParam = 0;
201 SkMatrix fYUVtoRGBMatrix;
202 SkPoint3 fYUVtoRGBTranslate;
203
204 // TODO: Currently these are only filled in when we're generating the key from an actual
205 // SkImageShader. In the pre-compile case we will need to create Graphite promise
206 // images which hold the appropriate data.
207 sk_sp<TextureProxy> fTextureProxies[4];
208 };
209
210 static void AddBlock(const KeyContext&,
211 PaintParamsKeyBuilder*,
212 PipelineDataGatherer*,
213 const ImageData&);
214 };
215
216 struct CoordClampShaderBlock {
217 struct CoordClampData {
CoordClampDataCoordClampShaderBlock::CoordClampData218 CoordClampData(SkRect subset) : fSubset(subset) {}
219
220 SkRect fSubset;
221 };
222
223 // The gatherer and data should be null or non-null together
224 static void BeginBlock(const KeyContext&,
225 PaintParamsKeyBuilder*,
226 PipelineDataGatherer*,
227 const CoordClampData&);
228 };
229
230 struct DitherShaderBlock {
231 struct DitherData {
DitherDataDitherShaderBlock::DitherData232 DitherData(float range, sk_sp<TextureProxy> proxy)
233 : fRange(range)
234 , fLUTProxy(std::move(proxy)) {}
235
236 float fRange;
237 sk_sp<TextureProxy> fLUTProxy;
238 };
239
240 static void AddBlock(const KeyContext&,
241 PaintParamsKeyBuilder*,
242 PipelineDataGatherer*,
243 const DitherData&);
244 };
245
246 struct PerlinNoiseShaderBlock {
247 enum class Type {
248 kFractalNoise,
249 kTurbulence,
250 };
251
252 struct PerlinNoiseData {
PerlinNoiseDataPerlinNoiseShaderBlock::PerlinNoiseData253 PerlinNoiseData(Type type,
254 SkVector baseFrequency,
255 int numOctaves,
256 SkISize stitchData)
257 : fType(type)
258 , fBaseFrequency(baseFrequency)
259 , fNumOctaves(numOctaves)
260 , fStitchData{ SkIntToFloat(stitchData.fWidth), SkIntToFloat(stitchData.fHeight) } {
261 }
262
stitchingPerlinNoiseShaderBlock::PerlinNoiseData263 bool stitching() const { return !fStitchData.isZero(); }
264
265 Type fType;
266 SkVector fBaseFrequency;
267 int fNumOctaves;
268 SkVector fStitchData;
269
270 sk_sp<TextureProxy> fPermutationsProxy;
271 sk_sp<TextureProxy> fNoiseProxy;
272 };
273
274 // The gatherer and data should be null or non-null together
275 static void AddBlock(const KeyContext&,
276 PaintParamsKeyBuilder*,
277 PipelineDataGatherer*,
278 const PerlinNoiseData&);
279 };
280
281 struct BlendComposeBlock {
282 static void BeginBlock(const KeyContext&, PaintParamsKeyBuilder*, PipelineDataGatherer*);
283 };
284
285 struct PorterDuffBlenderBlock {
286 static void AddBlock(const KeyContext&,
287 PaintParamsKeyBuilder*,
288 PipelineDataGatherer*,
289 SkSpan<const float> coeffs);
290 };
291
292 struct HSLCBlenderBlock {
293 static void AddBlock(const KeyContext&,
294 PaintParamsKeyBuilder*,
295 PipelineDataGatherer*,
296 SkSpan<const float> coeffs);
297 };
298
299 struct ComposeBlock {
300 static void BeginBlock(const KeyContext&,
301 PaintParamsKeyBuilder*,
302 PipelineDataGatherer*);
303 };
304
305 struct MatrixColorFilterBlock {
306 struct MatrixColorFilterData {
MatrixColorFilterDataMatrixColorFilterBlock::MatrixColorFilterData307 MatrixColorFilterData(const float matrix[20], bool inHSLA, bool clamp)
308 : fMatrix(matrix[ 0], matrix[ 1], matrix[ 2], matrix[ 3],
309 matrix[ 5], matrix[ 6], matrix[ 7], matrix[ 8],
310 matrix[10], matrix[11], matrix[12], matrix[13],
311 matrix[15], matrix[16], matrix[17], matrix[18])
312 , fTranslate{matrix[4], matrix[9], matrix[14], matrix[19]}
313 , fInHSLA(inHSLA)
314 , fClamp(clamp) {
315 }
316
317 SkM44 fMatrix;
318 SkV4 fTranslate;
319 bool fInHSLA;
320 bool fClamp;
321 };
322
323 // The gatherer and matrixCFData should be null or non-null together
324 static void AddBlock(const KeyContext&,
325 PaintParamsKeyBuilder*,
326 PipelineDataGatherer*,
327 const MatrixColorFilterData&);
328 };
329
330 struct TableColorFilterBlock {
331 struct TableColorFilterData {
TableColorFilterDataTableColorFilterBlock::TableColorFilterData332 TableColorFilterData(sk_sp<TextureProxy> proxy) : fTextureProxy(std::move(proxy)) {}
333
334 sk_sp<TextureProxy> fTextureProxy;
335 };
336
337 static void AddBlock(const KeyContext&,
338 PaintParamsKeyBuilder*,
339 PipelineDataGatherer*,
340 const TableColorFilterData&);
341 };
342
343 struct ColorSpaceTransformBlock {
344 struct ColorSpaceTransformData {
345 ColorSpaceTransformData(const SkColorSpace* src,
346 SkAlphaType srcAT,
347 const SkColorSpace* dst,
348 SkAlphaType dstAT);
ColorSpaceTransformDataColorSpaceTransformBlock::ColorSpaceTransformData349 ColorSpaceTransformData(const SkColorSpaceXformSteps& steps) { fSteps = steps; }
ColorSpaceTransformDataColorSpaceTransformBlock::ColorSpaceTransformData350 ColorSpaceTransformData(ReadSwizzle swizzle) : fReadSwizzle(swizzle) {
351 SkASSERT(fSteps.fFlags.mask() == 0); // By default, the colorspace should have no effect
352 }
353 SkColorSpaceXformSteps fSteps;
354 ReadSwizzle fReadSwizzle = ReadSwizzle::kRGBA;
355 };
356
357 static void AddBlock(const KeyContext&,
358 PaintParamsKeyBuilder*,
359 PipelineDataGatherer*,
360 const ColorSpaceTransformData&);
361 };
362
363 struct NonMSAAClipBlock {
364 struct NonMSAAClipData {
NonMSAAClipDataNonMSAAClipBlock::NonMSAAClipData365 NonMSAAClipData(SkRect rect,
366 SkPoint radiusPlusHalf,
367 SkRect edgeSelect,
368 SkPoint texCoordOffset,
369 SkRect maskBounds,
370 sk_sp<TextureProxy> atlasTexture)
371 : fRect(rect)
372 , fRadiusPlusHalf(radiusPlusHalf)
373 , fEdgeSelect(edgeSelect)
374 , fTexCoordOffset(texCoordOffset)
375 , fMaskBounds(maskBounds)
376 , fAtlasTexture(std::move(atlasTexture)){}
377 // analytic clip
378 SkRect fRect; // bounds, outset by 0.5
379 SkPoint fRadiusPlusHalf; // abs() of .x is radius+0.5, if < 0 indicates inverse fill
380 // .y is 1/(radius+0.5)
381 SkRect fEdgeSelect; // 1 indicates a rounded corner on that side (LTRB), 0 otherwise
382
383 // atlas clip
384 SkPoint fTexCoordOffset; // translation from local coords to unnormalized texel coords
385 SkRect fMaskBounds; // bounds of mask area, in unnormalized texel coords
386
387 sk_sp<TextureProxy> fAtlasTexture;
388 };
389
390 static void AddBlock(const KeyContext&,
391 PaintParamsKeyBuilder*,
392 PipelineDataGatherer*,
393 const NonMSAAClipData&);
394 };
395
396 /**
397 * Adds a block that references the primitive color produced by the RenderStep and accounts for
398 * color space transformation.
399 */
400 void AddPrimitiveColor(const KeyContext&,
401 PaintParamsKeyBuilder*,
402 PipelineDataGatherer*,
403 const SkColorSpace* primitiveColorSpace);
404
405 /**
406 * Blend mode color filters blend their input (as the dst color) with some given color (supplied
407 * via a uniform) as the src color.
408 */
409 void AddBlendModeColorFilter(const KeyContext&,
410 PaintParamsKeyBuilder*,
411 PipelineDataGatherer*,
412 SkBlendMode,
413 const SkPMColor4f& srcColor);
414
415 struct RuntimeEffectBlock {
416 struct ShaderData {
417 // This ctor is used during pre-compilation when we don't have enough information to
418 // extract uniform data.
419 ShaderData(sk_sp<const SkRuntimeEffect> effect);
420
421 // This ctor is used when extracting information from PaintParams.
422 ShaderData(sk_sp<const SkRuntimeEffect> effect,
423 sk_sp<const SkData> uniforms);
424
425 bool operator==(const ShaderData& rhs) const;
426 bool operator!=(const ShaderData& rhs) const { return !(*this == rhs); }
427
428 // Runtime shader data.
429 sk_sp<const SkRuntimeEffect> fEffect;
430 sk_sp<const SkData> fUniforms;
431 };
432
433 // On a false return, no block has been started
434 static bool BeginBlock(const KeyContext&,
435 PaintParamsKeyBuilder*,
436 PipelineDataGatherer*,
437 const ShaderData&);
438
439 // Add a no-op placeholder for an incorrect runtime effect
440 static void AddNoOpEffect(const KeyContext&,
441 PaintParamsKeyBuilder*,
442 PipelineDataGatherer*,
443 SkRuntimeEffect*);
444 };
445
446 void AddToKey(const KeyContext&,
447 PaintParamsKeyBuilder*,
448 PipelineDataGatherer*,
449 const SkBlender*);
450
451 /**
452 * Add implementation details, for the specified backend, of this SkColorFilter to the
453 * provided key.
454 *
455 * @param keyContext backend context for key creation
456 * @param builder builder for creating the key for this SkShader
457 * @param gatherer if non-null, storage for this colorFilter's data
458 * @param filter This function is a no-op if filter is null.
459 */
460 void AddToKey(const KeyContext& keyContext,
461 PaintParamsKeyBuilder* builder,
462 PipelineDataGatherer* gatherer,
463 const SkColorFilter* filter);
464
465 /**
466 * Add implementation details, for the specified backend, of this SkShader to the
467 * provided key.
468 *
469 * @param keyContext backend context for key creation
470 * @param builder builder for creating the key for this SkShader
471 * @param gatherer if non-null, storage for this colorFilter's data
472 * @param shader This function is a no-op if shader is null.
473 */
474 void AddToKey(const KeyContext& keyContext,
475 PaintParamsKeyBuilder* builder,
476 PipelineDataGatherer* gatherer,
477 const SkShader* shader);
478
479 // TODO(b/330864257) These visitation functions are redundant with AddToKey, except that they are
480 // executed in the Device::drawGeometry() stack frame, whereas the keys are currently deferred until
481 // DrawPass::Make. Image use needs to be detected in the draw frame to split tasks to match client
482 // actions. Once paint keys are extracted in the draw frame, this can go away entirely.
483 void NotifyImagesInUse(Recorder*, DrawContext*, const SkBlender*);
484 void NotifyImagesInUse(Recorder*, DrawContext*, const SkColorFilter*);
485 void NotifyImagesInUse(Recorder*, DrawContext*, const SkShader*);
486
487 template <typename AddBlendToKeyT, typename AddSrcToKeyT, typename AddDstToKeyT>
Blend(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,AddBlendToKeyT addBlendToKey,AddSrcToKeyT addSrcToKey,AddDstToKeyT addDstToKey)488 void Blend(const KeyContext& keyContext,
489 PaintParamsKeyBuilder* keyBuilder,
490 PipelineDataGatherer* gatherer,
491 AddBlendToKeyT addBlendToKey,
492 AddSrcToKeyT addSrcToKey,
493 AddDstToKeyT addDstToKey) {
494 BlendComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer);
495
496 addSrcToKey();
497
498 addDstToKey();
499
500 addBlendToKey();
501
502 keyBuilder->endBlock(); // BlendComposeBlock
503 }
504
505 template <typename AddInnerToKeyT, typename AddOuterToKeyT>
Compose(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,AddInnerToKeyT addInnerToKey,AddOuterToKeyT addOuterToKey)506 void Compose(const KeyContext& keyContext,
507 PaintParamsKeyBuilder* keyBuilder,
508 PipelineDataGatherer* gatherer,
509 AddInnerToKeyT addInnerToKey,
510 AddOuterToKeyT addOuterToKey) {
511 ComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer);
512
513 addInnerToKey();
514
515 addOuterToKey();
516
517 keyBuilder->endBlock(); // ComposeBlock
518 }
519
520 } // namespace skgpu::graphite
521
522 #endif // skgpu_graphite_KeyHelpers_DEFINED
523