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/core/SkColor.h"
12 #include "include/core/SkRefCnt.h"
13 #include "include/core/SkString.h"
14 #include "include/effects/SkRuntimeEffect.h"
15 #include "include/private/SkSLSampleUsage.h"
16 #include "include/private/base/SkAssert.h"
17 #include "include/private/base/SkDebug.h"
18 #include "include/private/base/SkSpan_impl.h"
19 #include "include/private/base/SkTArray.h"
20 #include "src/core/SkEffectPriv.h"
21 #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
22
23 #include <cstddef>
24 #include <cstdint>
25 #include <functional>
26 #include <memory>
27
28 #include "include/sksl/SkSLVersion.h"
29
30 class SkArenaAlloc;
31 class SkCapabilities;
32 class SkColorSpace;
33 class SkData;
34 class SkMatrix;
35 class SkReadBuffer;
36 class SkShader;
37 class SkWriteBuffer;
38 struct SkColorSpaceXformSteps;
39
40 namespace SkShaders {
41 class MatrixRec;
42 }
43
44 namespace SkSL {
45 class Context;
46 class Variable;
47 struct Program;
48 }
49
50 class SkRuntimeEffectPriv {
51 public:
52 struct UniformsCallbackContext {
53 const SkColorSpace* fDstColorSpace;
54 };
55
56 // Private (experimental) API for creating runtime shaders with late-bound uniforms.
57 // The callback must produce a uniform data blob of the correct size for the effect.
58 // It is invoked at "draw" time (essentially, when a draw call is made against the canvas
59 // using the resulting shader). There are no strong guarantees about timing.
60 // Serializing the resulting shader will immediately invoke the callback (and record the
61 // resulting uniforms).
62 using UniformsCallback = std::function<sk_sp<const SkData>(const UniformsCallbackContext&)>;
63 static sk_sp<SkShader> MakeDeferredShader(const SkRuntimeEffect* effect,
64 UniformsCallback uniformsCallback,
65 SkSpan<const SkRuntimeEffect::ChildPtr> children,
66 const SkMatrix* localMatrix = nullptr);
67
68 // Helper function when creating an effect for a GrSkSLFP that verifies an effect will
69 // implement the GrFragmentProcessor "constant output for constant input" optimization flag.
SupportsConstantOutputForConstantInput(const SkRuntimeEffect * effect)70 static bool SupportsConstantOutputForConstantInput(const SkRuntimeEffect* effect) {
71 // This optimization is only implemented for color filters without any children.
72 if (!effect->allowColorFilter() || !effect->children().empty()) {
73 return false;
74 }
75 return true;
76 }
77
Hash(const SkRuntimeEffect & effect)78 static uint32_t Hash(const SkRuntimeEffect& effect) {
79 return effect.hash();
80 }
81
StableKey(const SkRuntimeEffect & effect)82 static uint32_t StableKey(const SkRuntimeEffect& effect) {
83 return effect.fStableKey;
84 }
85
Program(const SkRuntimeEffect & effect)86 static const SkSL::Program& Program(const SkRuntimeEffect& effect) {
87 return *effect.fBaseProgram;
88 }
89
ES3Options()90 static SkRuntimeEffect::Options ES3Options() {
91 SkRuntimeEffect::Options options;
92 options.maxVersionAllowed = SkSL::Version::k300;
93 return options;
94 }
95
AllowPrivateAccess(SkRuntimeEffect::Options * options)96 static void AllowPrivateAccess(SkRuntimeEffect::Options* options) {
97 options->allowPrivateAccess = true;
98 }
99
SetStableKey(SkRuntimeEffect::Options * options,uint32_t stableKey)100 static void SetStableKey(SkRuntimeEffect::Options* options, uint32_t stableKey) {
101 options->fStableKey = stableKey;
102 }
103
104 static SkRuntimeEffect::Uniform VarAsUniform(const SkSL::Variable&,
105 const SkSL::Context&,
106 size_t* offset);
107
108 static SkRuntimeEffect::Child VarAsChild(const SkSL::Variable& var,
109 int index);
110
111 static const char* ChildTypeToStr(SkRuntimeEffect::ChildType type);
112
113 // If there are layout(color) uniforms then this performs color space transformation on the
114 // color values and returns a new SkData. Otherwise, the original data is returned.
115 static sk_sp<const SkData> TransformUniforms(SkSpan<const SkRuntimeEffect::Uniform> uniforms,
116 sk_sp<const SkData> originalData,
117 const SkColorSpaceXformSteps&);
118 static sk_sp<const SkData> TransformUniforms(SkSpan<const SkRuntimeEffect::Uniform> uniforms,
119 sk_sp<const SkData> originalData,
120 const SkColorSpace* dstCS);
121 static SkSpan<const float> UniformsAsSpan(
122 SkSpan<const SkRuntimeEffect::Uniform> uniforms,
123 sk_sp<const SkData> originalData,
124 bool alwaysCopyIntoAlloc,
125 const SkColorSpace* destColorSpace,
126 SkArenaAlloc* alloc);
127
128 static bool CanDraw(const SkCapabilities*, const SkSL::Program*);
129 static bool CanDraw(const SkCapabilities*, const SkRuntimeEffect*);
130
131 static bool ReadChildEffects(SkReadBuffer& buffer,
132 const SkRuntimeEffect* effect,
133 skia_private::TArray<SkRuntimeEffect::ChildPtr>* children);
134 static void WriteChildEffects(SkWriteBuffer& buffer,
135 SkSpan<const SkRuntimeEffect::ChildPtr> children);
136
UsesColorTransform(const SkRuntimeEffect * effect)137 static bool UsesColorTransform(const SkRuntimeEffect* effect) {
138 return effect->usesColorTransform();
139 }
140 };
141
142 // These internal APIs for creating runtime effects vary from the public API in two ways:
143 //
144 // 1) they're used in contexts where it's not useful to receive an error message;
145 // 2) they're cached.
146 //
147 // Users of the public SkRuntimeEffect::Make*() can of course cache however they like themselves;
148 // keeping these APIs private means users will not be forced into our cache or cache policy.
149
150 sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(
151 SkRuntimeEffect::Result (*make)(SkString sksl, const SkRuntimeEffect::Options&),
152 SkString sksl);
153
SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (* make)(SkString,const SkRuntimeEffect::Options &),const char * sksl)154 inline sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(
155 SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&),
156 const char* sksl) {
157 return SkMakeCachedRuntimeEffect(make, SkString{sksl});
158 }
159
160 // Internal API that assumes (and asserts) that the shader code is valid, but does no internal
161 // caching. Used when the caller will cache the result in a static variable. Ownership is passed to
162 // the caller; the effect will be leaked if it the pointer is not stored or explicitly deleted.
163 inline SkRuntimeEffect* SkMakeRuntimeEffect(
164 SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&),
165 const char* sksl,
166 SkRuntimeEffect::Options options = SkRuntimeEffect::Options{}) {
167 #if defined(SK_DEBUG)
168 // Our SKSL snippets we embed in Skia should not have comments or excess indentation.
169 // Removing them helps trim down code size and speeds up parsing
170 if (SkStrContains(sksl, "//") || SkStrContains(sksl, " ")) {
171 SkDEBUGFAILF("Found SkSL snippet that can be minified: \n %s\n", sksl);
172 }
173 #endif
174 SkRuntimeEffectPriv::AllowPrivateAccess(&options);
175 auto result = make(SkString{sksl}, options);
176 if (!result.effect) {
177 SK_ABORT("%s", result.errorText.c_str());
178 }
179 return result.effect.release();
180 }
181
182 class RuntimeEffectRPCallbacks : public SkSL::RP::Callbacks {
183 public:
184 // SkStageRec::fPaintColor is used (strictly) to tint alpha-only image shaders with the paint
185 // color. We want to suppress that behavior when they're sampled from runtime effects, so we
186 // just override the paint color here. See also: SkImageShader::appendStages.
RuntimeEffectRPCallbacks(const SkStageRec & s,const SkShaders::MatrixRec & m,SkSpan<const SkRuntimeEffect::ChildPtr> c,SkSpan<const SkSL::SampleUsage> u)187 RuntimeEffectRPCallbacks(const SkStageRec& s,
188 const SkShaders::MatrixRec& m,
189 SkSpan<const SkRuntimeEffect::ChildPtr> c,
190 SkSpan<const SkSL::SampleUsage> u)
191 : fStage{s.fPipeline,
192 s.fAlloc,
193 s.fDstColorType,
194 s.fDstCS,
195 SkColors::kTransparent,
196 s.fSurfaceProps}
197 , fMatrix(m)
198 , fChildren(c)
199 , fSampleUsages(u) {}
200
201 bool appendShader(int index) override;
202 bool appendColorFilter(int index) override;
203 bool appendBlender(int index) override;
204
205 // TODO: If an effect calls these intrinsics more than once, we could cache and re-use the steps
206 // object(s), rather than re-creating them in the arena repeatedly.
207 void toLinearSrgb(const void* color) override;
208
209 void fromLinearSrgb(const void* color) override;
210
211 private:
212 void applyColorSpaceXform(const SkColorSpaceXformSteps& tempXform, const void* color);
213
214 const SkStageRec fStage;
215 const SkShaders::MatrixRec& fMatrix;
216 SkSpan<const SkRuntimeEffect::ChildPtr> fChildren;
217 SkSpan<const SkSL::SampleUsage> fSampleUsages;
218 };
219
220 #endif // SkRuntimeEffectPriv_DEFINED
221