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