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