Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkRuntimeEffect.h
Go to the documentation of this file.
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"
18#include "include/core/SkSpan.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 */
60public:
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,
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;
102 int count;
103 uint32_t flags;
104
105 bool isArray() const { return SkToBool(this->flags & kArray_Flag); }
106 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;
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;
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.
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&);
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&);
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&);
181 return MakeForBlender(std::move(sksl), Options{});
182 }
183
184 // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child
186 public:
187 ChildPtr() = default;
188 ChildPtr(sk_sp<SkShader> s) : fChild(std::move(s)) {}
189 ChildPtr(sk_sp<SkColorFilter> cf) : fChild(std::move(cf)) {}
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
194
195 std::optional<ChildType> type() const;
196
197 SkShader* shader() const;
198 SkColorFilter* colorFilter() const;
199 SkBlender* blender() const;
200 SkFlattenable* flattenable() const { return fChild.get(); }
201
202 using sk_is_trivially_relocatable = std::true_type;
203
204 private:
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 */
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
253 SkSpan<const Uniform> uniforms() const { return SkSpan(fUniforms); }
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.
263 bool allowShader() const { return (fFlags & kAllowShader_Flag); }
264 bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); }
265 bool allowBlender() const { return (fFlags & kAllowBlender_Flag); }
266
267 static void RegisterFlattenables();
269
270private:
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,
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
301 uint32_t hash() const { return fHash; }
302 bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); }
303 bool samplesOutsideMain() const { return (fFlags & kSamplesOutsideMain_Flag); }
304 bool usesColorTransform() const { return (fFlags & kUsesColorTransform_Flag); }
305 bool alwaysOpaque() const { return (fFlags & kAlwaysOpaque_Flag); }
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
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. */
333public:
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
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>
370 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
386 const SkRuntimeEffect::Uniform* fVar; // nullptr if the variable was not found
387 };
388
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
409 const SkRuntimeEffect::Child* fChild; // nullptr if the child was not found
410 };
411
412 const SkRuntimeEffect* effect() const { return fEffect.get(); }
413
414 BuilderUniform uniform(std::string_view name) { return { this, fEffect->findUniform(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):
419 sk_sp<const SkData> uniforms() const { return fUniforms; }
421
422protected:
425 : fEffect(std::move(effect))
426 , fUniforms(SkData::MakeZeroInitialized(fEffect->uniformSize()))
427 , fChildren(fEffect->children().size()) {}
429 : fEffect(std::move(effect))
430 , fUniforms(std::move(uniforms))
431 , fChildren(fEffect->children().size()) {}
432
435
438
439private:
440 void* writableUniformData() {
441 if (!fUniforms->unique()) {
442 fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size());
443 }
444 return fUniforms->writable_data();
445 }
446
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 */
473public:
475 // This is currently required by Android Framework but may go away if that dependency
476 // can be removed.
479
480 sk_sp<SkShader> makeShader(const SkMatrix* localMatrix = nullptr) const;
481
482private:
484 : SkRuntimeEffectBuilder(std::move(effect), std::move(uniforms)) {}
485
487};
488
489/**
490 * SkRuntimeColorFilterBuilder makes it easy to setup and assign uniforms to runtime color filters.
491 */
502
503/**
504 * SkRuntimeBlendBuilder is a utility to simplify creation and uniform setup of runtime blenders.
505 */
516
517#endif // SkRuntimeEffect_DEFINED
const char * options
int count
uint16_t fFlags
#define SK_API
Definition SkAPI.h:35
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
static uint32_t hash(const SkShaderBase::GradientInfo &v)
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition SkData.cpp:111
SkScalar get(int index) const
Definition SkMatrix.h:392
SkRuntimeBlendBuilder(const SkRuntimeBlendBuilder &)=delete
SkRuntimeBlendBuilder & operator=(const SkRuntimeBlendBuilder &)=delete
SkRuntimeColorFilterBuilder(const SkRuntimeColorFilterBuilder &)=delete
SkRuntimeColorFilterBuilder & operator=(const SkRuntimeColorFilterBuilder &)=delete
SkRuntimeEffectBuilder & operator=(SkRuntimeEffectBuilder &&)=delete
BuilderUniform uniform(std::string_view name)
SkRuntimeEffectBuilder(sk_sp< SkRuntimeEffect > effect)
BuilderChild child(std::string_view name)
sk_sp< const SkData > uniforms() const
SkRuntimeEffectBuilder(const SkRuntimeEffectBuilder &)=default
SkRuntimeEffectBuilder(sk_sp< SkRuntimeEffect > effect, sk_sp< SkData > uniforms)
const SkRuntimeEffect * effect() const
SkSpan< const SkRuntimeEffect::ChildPtr > children() const
SkRuntimeEffectBuilder & operator=(const SkRuntimeEffectBuilder &)=delete
SkRuntimeEffectBuilder()=delete
SkRuntimeEffectBuilder(SkRuntimeEffectBuilder &&)=default
ChildPtr(sk_sp< SkBlender > b)
SkFlattenable * flattenable() const
ChildPtr(sk_sp< SkShader > s)
std::true_type sk_is_trivially_relocatable
ChildPtr(sk_sp< SkColorFilter > cf)
const Child * findChild(std::string_view name) const
SkSpan< const Child > children() const
bool allowColorFilter() const
~SkRuntimeEffect() override
bool allowBlender() const
static Result MakeForShader(SkString sksl)
static Result MakeForColorFilter(SkString sksl)
SkSpan< const Uniform > uniforms() const
bool allowShader() const
static Result MakeForBlender(SkString sksl)
const Uniform * findUniform(std::string_view name) const
SkRuntimeShaderBuilder(const SkRuntimeShaderBuilder &)=default
T * get() const
Definition SkRefCnt.h:303
SkBitmap source
Definition examples.cpp:28
static bool b
struct MyStruct s
FlutterSemanticsFlag flags
const char * name
Definition fuchsia.cc:50
Definition main.py:1
Definition ref_ptr.h:256
#define T
BuilderChild & operator=(sk_sp< T > val)
const SkRuntimeEffect::Child * fChild
BuilderChild & operator=(std::nullptr_t)
std::enable_if_t< std::is_trivially_copyable< T >::value, BuilderUniform & > operator=(const T &val)
BuilderUniform & operator=(const SkMatrix &val)
const SkRuntimeEffect::Uniform * fVar
bool set(const T val[], const int count)
sk_sp< SkRuntimeEffect > effect
sk_sp< SkSL::DebugTrace > debugTrace