/src/skia/src/core/SkRuntimeEffectPriv.h
Line | Count | Source (jump to first uncovered line) |
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. |
70 | 88.6k | static bool SupportsConstantOutputForConstantInput(const SkRuntimeEffect* effect) { |
71 | | // This optimization is only implemented for color filters without any children. |
72 | 88.6k | if (!effect->allowColorFilter() || !effect->children().empty()) { |
73 | 58.5k | return false; |
74 | 58.5k | } |
75 | 30.0k | return true; |
76 | 88.6k | } |
77 | | |
78 | 0 | static uint32_t Hash(const SkRuntimeEffect& effect) { |
79 | 0 | return effect.hash(); |
80 | 0 | } |
81 | | |
82 | 0 | static uint32_t StableKey(const SkRuntimeEffect& effect) { |
83 | 0 | return effect.fStableKey; |
84 | 0 | } |
85 | | |
86 | 0 | static const SkSL::Program& Program(const SkRuntimeEffect& effect) { |
87 | 0 | return *effect.fBaseProgram; |
88 | 0 | } |
89 | | |
90 | 0 | static SkRuntimeEffect::Options ES3Options() { |
91 | 0 | SkRuntimeEffect::Options options; |
92 | 0 | options.maxVersionAllowed = SkSL::Version::k300; |
93 | 0 | return options; |
94 | 0 | } |
95 | | |
96 | 9.45k | static void AllowPrivateAccess(SkRuntimeEffect::Options* options) { |
97 | 9.45k | options->allowPrivateAccess = true; |
98 | 9.45k | } |
99 | | |
100 | 62.2k | static void SetStableKey(SkRuntimeEffect::Options* options, uint32_t stableKey) { |
101 | 62.2k | options->fStableKey = stableKey; |
102 | 62.2k | } |
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 | | |
137 | 0 | static bool UsesColorTransform(const SkRuntimeEffect* effect) { |
138 | 0 | return effect->usesColorTransform(); |
139 | 0 | } |
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 | | |
154 | | inline sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect( |
155 | | SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&), |
156 | 0 | const char* sksl) { |
157 | 0 | return SkMakeCachedRuntimeEffect(make, SkString{sksl}); |
158 | 0 | } |
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 | 88 | 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 | 88 | SkRuntimeEffectPriv::AllowPrivateAccess(&options); |
175 | 88 | auto result = make(SkString{sksl}, options); |
176 | 88 | if (!result.effect) { |
177 | 0 | SK_ABORT("%s", result.errorText.c_str()); |
178 | 0 | } |
179 | 88 | return result.effect.release(); |
180 | 88 | } |
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. |
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 | 49.6k | , 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 |