• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkBlender.h"
12 #include "include/core/SkColorFilter.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkMatrix.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkSpan.h"
18 #include "include/core/SkString.h"
19 #include "include/private/SkOnce.h"
20 #include "include/private/SkSLSampleUsage.h"
21 #include "include/private/SkTOptional.h"
22 
23 #include <string>
24 #include <vector>
25 
26 #ifdef SK_ENABLE_SKSL
27 
28 class GrRecordingContext;
29 class SkFilterColorProgram;
30 class SkImage;
31 class SkRuntimeImageFilter;
32 
33 namespace SkSL {
34 class DebugTrace;
35 class ErrorReporter;
36 class FunctionDefinition;
37 struct Program;
38 enum class ProgramKind : int8_t;
39 struct ProgramSettings;
40 }  // namespace SkSL
41 
42 namespace skvm {
43 class Program;
44 }  // namespace skvm
45 
46 /*
47  * SkRuntimeEffect supports creating custom SkShader and SkColorFilter objects using Skia's SkSL
48  * shading language.
49  *
50  * NOTE: This API is experimental and subject to change.
51  */
52 class SK_API SkRuntimeEffect : public SkRefCnt {
53 public:
54     // Reflected description of a uniform variable in the effect's SkSL
55     struct Uniform {
56         enum class Type {
57             kFloat,
58             kFloat2,
59             kFloat3,
60             kFloat4,
61             kFloat2x2,
62             kFloat3x3,
63             kFloat4x4,
64             kInt,
65             kInt2,
66             kInt3,
67             kInt4,
68         };
69 
70         enum Flags {
71             // Uniform is an declared as an array. 'count' contains array length.
72             kArray_Flag = 0x1,
73 
74             // Uniform is declared with layout(color). Colors should be supplied as unpremultiplied,
75             // extended-range (unclamped) sRGB (ie SkColor4f). The uniform will be automatically
76             // transformed to unpremultiplied extended-range working-space colors.
77             kColor_Flag = 0x2,
78         };
79 
80         SkString  name;
81         size_t    offset;
82         Type      type;
83         int       count;
84         uint32_t  flags;
85 
isArrayUniform86         bool isArray() const { return SkToBool(this->flags & kArray_Flag); }
isColorUniform87         bool isColor() const { return SkToBool(this->flags & kColor_Flag); }
88         size_t sizeInBytes() const;
89     };
90 
91     // Reflected description of a uniform child (shader or colorFilter) in the effect's SkSL
92     enum class ChildType {
93         kShader,
94         kColorFilter,
95         kBlender,
96     };
97 
98     struct Child {
99         SkString  name;
100         ChildType type;
101         int       index;
102     };
103 
104     class Options {
105     public:
106         // For testing purposes, completely disable the inliner. (Normally, Runtime Effects don't
107         // run the inliner directly, but they still get an inlining pass once they are painted.)
108         bool forceNoInline = false;
109 
110     private:
111         friend class SkRuntimeEffect;
112         friend class SkRuntimeEffectPriv;
113 
114         // This flag lifts the ES2 restrictions on Runtime Effects that are gated by the
115         // `strictES2Mode` check. Be aware that the software renderer and pipeline-stage effect are
116         // still largely ES3-unaware and can still fail or crash if post-ES2 features are used.
117         // This is only intended for use by tests and certain internally created effects.
118         bool enforceES2Restrictions = true;
119 
120         // Similarly: Public SkSL does not allow access to sk_FragCoord. The semantics of that
121         // variable are confusing, and expose clients to implementation details of saveLayer and
122         // image filters.
123         bool allowFragCoord = false;
124     };
125 
126     // If the effect is compiled successfully, `effect` will be non-null.
127     // Otherwise, `errorText` will contain the reason for failure.
128     struct Result {
129         sk_sp<SkRuntimeEffect> effect;
130         SkString errorText;
131     };
132 
133     // MakeForColorFilter and MakeForShader verify that the SkSL code is valid for those stages of
134     // the Skia pipeline. In all of the signatures described below, color parameters and return
135     // values are flexible. They are listed as being 'vec4', but they can also be 'half4' or
136     // 'float4'. ('vec4' is an alias for 'float4').
137 
138     // We can't use a default argument for `options` due to a bug in Clang.
139     // https://bugs.llvm.org/show_bug.cgi?id=36684
140 
141     // Color filter SkSL requires an entry point that looks like:
142     //     vec4 main(vec4 inColor) { ... }
143     static Result MakeForColorFilter(SkString sksl, const Options&);
MakeForColorFilter(SkString sksl)144     static Result MakeForColorFilter(SkString sksl) {
145         return MakeForColorFilter(std::move(sksl), Options{});
146     }
147 
148     // Shader SkSL requires an entry point that looks like:
149     //     vec4 main(vec2 inCoords) { ... }
150     //   -or-
151     //     vec4 main(vec2 inCoords, vec4 inColor) { ... }
152     //
153     // Most shaders don't use the input color, so that parameter is optional.
154     static Result MakeForShader(SkString sksl, const Options&);
MakeForShader(SkString sksl)155     static Result MakeForShader(SkString sksl) {
156         return MakeForShader(std::move(sksl), Options{});
157     }
158 
159     // Blend SkSL requires an entry point that looks like:
160     //     vec4 main(vec4 srcColor, vec4 dstColor) { ... }
161     static Result MakeForBlender(SkString sksl, const Options&);
MakeForBlender(SkString sksl)162     static Result MakeForBlender(SkString sksl) {
163         return MakeForBlender(std::move(sksl), Options{});
164     }
165 
166     // DSL entry points
167     static Result MakeForColorFilter(std::unique_ptr<SkSL::Program> program, const Options&);
168     static Result MakeForColorFilter(std::unique_ptr<SkSL::Program> program);
169 
170     static Result MakeForShader(std::unique_ptr<SkSL::Program> program, const Options&);
171     static Result MakeForShader(std::unique_ptr<SkSL::Program> program);
172     static sk_sp<SkRuntimeEffect> MakeForShader(std::unique_ptr<SkSL::Program> program,
173                                                 const Options&, SkSL::ErrorReporter* errors);
174 
175 
176     static Result MakeForBlender(std::unique_ptr<SkSL::Program> program, const Options&);
177     static Result MakeForBlender(std::unique_ptr<SkSL::Program> program);
178 
179     // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child
180     class ChildPtr {
181     public:
182         ChildPtr() = default;
ChildPtr(sk_sp<SkShader> s)183         ChildPtr(sk_sp<SkShader> s) : fChild(std::move(s)) {}
ChildPtr(sk_sp<SkColorFilter> cf)184         ChildPtr(sk_sp<SkColorFilter> cf) : fChild(std::move(cf)) {}
ChildPtr(sk_sp<SkBlender> b)185         ChildPtr(sk_sp<SkBlender> b) : fChild(std::move(b)) {}
186 
187         std::optional<ChildType> type() const;
188 
189         SkShader* shader() const;
190         SkColorFilter* colorFilter() const;
191         SkBlender* blender() const;
flattenable()192         SkFlattenable* flattenable() const { return fChild.get(); }
193 
194     private:
195         sk_sp<SkFlattenable> fChild;
196     };
197 
198     sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms,
199                                sk_sp<SkShader> children[],
200                                size_t childCount,
201                                const SkMatrix* localMatrix = nullptr) const;
202     sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms,
203                                SkSpan<ChildPtr> children,
204                                const SkMatrix* localMatrix = nullptr) const;
205 
206     sk_sp<SkImage> makeImage(GrRecordingContext*,
207                              sk_sp<SkData> uniforms,
208                              SkSpan<ChildPtr> children,
209                              const SkMatrix* localMatrix,
210                              SkImageInfo resultInfo,
211                              bool mipmapped) const;
212 
213     sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms) const;
214     sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms,
215                                          sk_sp<SkColorFilter> children[],
216                                          size_t childCount) const;
217     sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms,
218                                          SkSpan<ChildPtr> children) const;
219 
220     sk_sp<SkBlender> makeBlender(sk_sp<SkData> uniforms, SkSpan<ChildPtr> children = {}) const;
221 
222     /**
223      * Creates a new Runtime Effect patterned after an already-existing one. The new shader behaves
224      * like the original, but also creates a debug trace of its execution at the requested
225      * coordinate. After painting with this shader, the associated DebugTrace object will contain a
226      * shader execution trace. Call `writeTrace` on the debug trace object to generate a full trace
227      * suitable for a debugger, or call `dump` to emit a human-readable trace.
228      *
229      * Debug traces are only supported on a raster (non-GPU) canvas.
230 
231      * Debug traces are currently only supported on shaders. Color filter and blender tracing is a
232      * work-in-progress.
233      */
234     struct TracedShader {
235         sk_sp<SkShader> shader;
236         sk_sp<SkSL::DebugTrace> debugTrace;
237     };
238     static TracedShader MakeTraced(sk_sp<SkShader> shader, const SkIPoint& traceCoord);
239 
240     // Returns the SkSL source of the runtime effect shader.
241     const std::string& source() const;
242 
243     // Combined size of all 'uniform' variables. When calling makeColorFilter or makeShader,
244     // provide an SkData of this size, containing values for all of those variables.
245     size_t uniformSize() const;
246 
uniforms()247     SkSpan<const Uniform> uniforms() const { return SkMakeSpan(fUniforms); }
children()248     SkSpan<const Child> children() const { return SkMakeSpan(fChildren); }
249 
250     // Returns pointer to the named uniform variable's description, or nullptr if not found
251     const Uniform* findUniform(const char* name) const;
252 
253     // Returns pointer to the named child's description, or nullptr if not found
254     const Child* findChild(const char* name) const;
255 
256     static void RegisterFlattenables();
257     ~SkRuntimeEffect() override;
258 
259 private:
260     enum Flags {
261         kUsesSampleCoords_Flag   = 0x01,
262         kAllowColorFilter_Flag   = 0x02,
263         kAllowShader_Flag        = 0x04,
264         kAllowBlender_Flag       = 0x08,
265         kSamplesOutsideMain_Flag = 0x10,
266         kUsesColorTransform_Flag = 0x20,
267         kAlwaysOpaque_Flag       = 0x40,
268     };
269 
270     SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram,
271                     const Options& options,
272                     const SkSL::FunctionDefinition& main,
273                     std::vector<Uniform>&& uniforms,
274                     std::vector<Child>&& children,
275                     std::vector<SkSL::SampleUsage>&& sampleUsages,
276                     uint32_t flags);
277 
278     sk_sp<SkRuntimeEffect> makeUnoptimizedClone();
279 
280     static Result MakeFromSource(SkString sksl, const Options& options, SkSL::ProgramKind kind);
281 
282     static Result MakeFromDSL(std::unique_ptr<SkSL::Program> program,
283                               const Options& options,
284                               SkSL::ProgramKind kind);
285 
286     static sk_sp<SkRuntimeEffect> MakeFromDSL(std::unique_ptr<SkSL::Program> program,
287                                               const Options& options,
288                                               SkSL::ProgramKind kind,
289                                               SkSL::ErrorReporter* errors);
290 
291     static Result MakeInternal(std::unique_ptr<SkSL::Program> program,
292                                const Options& options,
293                                SkSL::ProgramKind kind);
294 
295     static SkSL::ProgramSettings MakeSettings(const Options& options, bool optimize);
296 
hash()297     uint32_t hash() const { return fHash; }
usesSampleCoords()298     bool usesSampleCoords()   const { return (fFlags & kUsesSampleCoords_Flag);   }
allowShader()299     bool allowShader()        const { return (fFlags & kAllowShader_Flag);        }
allowColorFilter()300     bool allowColorFilter()   const { return (fFlags & kAllowColorFilter_Flag);   }
allowBlender()301     bool allowBlender()       const { return (fFlags & kAllowBlender_Flag);       }
samplesOutsideMain()302     bool samplesOutsideMain() const { return (fFlags & kSamplesOutsideMain_Flag); }
usesColorTransform()303     bool usesColorTransform() const { return (fFlags & kUsesColorTransform_Flag); }
alwaysOpaque()304     bool alwaysOpaque()       const { return (fFlags & kAlwaysOpaque_Flag);       }
305 
306     const SkFilterColorProgram* getFilterColorProgram();
307 
308 #if SK_SUPPORT_GPU
309     friend class GrSkSLFP;             // fBaseProgram, fSampleUsages
310     friend class GrGLSLSkSLFP;         //
311 #endif
312 
313     friend class SkRTShader;            // fBaseProgram, fMain
314     friend class SkRuntimeBlender;      //
315     friend class SkRuntimeColorFilter;  //
316 
317     friend class SkFilterColorProgram;
318     friend class SkRuntimeEffectPriv;
319 
320     uint32_t fHash;
321 
322     std::unique_ptr<SkSL::Program> fBaseProgram;
323     const SkSL::FunctionDefinition& fMain;
324     std::vector<Uniform> fUniforms;
325     std::vector<Child> fChildren;
326     std::vector<SkSL::SampleUsage> fSampleUsages;
327 
328     std::unique_ptr<SkFilterColorProgram> fFilterColorProgram;
329 
330     uint32_t fFlags;  // Flags
331 };
332 
333 /** Base class for SkRuntimeShaderBuilder, defined below. */
334 class SkRuntimeEffectBuilder {
335 public:
336     struct BuilderUniform {
337         // Copy 'val' to this variable. No type conversion is performed - 'val' must be same
338         // size as expected by the effect. Information about the variable can be queried by
339         // looking at fVar. If the size is incorrect, no copy will be performed, and debug
340         // builds will abort. If this is the result of querying a missing variable, fVar will
341         // be nullptr, and assigning will also do nothing (and abort in debug builds).
342         template <typename T>
343         std::enable_if_t<std::is_trivially_copyable<T>::value, BuilderUniform&> operator=(
344                 const T& val) {
345             if (!fVar) {
346                 SkDEBUGFAIL("Assigning to missing variable");
347             } else if (sizeof(val) != fVar->sizeInBytes()) {
348                 SkDEBUGFAIL("Incorrect value size");
349             } else {
350                 memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset),
351                        &val, sizeof(val));
352             }
353             return *this;
354         }
355 
356         BuilderUniform& operator=(const SkMatrix& val) {
357             if (!fVar) {
358                 SkDEBUGFAIL("Assigning to missing variable");
359             } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
360                 SkDEBUGFAIL("Incorrect value size");
361             } else {
362                 float* data = SkTAddOffset<float>(fOwner->writableUniformData(),
363                                                   (ptrdiff_t)fVar->offset);
364                 data[0] = val.get(0); data[1] = val.get(3); data[2] = val.get(6);
365                 data[3] = val.get(1); data[4] = val.get(4); data[5] = val.get(7);
366                 data[6] = val.get(2); data[7] = val.get(5); data[8] = val.get(8);
367             }
368             return *this;
369         }
370 
371         template <typename T>
setBuilderUniform372         bool set(const T val[], const int count) {
373             static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
374             if (!fVar) {
375                 SkDEBUGFAIL("Assigning to missing variable");
376                 return false;
377             } else if (sizeof(T) * count != fVar->sizeInBytes()) {
378                 SkDEBUGFAIL("Incorrect value size");
379                 return false;
380             } else {
381                 memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset),
382                        val, sizeof(T) * count);
383             }
384             return true;
385         }
386 
387         SkRuntimeEffectBuilder*         fOwner;
388         const SkRuntimeEffect::Uniform* fVar;    // nullptr if the variable was not found
389     };
390 
391     struct BuilderChild {
392         template <typename T> BuilderChild& operator=(sk_sp<T> val) {
393             if (!fChild) {
394                 SkDEBUGFAIL("Assigning to missing child");
395             } else {
396                 fOwner->fChildren[(size_t)fChild->index] = std::move(val);
397             }
398             return *this;
399         }
400 
401         BuilderChild& operator=(std::nullptr_t) {
402             if (!fChild) {
403                 SkDEBUGFAIL("Assigning to missing child");
404             } else {
405                 fOwner->fChildren[(size_t)fChild->index] = SkRuntimeEffect::ChildPtr{};
406             }
407             return *this;
408         }
409 
410         SkRuntimeEffectBuilder*       fOwner;
411         const SkRuntimeEffect::Child* fChild;  // nullptr if the child was not found
412     };
413 
effect()414     const SkRuntimeEffect* effect() const { return fEffect.get(); }
415 
uniform(const char * name)416     BuilderUniform uniform(const char* name) { return { this, fEffect->findUniform(name) }; }
child(const char * name)417     BuilderChild child(const char* name) {
418         const SkRuntimeEffect::Child* child = fEffect->findChild(name);
419         return { this, child };
420     }
421 
422 protected:
423     SkRuntimeEffectBuilder() = delete;
SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)424     explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)
425             : fEffect(std::move(effect))
426             , fUniforms(SkData::MakeZeroInitialized(fEffect->uniformSize()))
427             , fChildren(fEffect->children().size()) {}
SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms)428     explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms)
429             : fEffect(std::move(effect))
430             , fUniforms(std::move(uniforms))
431             , fChildren(fEffect->children().size()) {}
432 
433     SkRuntimeEffectBuilder(SkRuntimeEffectBuilder&&) = default;
434     SkRuntimeEffectBuilder(const SkRuntimeEffectBuilder&) = default;
435 
436     SkRuntimeEffectBuilder& operator=(SkRuntimeEffectBuilder&&) = delete;
437     SkRuntimeEffectBuilder& operator=(const SkRuntimeEffectBuilder&) = delete;
438 
uniforms()439     sk_sp<SkData> uniforms() { return fUniforms; }
children()440     SkRuntimeEffect::ChildPtr* children() { return fChildren.data(); }
numChildren()441     size_t numChildren() { return fChildren.size(); }
442 
443 private:
writableUniformData()444     void* writableUniformData() {
445         if (!fUniforms->unique()) {
446             fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size());
447         }
448         return fUniforms->writable_data();
449     }
450 
451     sk_sp<SkRuntimeEffect>                 fEffect;
452     sk_sp<SkData>                          fUniforms;
453     std::vector<SkRuntimeEffect::ChildPtr> fChildren;
454 };
455 
456 /**
457  * SkRuntimeShaderBuilder is a utility to simplify creating SkShader objects from SkRuntimeEffects.
458  *
459  * NOTE: Like SkRuntimeEffect, this API is experimental and subject to change!
460  *
461  * Given an SkRuntimeEffect, the SkRuntimeShaderBuilder manages creating an input data block and
462  * provides named access to the 'uniform' variables in that block, as well as named access
463  * to a list of child shader slots. Usage:
464  *
465  *   sk_sp<SkRuntimeEffect> effect = ...;
466  *   SkRuntimeShaderBuilder builder(effect);
467  *   builder.uniform("some_uniform_float")  = 3.14f;
468  *   builder.uniform("some_uniform_matrix") = SkM44::Rotate(...);
469  *   builder.child("some_child_effect")     = mySkImage->makeShader(...);
470  *   ...
471  *   sk_sp<SkShader> shader = builder.makeShader(nullptr, false);
472  *
473  * Note that SkRuntimeShaderBuilder is built entirely on the public API of SkRuntimeEffect,
474  * so can be used as-is or serve as inspiration for other interfaces or binding techniques.
475  */
476 class SK_API SkRuntimeShaderBuilder : public SkRuntimeEffectBuilder {
477 public:
478     explicit SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect>);
479     // This is currently required by Android Framework but may go away if that dependency
480     // can be removed.
481     SkRuntimeShaderBuilder(const SkRuntimeShaderBuilder&) = default;
482     ~SkRuntimeShaderBuilder();
483 
484     sk_sp<SkShader> makeShader(const SkMatrix* localMatrix = nullptr);
485     sk_sp<SkImage> makeImage(GrRecordingContext*,
486                              const SkMatrix* localMatrix,
487                              SkImageInfo resultInfo,
488                              bool mipmapped);
489 
490 private:
491     using INHERITED = SkRuntimeEffectBuilder;
492 
SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms)493     explicit SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms)
494             : INHERITED(std::move(effect), std::move(uniforms)) {}
495 
496     friend class SkRuntimeImageFilter;
497 };
498 
499 /**
500  * SkRuntimeBlendBuilder is a utility to simplify creation and uniform setup of runtime blenders.
501  */
502 class SK_API SkRuntimeBlendBuilder : public SkRuntimeEffectBuilder {
503 public:
504     explicit SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect>);
505     ~SkRuntimeBlendBuilder();
506 
507     SkRuntimeBlendBuilder(const SkRuntimeBlendBuilder&) = delete;
508     SkRuntimeBlendBuilder& operator=(const SkRuntimeBlendBuilder&) = delete;
509 
510     sk_sp<SkBlender> makeBlender();
511 
512 private:
513     using INHERITED = SkRuntimeEffectBuilder;
514 };
515 
516 #endif  // SK_ENABLE_SKSL
517 
518 #endif  // SkRuntimeEffect_DEFINED
519