1 | /* |
2 | * Copyright 2019 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 SkRuntimeEffect_DEFINED |
9 | #define SkRuntimeEffect_DEFINED |
10 | |
11 | #include "include/core/SkData.h" |
12 | #include "include/core/SkMatrix.h" |
13 | #include "include/core/SkString.h" |
14 | #include "include/private/GrTypesPriv.h" |
15 | #include "include/private/SkSLSampleUsage.h" |
16 | |
17 | #include <string> |
18 | #include <vector> |
19 | |
20 | #if SK_SUPPORT_GPU |
21 | #include "include/gpu/GrContextOptions.h" |
22 | #endif |
23 | |
24 | class GrShaderCaps; |
25 | class SkColorFilter; |
26 | class SkShader; |
27 | |
28 | namespace SkSL { |
29 | class ByteCode; |
30 | struct PipelineStageArgs; |
31 | struct Program; |
32 | class SharedCompiler; |
33 | } // namespace SkSL |
34 | |
35 | /* |
36 | * SkRuntimeEffect supports creating custom SkShader and SkColorFilter objects using Skia's SkSL |
37 | * shading language. |
38 | * |
39 | * NOTE: This API is experimental and subject to change. |
40 | */ |
41 | class SK_API SkRuntimeEffect : public SkRefCnt { |
42 | public: |
43 | struct Uniform { |
44 | enum class Type { |
45 | kFloat, |
46 | kFloat2, |
47 | kFloat3, |
48 | kFloat4, |
49 | kFloat2x2, |
50 | kFloat3x3, |
51 | kFloat4x4, |
52 | }; |
53 | |
54 | enum Flags { |
55 | kArray_Flag = 0x1, |
56 | kMarker_Flag = 0x2, |
57 | kMarkerNormals_Flag = 0x4, |
58 | kSRGBUnpremul_Flag = 0x8, |
59 | }; |
60 | |
61 | SkString fName; |
62 | size_t fOffset; |
63 | Type fType; |
64 | GrSLType fGPUType; |
65 | int fCount; |
66 | uint32_t fFlags; |
67 | uint32_t fMarker; |
68 | |
69 | bool isArray() const { return SkToBool(fFlags & kArray_Flag); } |
70 | size_t sizeInBytes() const; |
71 | }; |
72 | |
73 | struct Varying { |
74 | SkString fName; |
75 | int fWidth; // 1 - 4 (floats) |
76 | }; |
77 | |
78 | // [Effect, ErrorText] |
79 | // If successful, Effect != nullptr, otherwise, ErrorText contains the reason for failure. |
80 | using EffectResult = std::tuple<sk_sp<SkRuntimeEffect>, SkString>; |
81 | static EffectResult Make(SkString sksl); |
82 | |
83 | sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms, |
84 | sk_sp<SkShader> children[], |
85 | size_t childCount, |
86 | const SkMatrix* localMatrix, |
87 | bool isOpaque); |
88 | |
89 | sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms); |
90 | sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms, |
91 | sk_sp<SkColorFilter> children[], |
92 | size_t childCount); |
93 | |
94 | const SkString& source() const { return fSkSL; } |
95 | uint32_t hash() const { return fHash; } |
96 | |
97 | template <typename T> |
98 | class ConstIterable { |
99 | public: |
100 | ConstIterable(const std::vector<T>& vec) : fVec(vec) {} |
101 | |
102 | using const_iterator = typename std::vector<T>::const_iterator; |
103 | |
104 | const_iterator begin() const { return fVec.begin(); } |
105 | const_iterator end() const { return fVec.end(); } |
106 | size_t count() const { return fVec.size(); } |
107 | |
108 | private: |
109 | const std::vector<T>& fVec; |
110 | }; |
111 | |
112 | // Combined size of all 'uniform' variables. When calling makeColorFilter or makeShader, |
113 | // provide an SkData of this size, containing values for all of those variables. |
114 | size_t uniformSize() const; |
115 | |
116 | ConstIterable<Uniform> uniforms() const { return ConstIterable<Uniform>(fUniforms); } |
117 | ConstIterable<SkString> children() const { return ConstIterable<SkString>(fChildren); } |
118 | ConstIterable<Varying> varyings() const { return ConstIterable<Varying>(fVaryings); } |
119 | |
120 | // Returns pointer to the named uniform variable's description, or nullptr if not found |
121 | const Uniform* findUniform(const char* name) const; |
122 | |
123 | // Returns index of the named child, or -1 if not found |
124 | int findChild(const char* name) const; |
125 | |
126 | bool usesSampleCoords() const { return fUsesSampleCoords; } |
127 | |
128 | static void RegisterFlattenables(); |
129 | ~SkRuntimeEffect() override; |
130 | |
131 | private: |
132 | SkRuntimeEffect(SkString sksl, |
133 | std::unique_ptr<SkSL::Program> baseProgram, |
134 | std::vector<Uniform>&& uniforms, |
135 | std::vector<SkString>&& children, |
136 | std::vector<SkSL::SampleUsage>&& sampleUsages, |
137 | std::vector<Varying>&& varyings, |
138 | bool usesSampleCoords, |
139 | bool allowColorFilter); |
140 | |
141 | #if SK_SUPPORT_GPU |
142 | friend class GrSkSLFP; // toPipelineStage |
143 | friend class GrGLSLSkSLFP; // fSampleUsages |
144 | |
145 | // This re-compiles the program from scratch, using the supplied shader caps. |
146 | // This is necessary to get the correct values of settings. |
147 | bool toPipelineStage(const GrShaderCaps* shaderCaps, |
148 | GrContextOptions::ShaderErrorHandler* errorHandler, |
149 | SkSL::PipelineStageArgs* outArgs); |
150 | #endif |
151 | |
152 | friend class SkRTShader; // toByteCode |
153 | friend class SkRuntimeColorFilter; // |
154 | |
155 | // [ByteCode, ErrorText] |
156 | // If successful, ByteCode != nullptr, otherwise, ErrorText contains the reason for failure. |
157 | using ByteCodeResult = std::tuple<std::unique_ptr<SkSL::ByteCode>, SkString>; |
158 | ByteCodeResult toByteCode() const; |
159 | |
160 | |
161 | uint32_t fHash; |
162 | SkString fSkSL; |
163 | |
164 | std::unique_ptr<SkSL::Program> fBaseProgram; |
165 | std::vector<Uniform> fUniforms; |
166 | std::vector<SkString> fChildren; |
167 | std::vector<SkSL::SampleUsage> fSampleUsages; |
168 | std::vector<Varying> fVaryings; |
169 | |
170 | bool fUsesSampleCoords; |
171 | bool fAllowColorFilter; |
172 | }; |
173 | |
174 | /** |
175 | * SkRuntimeShaderBuilder is a utility to simplify creating SkShader objects from SkRuntimeEffects. |
176 | * |
177 | * NOTE: Like SkRuntimeEffect, this API is experimental and subject to change! |
178 | * |
179 | * Given an SkRuntimeEffect, the SkRuntimeShaderBuilder manages creating an input data block and |
180 | * provides named access to the 'uniform' variables in that block, as well as named access |
181 | * to a list of child shader slots. Usage: |
182 | * |
183 | * sk_sp<SkRuntimeEffect> effect = ...; |
184 | * SkRuntimeShaderBuilder builder(effect); |
185 | * builder.uniform("some_uniform_float") = 3.14f; |
186 | * builder.uniform("some_uniform_matrix") = SkM44::Rotate(...); |
187 | * builder.child("some_child_effect") = mySkImage->makeShader(...); |
188 | * ... |
189 | * sk_sp<SkShader> shader = builder.makeShader(nullptr, false); |
190 | * |
191 | * Note that SkRuntimeShaderBuilder is built entirely on the public API of SkRuntimeEffect, |
192 | * so can be used as-is or serve as inspiration for other interfaces or binding techniques. |
193 | */ |
194 | struct SkRuntimeShaderBuilder { |
195 | SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect>); |
196 | ~SkRuntimeShaderBuilder(); |
197 | |
198 | struct BuilderUniform { |
199 | // Copy 'val' to this variable. No type conversion is performed - 'val' must be same |
200 | // size as expected by the effect. Information about the variable can be queried by |
201 | // looking at fVar. If the size is incorrect, no copy will be performed, and debug |
202 | // builds will abort. If this is the result of querying a missing variable, fVar will |
203 | // be nullptr, and assigning will also do nothing (and abort in debug builds). |
204 | template <typename T> |
205 | std::enable_if_t<std::is_trivially_copyable<T>::value, BuilderUniform&> operator=( |
206 | const T& val) { |
207 | if (!fVar) { |
208 | SkDEBUGFAIL("Assigning to missing variable" ); |
209 | } else if (sizeof(val) != fVar->sizeInBytes()) { |
210 | SkDEBUGFAIL("Incorrect value size" ); |
211 | } else { |
212 | memcpy(SkTAddOffset<void>(fOwner->fUniforms->writable_data(), fVar->fOffset), |
213 | &val, sizeof(val)); |
214 | } |
215 | return *this; |
216 | } |
217 | |
218 | BuilderUniform& operator=(const SkMatrix& val) { |
219 | if (!fVar) { |
220 | SkDEBUGFAIL("Assigning to missing variable" ); |
221 | } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { |
222 | SkDEBUGFAIL("Incorrect value size" ); |
223 | } else { |
224 | float* data = SkTAddOffset<float>(fOwner->fUniforms->writable_data(), |
225 | fVar->fOffset); |
226 | data[0] = val.get(0); data[1] = val.get(3); data[2] = val.get(6); |
227 | data[3] = val.get(1); data[4] = val.get(4); data[5] = val.get(7); |
228 | data[6] = val.get(2); data[7] = val.get(5); data[8] = val.get(8); |
229 | } |
230 | return *this; |
231 | } |
232 | |
233 | SkRuntimeShaderBuilder* fOwner; |
234 | const SkRuntimeEffect::Uniform* fVar; // nullptr if the variable was not found |
235 | }; |
236 | |
237 | struct BuilderChild { |
238 | BuilderChild& operator=(const sk_sp<SkShader>& val); |
239 | |
240 | SkRuntimeShaderBuilder* fOwner; |
241 | int fIndex; // -1 if the child was not found |
242 | }; |
243 | |
244 | BuilderUniform uniform(const char* name) { return { this, fEffect->findUniform(name) }; } |
245 | BuilderChild child(const char* name) { return { this, fEffect->findChild(name) }; } |
246 | |
247 | sk_sp<SkShader> makeShader(const SkMatrix* localMatrix, bool isOpaque); |
248 | |
249 | sk_sp<SkRuntimeEffect> fEffect; |
250 | sk_sp<SkData> fUniforms; |
251 | std::vector<sk_sp<SkShader>> fChildren; |
252 | }; |
253 | |
254 | #endif |
255 | |