1 /*
2 * Copyright 2020 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 SkRuntimeEffectPriv_DEFINED
9 #define SkRuntimeEffectPriv_DEFINED
10
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "include/private/SkColorData.h"
13 #include "src/core/SkVM.h"
14
15 #include <functional>
16
17 #ifdef SK_ENABLE_SKSL
18
19 class SkRuntimeEffectPriv {
20 public:
21 // Helper function when creating an effect for a GrSkSLFP that verifies an effect will
22 // implement the constant output for constant input optimization flag.
SupportsConstantOutputForConstantInput(sk_sp<SkRuntimeEffect> effect)23 static bool SupportsConstantOutputForConstantInput(sk_sp<SkRuntimeEffect> effect) {
24 return effect->getFilterColorProgram();
25 }
26
ES3Options()27 static SkRuntimeEffect::Options ES3Options() {
28 SkRuntimeEffect::Options options;
29 options.enforceES2Restrictions = false;
30 return options;
31 }
32
EnableFragCoord(SkRuntimeEffect::Options * options)33 static void EnableFragCoord(SkRuntimeEffect::Options* options) {
34 options->allowFragCoord = true;
35 }
36 };
37
38 // These internal APIs for creating runtime effects vary from the public API in two ways:
39 //
40 // 1) they're used in contexts where it's not useful to receive an error message;
41 // 2) they're cached.
42 //
43 // Users of the public SkRuntimeEffect::Make*() can of course cache however they like themselves;
44 // keeping these APIs private means users will not be forced into our cache or cache policy.
45
46 sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (*make)(SkString sksl),
47 SkString sksl);
48
SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (* make)(SkString),const char * sksl)49 inline sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (*make)(SkString),
50 const char* sksl) {
51 return SkMakeCachedRuntimeEffect(make, SkString{sksl});
52 }
53
54 // Internal API that assumes (and asserts) that the shader code is valid, but does no internal
55 // caching. Used when the caller will cache the result in a static variable.
56 inline sk_sp<SkRuntimeEffect> SkMakeRuntimeEffect(
57 SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&),
58 const char* sksl,
59 SkRuntimeEffect::Options options = SkRuntimeEffect::Options{}) {
60 SkRuntimeEffectPriv::EnableFragCoord(&options);
61 auto result = make(SkString{sksl}, options);
62 SkASSERTF(result.effect, "%s", result.errorText.c_str());
63 return result.effect;
64 }
65
66 // This is mostly from skvm's rgb->hsl code, with some GPU-related finesse pulled from
67 // GrHighContrastFilterEffect.fp, see next comment.
68 inline constexpr char kRGB_to_HSL_sksl[] =
69 "half3 rgb_to_hsl(half3 c) {"
70 "half mx = max(max(c.r,c.g),c.b),"
71 " mn = min(min(c.r,c.g),c.b),"
72 " d = mx-mn, "
73 " invd = 1.0 / d, "
74 " g_lt_b = c.g < c.b ? 6.0 : 0.0;"
75
76 // We'd prefer to write these tests like `mx == c.r`, but on some GPUs max(x,y) is
77 // not always equal to either x or y. So we use long form, c.r >= c.g && c.r >= c.b.
78 "half h = (1/6.0) * (mx == mn ? 0.0 :"
79 " /*mx==c.r*/ c.r >= c.g && c.r >= c.b ? invd * (c.g - c.b) + g_lt_b :"
80 " /*mx==c.g*/ c.g >= c.b ? invd * (c.b - c.r) + 2.0 "
81 " /*mx==c.b*/ : invd * (c.r - c.g) + 4.0);"
82
83 "half sum = mx+mn,"
84 " l = sum * 0.5,"
85 " s = mx == mn ? 0.0"
86 " : d / (l > 0.5 ? 2.0 - sum : sum);"
87 "return half3(h,s,l);"
88 "}";
89
90 //This is straight out of GrHSLToRGBFilterEffect.fp.
91 inline constexpr char kHSL_to_RGB_sksl[] =
92 "half3 hsl_to_rgb(half3 hsl) {"
93 "half C = (1 - abs(2 * hsl.z - 1)) * hsl.y;"
94 "half3 p = hsl.xxx + half3(0, 2/3.0, 1/3.0);"
95 "half3 q = saturate(abs(fract(p) * 6 - 3) - 1);"
96 "return (q - 0.5) * C + hsl.z;"
97 "}";
98
99 /**
100 * Runtime effects are often long lived & cached. Individual color filters or FPs created from them
101 * and are often short-lived. However, color filters and FPs may need to operate on a single color
102 * (on the CPU). This may be done at the paint level (eg, filter the paint color), or as part of
103 * FP tree analysis.
104 *
105 * SkFilterColorProgram is an skvm program representing a (color filter) SkRuntimeEffect. It can
106 * process a single color, without knowing the details of a particular instance (uniform values or
107 * children).
108 */
109 class SkFilterColorProgram {
110 public:
111 static std::unique_ptr<SkFilterColorProgram> Make(const SkRuntimeEffect* effect);
112
113 SkPMColor4f eval(const SkPMColor4f& inColor,
114 const void* uniformData,
115 std::function<SkPMColor4f(int, SkPMColor4f)> evalChild) const;
116
isAlphaUnchanged()117 bool isAlphaUnchanged() const { return fAlphaUnchanged; }
118
119 private:
120 struct SampleCall {
121 enum class Kind {
122 kInputColor, // eg child.eval(inputColor)
123 kImmediate, // eg child.eval(half4(1))
124 kPrevious, // eg child1.eval(child2.eval(...))
125 kUniform, // eg uniform half4 color; ... child.eval(color)
126 };
127
128 int fChild;
129 Kind fKind;
130 union {
131 SkPMColor4f fImm; // for kImmediate
132 int fPrevious; // for kPrevious
133 int fOffset; // for kUniform
134 };
135 };
136
137 SkFilterColorProgram(skvm::Program program,
138 std::vector<SampleCall> sampleCalls,
139 bool alphaUnchanged);
140
141 skvm::Program fProgram;
142 std::vector<SampleCall> fSampleCalls;
143 bool fAlphaUnchanged;
144 };
145
146 #endif // SK_ENABLE_SKSL
147
148 #endif // SkRuntimeEffectPriv_DEFINED
149