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