xref: /aosp_15_r20/external/skia/src/core/SkKnownRuntimeEffects.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2024 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/core/SkKnownRuntimeEffects.h"
9 
10 #include "include/core/SkString.h"
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "src/core/SkRuntimeEffectPriv.h"
13 #include "src/effects/imagefilters/SkMatrixConvolutionImageFilter.h"
14 
15 namespace SkKnownRuntimeEffects {
16 
17 namespace {
18 
19 // This must be kept in sync w/ the version in BlurUtils.h
20 static constexpr int kMaxBlurSamples = 28;
21 
make_blur_1D_effect(int kernelWidth,const SkRuntimeEffect::Options & options)22 SkRuntimeEffect* make_blur_1D_effect(int kernelWidth, const SkRuntimeEffect::Options& options) {
23     SkASSERT(kernelWidth <= kMaxBlurSamples);
24     // The SkSL structure performs two kernel taps; if the kernel has an odd width the last
25     // sample will be skipped with the current loop limit calculation.
26     SkASSERT(kernelWidth % 2 == 0);
27     return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
28             SkStringPrintf(
29                     // The coefficients are always stored for the max radius to keep the
30                     // uniform block consistent across all effects.
31                     "const int kMaxUniformKernelSize = %d / 2;"
32                     // But we generate an exact loop over the kernel size. Note that this
33                     // program can be used for kernels smaller than the constructed max as long
34                     // as the kernel weights for excess entries are set to 0.
35                     "const int kMaxLoopLimit = %d / 2;"
36 
37                     "uniform half4 offsetsAndKernel[kMaxUniformKernelSize];"
38                     "uniform half2 dir;"
39 
40                     "uniform shader child;"
41 
42                     "half4 main(float2 coord) {"
43                         "half4 sum = half4(0);"
44                         "for (int i = 0; i < kMaxLoopLimit; ++i) {"
45                             "half4 s = offsetsAndKernel[i];"
46                             "sum += s.y * child.eval(coord + s.x*dir);"
47                             "sum += s.w * child.eval(coord + s.z*dir);"
48                         "}"
49                         "return sum;"
50                     "}", kMaxBlurSamples, kernelWidth).c_str(),
51                     options);
52 }
53 
make_blur_2D_effect(int maxKernelSize,const SkRuntimeEffect::Options & options)54 SkRuntimeEffect* make_blur_2D_effect(int maxKernelSize, const SkRuntimeEffect::Options& options) {
55     SkASSERT(maxKernelSize % 4 == 0);
56     return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
57             SkStringPrintf(
58                     // The coefficients are always stored for the max radius to keep the
59                     // uniform block consistent across all effects.
60                     "const int kMaxUniformKernelSize = %d / 4;"
61                     "const int kMaxUniformOffsetsSize = 2*kMaxUniformKernelSize;"
62                     // But we generate an exact loop over the kernel size. Note that this
63                     // program can be used for kernels smaller than the constructed max as long
64                     // as the kernel weights for excess entries are set to 0.
65                     "const int kMaxLoopLimit = %d / 4;"
66 
67                     // Pack scalar coefficients into half4 for better packing on std140, and
68                     // upload offsets to avoid having to transform the 1D index into a 2D coord
69                     "uniform half4 kernel[kMaxUniformKernelSize];"
70                     "uniform half4 offsets[kMaxUniformOffsetsSize];"
71 
72                     "uniform shader child;"
73 
74                     "half4 main(float2 coord) {"
75                         "half4 sum = half4(0);"
76 
77                         "for (int i = 0; i < kMaxLoopLimit; ++i) {"
78                             "half4 k = kernel[i];"
79                             "half4 o = offsets[2*i];"
80                             "sum += k.x * child.eval(coord + o.xy);"
81                             "sum += k.y * child.eval(coord + o.zw);"
82                             "o = offsets[2*i + 1];"
83                             "sum += k.z * child.eval(coord + o.xy);"
84                             "sum += k.w * child.eval(coord + o.zw);"
85                         "}"
86                         "return sum;"
87                     "}", kMaxBlurSamples, maxKernelSize).c_str(),
88                     options);
89 }
90 
91 enum class MatrixConvolutionImpl {
92     kUniformBased,
93     kTextureBasedSm,
94     kTextureBasedLg,
95 };
96 
97 // There are three shader variants:
98 //    a smaller kernel version that stores the matrix in uniforms and iterates in 1D
99 //    a larger kernel version that stores the matrix in a 1D texture. The texture version has small
100 //    and large variants w/ the actual kernel size uploaded as a uniform.
make_matrix_conv_effect(MatrixConvolutionImpl impl,const SkRuntimeEffect::Options & options)101 SkRuntimeEffect* make_matrix_conv_effect(MatrixConvolutionImpl impl,
102                                          const SkRuntimeEffect::Options& options) {
103     // While the uniforms and kernel access are different, pieces of the algorithm are common and
104     // defined statically for re-use in the two shaders:
105     static const char* kHeaderAndBeginLoopSkSL =
106         "uniform int2 size;"
107         "uniform int2 offset;"
108         "uniform half2 gainAndBias;"
109         "uniform int convolveAlpha;" // FIXME not a full  int? Put in a half3 w/ gainAndBias?
110 
111         "uniform shader child;"
112 
113         "half4 main(float2 coord) {"
114             "half4 sum = half4(0);"
115             "half origAlpha = 0;"
116             "int2 kernelPos = int2(0);"
117             "for (int i = 0; i < kMaxKernelSize; ++i) {"
118                 "if (kernelPos.y >= size.y) { break; }";
119 
120     // Used in the inner loop to accumulate convolution sum and increment the kernel position
121     static const char* kAccumulateAndIncrementSkSL =
122                 "half4 c = child.eval(coord + half2(kernelPos) - half2(offset));"
123                 "if (convolveAlpha == 0) {"
124                     // When not convolving alpha, remember the original alpha for actual sample
125                     // coord, and perform accumulation on unpremul colors.
126                     "if (kernelPos == offset) {"
127                         "origAlpha = c.a;"
128                     "}"
129                     "c = unpremul(c);"
130                 "}"
131                 "sum += c*k;"
132                 "kernelPos.x += 1;"
133                 "if (kernelPos.x >= size.x) {"
134                     "kernelPos.x = 0;"
135                     "kernelPos.y += 1;"
136                 "}";
137 
138     // Closes the loop and calculates final color
139     static const char* kCloseLoopAndFooterSkSL =
140             "}"
141             "half4 color = sum*gainAndBias.x + gainAndBias.y;"
142             "if (convolveAlpha == 0) {"
143                 // Reset the alpha to the original and convert to premul RGB
144                 "color = half4(color.rgb*origAlpha, origAlpha);"
145             "} else {"
146                 // Ensure convolved alpha is within [0, 1]
147                 "color.a = saturate(color.a);"
148             "}"
149             // Make RGB valid premul w/ respect to the alpha (either original or convolved)
150             "color.rgb = clamp(color.rgb, 0, color.a);"
151             "return color;"
152         "}";
153 
154     static const auto makeTextureEffect = [](int maxTextureKernelSize,
155                                              const SkRuntimeEffect::Options& options) {
156         return SkMakeRuntimeEffect(
157                         SkRuntimeEffect::MakeForShader,
158                         SkStringPrintf("const int kMaxKernelSize = %d;"
159                                        "uniform shader kernel;"
160                                        "uniform half2 innerGainAndBias;"
161                                        "%s" // kHeaderAndBeginLoopSkSL
162                                                "half k = kernel.eval(half2(half(i) + 0.5, 0.5)).a;"
163                                                "k = k * innerGainAndBias.x + innerGainAndBias.y;"
164                                                "%s" // kAccumulateAndIncrementSkSL
165                                        "%s", // kCloseLoopAndFooterSkSL
166                                        maxTextureKernelSize,
167                                        kHeaderAndBeginLoopSkSL,
168                                        kAccumulateAndIncrementSkSL,
169                                        kCloseLoopAndFooterSkSL).c_str(),
170                         options);
171     };
172 
173     switch (impl) {
174         case MatrixConvolutionImpl::kUniformBased: {
175             return SkMakeRuntimeEffect(
176                         SkRuntimeEffect::MakeForShader,
177                         SkStringPrintf("const int kMaxKernelSize = %d / 4;"
178                                        "uniform half4 kernel[kMaxKernelSize];"
179                                        "%s" // kHeaderAndBeginLoopSkSL
180                                                 "half4 k4 = kernel[i];"
181                                                 "for (int j = 0; j < 4; ++j) {"
182                                                     "if (kernelPos.y >= size.y) { break; }"
183                                                     "half k = k4[j];"
184                                                     "%s" // kAccumulateAndIncrementSkSL
185                                                 "}"
186                                        "%s", // kCloseLoopAndFooterSkSL
187                                        MatrixConvolutionImageFilter::kMaxUniformKernelSize,
188                                        kHeaderAndBeginLoopSkSL,
189                                        kAccumulateAndIncrementSkSL,
190                                        kCloseLoopAndFooterSkSL).c_str(),
191                         options);
192         }
193         case MatrixConvolutionImpl::kTextureBasedSm:
194             return makeTextureEffect(MatrixConvolutionImageFilter::kSmallKernelSize, options);
195         case MatrixConvolutionImpl::kTextureBasedLg:
196             return makeTextureEffect(MatrixConvolutionImageFilter::kLargeKernelSize, options);
197     }
198 
199     SkUNREACHABLE;
200 }
201 
202 } // anonymous namespace
203 
GetKnownRuntimeEffect(StableKey stableKey)204 const SkRuntimeEffect* GetKnownRuntimeEffect(StableKey stableKey) {
205     SkRuntimeEffect::Options options;
206     SkRuntimeEffectPriv::SetStableKey(&options, static_cast<uint32_t>(stableKey));
207     SkRuntimeEffectPriv::AllowPrivateAccess(&options);
208 
209     switch (stableKey) {
210         case StableKey::kInvalid:
211             return nullptr;
212 
213         // Shaders
214         case StableKey::k1DBlur4: {
215             static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(4, options);
216             return s1DBlurEffect;
217         }
218         case StableKey::k1DBlur8: {
219             static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(8, options);
220             return s1DBlurEffect;
221         }
222         case StableKey::k1DBlur12: {
223             static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(12, options);
224             return s1DBlurEffect;
225         }
226         case StableKey::k1DBlur16: {
227             static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(16, options);
228             return s1DBlurEffect;
229         }
230         case StableKey::k1DBlur20: {
231             static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(20, options);
232             return s1DBlurEffect;
233         }
234         case StableKey::k1DBlur28: {
235             static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(28, options);
236             return s1DBlurEffect;
237         }
238         case StableKey::k2DBlur4: {
239             static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(4, options);
240             return s2DBlurEffect;
241         }
242         case StableKey::k2DBlur8: {
243             static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(8, options);
244             return s2DBlurEffect;
245         }
246         case StableKey::k2DBlur12: {
247             static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(12, options);
248             return s2DBlurEffect;
249         }
250         case StableKey::k2DBlur16: {
251             static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(16, options);
252             return s2DBlurEffect;
253         }
254         case StableKey::k2DBlur20: {
255             static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(20, options);
256             return s2DBlurEffect;
257         }
258         case StableKey::k2DBlur28: {
259             static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(28, options);
260             return s2DBlurEffect;
261         }
262         case StableKey::kBlend: {
263             static constexpr char kBlendShaderCode[] =
264                 "uniform shader s, d;"
265                 "uniform blender b;"
266                 "half4 main(float2 xy) {"
267                     "return b.eval(s.eval(xy), d.eval(xy));"
268                 "}";
269 
270             static const SkRuntimeEffect* sBlendEffect =
271                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
272                                         kBlendShaderCode,
273                                         options);
274             return sBlendEffect;
275         }
276         case StableKey::kLerp: {
277             static constexpr char kLerpFilterCode[] =
278                 "uniform colorFilter cf0;"
279                 "uniform colorFilter cf1;"
280                 "uniform half weight;"
281 
282                 "half4 main(half4 color) {"
283                     "return mix(cf0.eval(color), cf1.eval(color), weight);"
284                 "}";
285 
286             static const SkRuntimeEffect* sLerpEffect =
287                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
288                                         kLerpFilterCode,
289                                         options);
290             return sLerpEffect;
291         }
292         case StableKey::kMatrixConvUniforms: {
293             static const SkRuntimeEffect* sMatrixConvUniformsEffect =
294                     make_matrix_conv_effect(MatrixConvolutionImpl::kUniformBased, options);
295             return sMatrixConvUniformsEffect;
296         }
297 
298         case StableKey::kMatrixConvTexSm: {
299             static const SkRuntimeEffect* sMatrixConvTexSmEffect =
300                     make_matrix_conv_effect(MatrixConvolutionImpl::kTextureBasedSm, options);
301             return sMatrixConvTexSmEffect;
302         }
303 
304         case StableKey::kMatrixConvTexLg: {
305             static const SkRuntimeEffect* sMatrixConvTexMaxEffect =
306                     make_matrix_conv_effect(MatrixConvolutionImpl::kTextureBasedLg, options);
307             return sMatrixConvTexMaxEffect;
308         }
309         case StableKey::kDecal: {
310             static constexpr char kDecalShaderCode[] =
311                 "uniform shader image;"
312                 "uniform float4 decalBounds;"
313 
314                 "half4 main(float2 coord) {"
315                     "return sk_decal(image, coord, decalBounds);"
316                 "}";
317 
318             static const SkRuntimeEffect* sDecalEffect =
319                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
320                                         kDecalShaderCode,
321                                         options);
322             return sDecalEffect;
323         }
324         case StableKey::kDisplacement: {
325             // NOTE: This uses dot product selection to work on all GLES2 hardware (enforced by
326             // public runtime effect restrictions). Otherwise, this would use a "uniform ivec2"
327             // and component indexing to convert the displacement color into a vector.
328             static constexpr char kDisplacementShaderCode[] =
329                 "uniform shader displMap;"
330                 "uniform shader colorMap;"
331                 "uniform half2 scale;"
332                 "uniform half4 xSelect;" // Only one of RGBA will be 1, the rest are 0
333                 "uniform half4 ySelect;"
334 
335                 "half4 main(float2 coord) {"
336                     "return sk_displacement(displMap, colorMap, coord, scale, xSelect, ySelect);"
337                 "}";
338 
339             static const SkRuntimeEffect* sDisplacementEffect =
340                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
341                                         kDisplacementShaderCode,
342                                         options);
343             return sDisplacementEffect;
344         }
345         case StableKey::kLighting: {
346             static constexpr char kLightingShaderCode[] =
347                 "uniform shader normalMap;"
348 
349                 // Packs surface depth, shininess, material type (0 == diffuse) and light type
350                 // (< 0 = distant, 0 = point, > 0 = spot)
351                 "uniform half4 materialAndLightType;"
352 
353                 "uniform half4 lightPosAndSpotFalloff;" // (x,y,z) are lightPos, w is spot falloff
354                                                         // exponent
355                 "uniform half4 lightDirAndSpotCutoff;" // (x,y,z) are lightDir,
356                                                        // w is spot cos(cutoffAngle)
357                 "uniform half3 lightColor;" // Material's k has already been multiplied in
358 
359                 "half4 main(float2 coord) {"
360                     "return sk_lighting(normalMap, coord,"
361                                         /*depth=*/"materialAndLightType.x,"
362                                         /*shininess=*/"materialAndLightType.y,"
363                                         /*materialType=*/"materialAndLightType.z,"
364                                         /*lightType=*/"materialAndLightType.w,"
365                                         /*lightPos=*/"lightPosAndSpotFalloff.xyz,"
366                                         /*spotFalloff=*/"lightPosAndSpotFalloff.w,"
367                                         /*lightDir=*/"lightDirAndSpotCutoff.xyz,"
368                                         /*cosCutoffAngle=*/"lightDirAndSpotCutoff.w,"
369                                         "lightColor);"
370                 "}";
371 
372             static const SkRuntimeEffect* sLightingEffect =
373                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
374                                         kLightingShaderCode,
375                                         options);
376             return sLightingEffect;
377         }
378         case StableKey::kLinearMorphology: {
379             static constexpr char kLinearMorphologyShaderCode[] =
380                 "uniform shader child;"
381                 "uniform half2 offset;"
382                 "uniform half flip;" // -1 converts the max() calls to min()
383                 "uniform int radius;"
384 
385                 "half4 main(float2 coord) {"
386                     "return sk_linear_morphology(child, coord, offset, flip, radius);"
387                 "}";
388 
389             static const SkRuntimeEffect* sLinearMorphologyEffect =
390                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
391                                         kLinearMorphologyShaderCode,
392                                         options);
393             return sLinearMorphologyEffect;
394         }
395 
396         case StableKey::kMagnifier: {
397             static constexpr char kMagnifierShaderCode[] =
398                 "uniform shader src;"
399                 "uniform float4 lensBounds;"
400                 "uniform float4 zoomXform;"
401                 "uniform float2 invInset;"
402 
403                 "half4 main(float2 coord) {"
404                     "return sk_magnifier(src, coord, lensBounds, zoomXform, invInset);"
405                 "}";
406 
407             static const SkRuntimeEffect* sMagnifierEffect =
408                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
409                                         kMagnifierShaderCode,
410                                         options);
411             return sMagnifierEffect;
412         }
413         case StableKey::kNormal: {
414             static constexpr char kNormalShaderCode[] =
415                 "uniform shader alphaMap;"
416                 "uniform float4 edgeBounds;"
417                 "uniform half negSurfaceDepth;"
418 
419                 "half4 main(float2 coord) {"
420                    "return sk_normal(alphaMap, coord, edgeBounds, negSurfaceDepth);"
421                 "}";
422 
423             static const SkRuntimeEffect* sNormalEffect =
424                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
425                                         kNormalShaderCode,
426                                         options);
427             return sNormalEffect;
428         }
429         case StableKey::kSparseMorphology: {
430             static constexpr char kSparseMorphologyShaderCode[] =
431                 "uniform shader child;"
432                 "uniform half2 offset;"
433                 "uniform half flip;"
434 
435                 "half4 main(float2 coord) {"
436                     "return sk_sparse_morphology(child, coord, offset, flip);"
437                 "}";
438 
439             static const SkRuntimeEffect* sSparseMorphologyEffect =
440                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
441                                         kSparseMorphologyShaderCode,
442                                         options);
443             return sSparseMorphologyEffect;
444         }
445 
446         // Blenders
447         case StableKey::kArithmetic: {
448             static constexpr char kArithmeticBlenderCode[] =
449                 "uniform half4 k;"
450                 "uniform half pmClamp;"
451 
452                 "half4 main(half4 src, half4 dst) {"
453                     "return sk_arithmetic_blend(src, dst, k, pmClamp);"
454                 "}";
455 
456             static const SkRuntimeEffect* sArithmeticEffect =
457                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForBlender,
458                                         kArithmeticBlenderCode,
459                                         options);
460             return sArithmeticEffect;
461         }
462 
463         // Color Filters
464         case StableKey::kHighContrast: {
465             static constexpr char kHighContrastFilterCode[] =
466                 "uniform half grayscale, invertStyle, contrast;"
467                 "half4 main(half4 color) {"
468                     "return half4(sk_high_contrast(color.rgb, grayscale, invertStyle, contrast),"
469                                  "color.a);"
470                 "}";
471 
472             static const SkRuntimeEffect* sHighContrastEffect =
473                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
474                                         kHighContrastFilterCode,
475                                         options);
476             return sHighContrastEffect;
477         }
478 
479         case StableKey::kLuma: {
480             static constexpr char kLumaFilterCode[] =
481                 "half4 main(half4 color) {"
482                     "return sk_luma(color.rgb);"
483                 "}";
484 
485             static const SkRuntimeEffect* sLumaEffect =
486                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
487                                         kLumaFilterCode,
488                                         options);
489             return sLumaEffect;
490         }
491 
492         case StableKey::kOverdraw: {
493             static constexpr char kOverdrawFilterCode[] =
494                 "uniform half4 color0, color1, color2, color3, color4, color5;"
495 
496                 "half4 main(half4 color) {"
497                     "return sk_overdraw(color.a, color0, color1, color2, color3, color4, color5);"
498                 "}";
499 
500             static const SkRuntimeEffect* sOverdrawEffect =
501                     SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
502                                         kOverdrawFilterCode,
503                                         options);
504             return sOverdrawEffect;
505         }
506     }
507 
508     SkUNREACHABLE;
509 }
510 
511 } // namespace SkKnownRuntimeEffects
512