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
39struct SkIPoint;
40
41namespace SkSL {
42class DebugTracePriv;
43class FunctionDefinition;
44struct Program;
45enum class ProgramKind : int8_t;
46struct ProgramSettings;
47} // namespace SkSL
48
49namespace SkSL::RP {
50class 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 */
59class SK_API SkRuntimeEffect : public SkRefCnt {
60public:
61 // Reflected description of a uniform variable in the effect's SkSL
62 struct 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
105 bool isArray() const { return SkToBool(x: this->flags & kArray_Flag); }
106 bool isColor() const { return SkToBool(x: 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
138 // TODO(skia:11209) - Replace this with a promised SkCapabilities?
139 // This flag lifts the ES2 restrictions on Runtime Effects that are gated by the
140 // `strictES2Mode` check. Be aware that the software renderer and pipeline-stage effect are
141 // still largely ES3-unaware and can still fail or crash if post-ES2 features are used.
142 // This is only intended for use by tests and certain internally created effects.
143 SkSL::Version maxVersionAllowed = SkSL::Version::k100;
144 };
145
146 // If the effect is compiled successfully, `effect` will be non-null.
147 // Otherwise, `errorText` will contain the reason for failure.
148 struct Result {
149 sk_sp<SkRuntimeEffect> effect;
150 SkString errorText;
151 };
152
153 // MakeForColorFilter and MakeForShader verify that the SkSL code is valid for those stages of
154 // the Skia pipeline. In all of the signatures described below, color parameters and return
155 // values are flexible. They are listed as being 'vec4', but they can also be 'half4' or
156 // 'float4'. ('vec4' is an alias for 'float4').
157
158 // We can't use a default argument for `options` due to a bug in Clang.
159 // https://bugs.llvm.org/show_bug.cgi?id=36684
160
161 // Color filter SkSL requires an entry point that looks like:
162 // vec4 main(vec4 inColor) { ... }
163 static Result MakeForColorFilter(SkString sksl, const Options&);
164 static Result MakeForColorFilter(SkString sksl) {
165 return MakeForColorFilter(sksl: std::move(sksl), Options{});
166 }
167
168 // Shader SkSL requires an entry point that looks like:
169 // vec4 main(vec2 inCoords) { ... }
170 static Result MakeForShader(SkString sksl, const Options&);
171 static Result MakeForShader(SkString sksl) {
172 return MakeForShader(sksl: std::move(sksl), Options{});
173 }
174
175 // Blend SkSL requires an entry point that looks like:
176 // vec4 main(vec4 srcColor, vec4 dstColor) { ... }
177 static Result MakeForBlender(SkString sksl, const Options&);
178 static Result MakeForBlender(SkString sksl) {
179 return MakeForBlender(sksl: std::move(sksl), Options{});
180 }
181
182 // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child
183 class ChildPtr {
184 public:
185 ChildPtr() = default;
186 ChildPtr(sk_sp<SkShader> s) : fChild(std::move(s)) {}
187 ChildPtr(sk_sp<SkColorFilter> cf) : fChild(std::move(cf)) {}
188 ChildPtr(sk_sp<SkBlender> b) : fChild(std::move(b)) {}
189
190 // Asserts that the flattenable is either null, or one of the legal derived types
191 ChildPtr(sk_sp<SkFlattenable> f);
192
193 std::optional<ChildType> type() const;
194
195 SkShader* shader() const;
196 SkColorFilter* colorFilter() const;
197 SkBlender* blender() const;
198 SkFlattenable* flattenable() const { return fChild.get(); }
199
200 using sk_is_trivially_relocatable = std::true_type;
201
202 private:
203 sk_sp<SkFlattenable> fChild;
204
205 static_assert(::sk_is_trivially_relocatable<decltype(fChild)>::value);
206 };
207
208 sk_sp<SkShader> makeShader(sk_sp<const SkData> uniforms,
209 sk_sp<SkShader> children[],
210 size_t childCount,
211 const SkMatrix* localMatrix = nullptr) const;
212 sk_sp<SkShader> makeShader(sk_sp<const SkData> uniforms,
213 SkSpan<const ChildPtr> children,
214 const SkMatrix* localMatrix = nullptr) const;
215
216 sk_sp<SkColorFilter> makeColorFilter(sk_sp<const SkData> uniforms) const;
217 sk_sp<SkColorFilter> makeColorFilter(sk_sp<const SkData> uniforms,
218 sk_sp<SkColorFilter> children[],
219 size_t childCount) const;
220 sk_sp<SkColorFilter> makeColorFilter(sk_sp<const SkData> uniforms,
221 SkSpan<const ChildPtr> children) const;
222
223 sk_sp<SkBlender> makeBlender(sk_sp<const SkData> uniforms,
224 SkSpan<const ChildPtr> children = {}) const;
225
226 /**
227 * Creates a new Runtime Effect patterned after an already-existing one. The new shader behaves
228 * like the original, but also creates a debug trace of its execution at the requested
229 * coordinate. After painting with this shader, the associated DebugTrace object will contain a
230 * shader execution trace. Call `writeTrace` on the debug trace object to generate a full trace
231 * suitable for a debugger, or call `dump` to emit a human-readable trace.
232 *
233 * Debug traces are only supported on a raster (non-GPU) canvas.
234
235 * Debug traces are currently only supported on shaders. Color filter and blender tracing is a
236 * work-in-progress.
237 */
238 struct TracedShader {
239 sk_sp<SkShader> shader;
240 sk_sp<SkSL::DebugTrace> debugTrace;
241 };
242 static TracedShader MakeTraced(sk_sp<SkShader> shader, const SkIPoint& traceCoord);
243
244 // Returns the SkSL source of the runtime effect shader.
245 const std::string& source() const;
246
247 // Combined size of all 'uniform' variables. When calling makeColorFilter or makeShader,
248 // provide an SkData of this size, containing values for all of those variables.
249 size_t uniformSize() const;
250
251 SkSpan<const Uniform> uniforms() const { return SkSpan(fUniforms); }
252 SkSpan<const Child> children() const { return SkSpan(fChildren); }
253
254 // Returns pointer to the named uniform variable's description, or nullptr if not found
255 const Uniform* findUniform(std::string_view name) const;
256
257 // Returns pointer to the named child's description, or nullptr if not found
258 const Child* findChild(std::string_view name) const;
259
260 // Allows the runtime effect type to be identified.
261 bool allowShader() const { return (fFlags & kAllowShader_Flag); }
262 bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); }
263 bool allowBlender() const { return (fFlags & kAllowBlender_Flag); }
264
265 static void RegisterFlattenables();
266 ~SkRuntimeEffect() override;
267
268private:
269 enum Flags {
270 kUsesSampleCoords_Flag = 0x001,
271 kAllowColorFilter_Flag = 0x002,
272 kAllowShader_Flag = 0x004,
273 kAllowBlender_Flag = 0x008,
274 kSamplesOutsideMain_Flag = 0x010,
275 kUsesColorTransform_Flag = 0x020,
276 kAlwaysOpaque_Flag = 0x040,
277 kAlphaUnchanged_Flag = 0x080,
278 kDisableOptimization_Flag = 0x100,
279 };
280
281 SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram,
282 const Options& options,
283 const SkSL::FunctionDefinition& main,
284 std::vector<Uniform>&& uniforms,
285 std::vector<Child>&& children,
286 std::vector<SkSL::SampleUsage>&& sampleUsages,
287 uint32_t flags);
288
289 sk_sp<SkRuntimeEffect> makeUnoptimizedClone();
290
291 static Result MakeFromSource(SkString sksl, const Options& options, SkSL::ProgramKind kind);
292
293 static Result MakeInternal(std::unique_ptr<SkSL::Program> program,
294 const Options& options,
295 SkSL::ProgramKind kind);
296
297 static SkSL::ProgramSettings MakeSettings(const Options& options);
298
299 uint32_t hash() const { return fHash; }
300 bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); }
301 bool samplesOutsideMain() const { return (fFlags & kSamplesOutsideMain_Flag); }
302 bool usesColorTransform() const { return (fFlags & kUsesColorTransform_Flag); }
303 bool alwaysOpaque() const { return (fFlags & kAlwaysOpaque_Flag); }
304 bool isAlphaUnchanged() const { return (fFlags & kAlphaUnchanged_Flag); }
305
306 const SkSL::RP::Program* getRPProgram(SkSL::DebugTracePriv* debugTrace) const;
307
308#if defined(SK_GANESH)
309 friend class GrSkSLFP; // fBaseProgram, fSampleUsages
310 friend class GrGLSLSkSLFP; //
311#endif
312
313 friend class SkRuntimeShader; // fBaseProgram, fMain, fSampleUsages, getRPProgram()
314 friend class SkRuntimeBlender; //
315 friend class SkRuntimeColorFilter; //
316
317 friend class SkRuntimeEffectPriv;
318
319 uint32_t fHash;
320
321 std::unique_ptr<SkSL::Program> fBaseProgram;
322 std::unique_ptr<SkSL::RP::Program> fRPProgram;
323 mutable SkOnce fCompileRPProgramOnce;
324 const SkSL::FunctionDefinition& fMain;
325 std::vector<Uniform> fUniforms;
326 std::vector<Child> fChildren;
327 std::vector<SkSL::SampleUsage> fSampleUsages;
328
329 uint32_t fFlags; // Flags
330};
331
332/** Base class for SkRuntimeShaderBuilder, defined below. */
333class SkRuntimeEffectBuilder {
334public:
335 struct BuilderUniform {
336 // Copy 'val' to this variable. No type conversion is performed - 'val' must be same
337 // size as expected by the effect. Information about the variable can be queried by
338 // looking at fVar. If the size is incorrect, no copy will be performed, and debug
339 // builds will abort. If this is the result of querying a missing variable, fVar will
340 // be nullptr, and assigning will also do nothing (and abort in debug builds).
341 template <typename T>
342 std::enable_if_t<std::is_trivially_copyable<T>::value, BuilderUniform&> operator=(
343 const T& val) {
344 if (!fVar) {
345 SkDEBUGFAIL("Assigning to missing variable");
346 } else if (sizeof(val) != fVar->sizeInBytes()) {
347 SkDEBUGFAIL("Incorrect value size");
348 } else {
349 memcpy(SkTAddOffset<void>(ptr: fOwner->writableUniformData(), byteOffset: fVar->offset),
350 &val, sizeof(val));
351 }
352 return *this;
353 }
354
355 BuilderUniform& operator=(const SkMatrix& val) {
356 if (!fVar) {
357 SkDEBUGFAIL("Assigning to missing variable");
358 } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
359 SkDEBUGFAIL("Incorrect value size");
360 } else {
361 float* data = SkTAddOffset<float>(ptr: fOwner->writableUniformData(),
362 byteOffset: (ptrdiff_t)fVar->offset);
363 data[0] = val.get(index: 0); data[1] = val.get(index: 3); data[2] = val.get(index: 6);
364 data[3] = val.get(index: 1); data[4] = val.get(index: 4); data[5] = val.get(index: 7);
365 data[6] = val.get(index: 2); data[7] = val.get(index: 5); data[8] = val.get(index: 8);
366 }
367 return *this;
368 }
369
370 template <typename T>
371 bool set(const T val[], const int count) {
372 static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
373 if (!fVar) {
374 SkDEBUGFAIL("Assigning to missing variable");
375 return false;
376 } else if (sizeof(T) * count != fVar->sizeInBytes()) {
377 SkDEBUGFAIL("Incorrect value size");
378 return false;
379 } else {
380 memcpy(SkTAddOffset<void>(ptr: fOwner->writableUniformData(), byteOffset: fVar->offset),
381 val, sizeof(T) * count);
382 }
383 return true;
384 }
385
386 SkRuntimeEffectBuilder* fOwner;
387 const SkRuntimeEffect::Uniform* fVar; // nullptr if the variable was not found
388 };
389
390 struct BuilderChild {
391 template <typename T> BuilderChild& operator=(sk_sp<T> val) {
392 if (!fChild) {
393 SkDEBUGFAIL("Assigning to missing child");
394 } else {
395 fOwner->fChildren[(size_t)fChild->index] = std::move(val);
396 }
397 return *this;
398 }
399
400 BuilderChild& operator=(std::nullptr_t) {
401 if (!fChild) {
402 SkDEBUGFAIL("Assigning to missing child");
403 } else {
404 fOwner->fChildren[(size_t)fChild->index] = SkRuntimeEffect::ChildPtr{};
405 }
406 return *this;
407 }
408
409 SkRuntimeEffectBuilder* fOwner;
410 const SkRuntimeEffect::Child* fChild; // nullptr if the child was not found
411 };
412
413 const SkRuntimeEffect* effect() const { return fEffect.get(); }
414
415 BuilderUniform uniform(std::string_view name) { return { .fOwner: this, .fVar: fEffect->findUniform(name) }; }
416 BuilderChild child(std::string_view name) { return { .fOwner: this, .fChild: fEffect->findChild(name) }; }
417
418 // Get access to the collated uniforms and children (in the order expected by APIs like
419 // makeShader on the effect):
420 sk_sp<const SkData> uniforms() const { return fUniforms; }
421 SkSpan<const SkRuntimeEffect::ChildPtr> children() const { return fChildren; }
422
423protected:
424 SkRuntimeEffectBuilder() = delete;
425 explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)
426 : fEffect(std::move(effect))
427 , fUniforms(SkData::MakeZeroInitialized(length: fEffect->uniformSize()))
428 , fChildren(fEffect->children().size()) {}
429 explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms)
430 : fEffect(std::move(effect))
431 , fUniforms(std::move(uniforms))
432 , fChildren(fEffect->children().size()) {}
433
434 SkRuntimeEffectBuilder(SkRuntimeEffectBuilder&&) = default;
435 SkRuntimeEffectBuilder(const SkRuntimeEffectBuilder&) = default;
436
437 SkRuntimeEffectBuilder& operator=(SkRuntimeEffectBuilder&&) = delete;
438 SkRuntimeEffectBuilder& operator=(const SkRuntimeEffectBuilder&) = delete;
439
440private:
441 void* writableUniformData() {
442 if (!fUniforms->unique()) {
443 fUniforms = SkData::MakeWithCopy(data: fUniforms->data(), length: fUniforms->size());
444 }
445 return fUniforms->writable_data();
446 }
447
448 sk_sp<SkRuntimeEffect> fEffect;
449 sk_sp<SkData> fUniforms;
450 std::vector<SkRuntimeEffect::ChildPtr> fChildren;
451};
452
453/**
454 * SkRuntimeShaderBuilder is a utility to simplify creating SkShader objects from SkRuntimeEffects.
455 *
456 * NOTE: Like SkRuntimeEffect, this API is experimental and subject to change!
457 *
458 * Given an SkRuntimeEffect, the SkRuntimeShaderBuilder manages creating an input data block and
459 * provides named access to the 'uniform' variables in that block, as well as named access
460 * to a list of child shader slots. Usage:
461 *
462 * sk_sp<SkRuntimeEffect> effect = ...;
463 * SkRuntimeShaderBuilder builder(effect);
464 * builder.uniform("some_uniform_float") = 3.14f;
465 * builder.uniform("some_uniform_matrix") = SkM44::Rotate(...);
466 * builder.child("some_child_effect") = mySkImage->makeShader(...);
467 * ...
468 * sk_sp<SkShader> shader = builder.makeShader(nullptr, false);
469 *
470 * Note that SkRuntimeShaderBuilder is built entirely on the public API of SkRuntimeEffect,
471 * so can be used as-is or serve as inspiration for other interfaces or binding techniques.
472 */
473class SK_API SkRuntimeShaderBuilder : public SkRuntimeEffectBuilder {
474public:
475 explicit SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect>);
476 // This is currently required by Android Framework but may go away if that dependency
477 // can be removed.
478 SkRuntimeShaderBuilder(const SkRuntimeShaderBuilder&) = default;
479 ~SkRuntimeShaderBuilder();
480
481 sk_sp<SkShader> makeShader(const SkMatrix* localMatrix = nullptr) const;
482
483private:
484 explicit SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms)
485 : SkRuntimeEffectBuilder(std::move(effect), std::move(uniforms)) {}
486
487 friend class SkRuntimeImageFilter;
488};
489
490/**
491 * SkRuntimeColorFilterBuilder makes it easy to setup and assign uniforms to runtime color filters.
492 */
493class SK_API SkRuntimeColorFilterBuilder : public SkRuntimeEffectBuilder {
494public:
495 explicit SkRuntimeColorFilterBuilder(sk_sp<SkRuntimeEffect>);
496 ~SkRuntimeColorFilterBuilder();
497
498 SkRuntimeColorFilterBuilder(const SkRuntimeColorFilterBuilder&) = delete;
499 SkRuntimeColorFilterBuilder& operator=(const SkRuntimeColorFilterBuilder&) = delete;
500
501 sk_sp<SkColorFilter> makeColorFilter() const;
502};
503
504/**
505 * SkRuntimeBlendBuilder is a utility to simplify creation and uniform setup of runtime blenders.
506 */
507class SK_API SkRuntimeBlendBuilder : public SkRuntimeEffectBuilder {
508public:
509 explicit SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect>);
510 ~SkRuntimeBlendBuilder();
511
512 SkRuntimeBlendBuilder(const SkRuntimeBlendBuilder&) = delete;
513 SkRuntimeBlendBuilder& operator=(const SkRuntimeBlendBuilder&) = delete;
514
515 sk_sp<SkBlender> makeBlender() const;
516};
517
518#endif // SkRuntimeEffect_DEFINED
519

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com

source code of flutter_engine/third_party/skia/include/effects/SkRuntimeEffect.h