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