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 // https://fiddle.skia.org/c/@runtimeeffect_colorfilter_grid 166 static Result MakeForColorFilter(SkString sksl, const Options&); MakeForColorFilter(SkString sksl)167 static Result MakeForColorFilter(SkString sksl) { 168 return MakeForColorFilter(std::move(sksl), Options{}); 169 } 170 171 // Shader SkSL requires an entry point that looks like: 172 // vec4 main(vec2 inCoords) { ... } 173 // The color that is returned should be premultiplied. 174 static Result MakeForShader(SkString sksl, const Options&); MakeForShader(SkString sksl)175 static Result MakeForShader(SkString sksl) { 176 return MakeForShader(std::move(sksl), Options{}); 177 } 178 179 // Blend SkSL requires an entry point that looks like: 180 // vec4 main(vec4 srcColor, vec4 dstColor) { ... } 181 static Result MakeForBlender(SkString sksl, const Options&); MakeForBlender(SkString sksl)182 static Result MakeForBlender(SkString sksl) { 183 return MakeForBlender(std::move(sksl), Options{}); 184 } 185 186 // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child 187 class SK_API ChildPtr { 188 public: 189 ChildPtr() = default; ChildPtr(sk_sp<SkShader> s)190 ChildPtr(sk_sp<SkShader> s) : fChild(std::move(s)) {} ChildPtr(sk_sp<SkColorFilter> cf)191 ChildPtr(sk_sp<SkColorFilter> cf) : fChild(std::move(cf)) {} ChildPtr(sk_sp<SkBlender> b)192 ChildPtr(sk_sp<SkBlender> b) : fChild(std::move(b)) {} 193 194 // Asserts that the flattenable is either null, or one of the legal derived types 195 ChildPtr(sk_sp<SkFlattenable> f); 196 197 std::optional<ChildType> type() const; 198 199 SkShader* shader() const; 200 SkColorFilter* colorFilter() const; 201 SkBlender* blender() const; flattenable()202 SkFlattenable* flattenable() const { return fChild.get(); } 203 204 using sk_is_trivially_relocatable = std::true_type; 205 206 private: 207 sk_sp<SkFlattenable> fChild; 208 209 static_assert(::sk_is_trivially_relocatable<decltype(fChild)>::value); 210 }; 211 212 sk_sp<SkShader> makeShader(sk_sp<const SkData> uniforms, 213 sk_sp<SkShader> children[], 214 size_t childCount, 215 const SkMatrix* localMatrix = nullptr) const; 216 sk_sp<SkShader> makeShader(sk_sp<const SkData> uniforms, 217 SkSpan<const ChildPtr> children, 218 const SkMatrix* localMatrix = nullptr) const; 219 220 sk_sp<SkColorFilter> makeColorFilter(sk_sp<const SkData> uniforms) const; 221 sk_sp<SkColorFilter> makeColorFilter(sk_sp<const SkData> uniforms, 222 sk_sp<SkColorFilter> children[], 223 size_t childCount) const; 224 sk_sp<SkColorFilter> makeColorFilter(sk_sp<const SkData> uniforms, 225 SkSpan<const ChildPtr> children) const; 226 227 sk_sp<SkBlender> makeBlender(sk_sp<const SkData> uniforms, 228 SkSpan<const ChildPtr> children = {}) const; 229 230 /** 231 * Creates a new Runtime Effect patterned after an already-existing one. The new shader behaves 232 * like the original, but also creates a debug trace of its execution at the requested 233 * coordinate. After painting with this shader, the associated DebugTrace object will contain a 234 * shader execution trace. Call `writeTrace` on the debug trace object to generate a full trace 235 * suitable for a debugger, or call `dump` to emit a human-readable trace. 236 * 237 * Debug traces are only supported on a raster (non-GPU) canvas. 238 239 * Debug traces are currently only supported on shaders. Color filter and blender tracing is a 240 * work-in-progress. 241 */ 242 struct TracedShader { 243 sk_sp<SkShader> shader; 244 sk_sp<SkSL::DebugTrace> debugTrace; 245 }; 246 static TracedShader MakeTraced(sk_sp<SkShader> shader, const SkIPoint& traceCoord); 247 248 // Returns the SkSL source of the runtime effect shader. 249 const std::string& source() const; 250 251 // Combined size of all 'uniform' variables. When calling makeColorFilter or makeShader, 252 // provide an SkData of this size, containing values for all of those variables. 253 size_t uniformSize() const; 254 uniforms()255 SkSpan<const Uniform> uniforms() const { return SkSpan(fUniforms); } children()256 SkSpan<const Child> children() const { return SkSpan(fChildren); } 257 258 // Returns pointer to the named uniform variable's description, or nullptr if not found 259 const Uniform* findUniform(std::string_view name) const; 260 261 // Returns pointer to the named child's description, or nullptr if not found 262 const Child* findChild(std::string_view name) const; 263 264 // Allows the runtime effect type to be identified. allowShader()265 bool allowShader() const { return (fFlags & kAllowShader_Flag); } allowColorFilter()266 bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); } allowBlender()267 bool allowBlender() const { return (fFlags & kAllowBlender_Flag); } 268 269 static void RegisterFlattenables(); 270 ~SkRuntimeEffect() override; 271 272 private: 273 enum Flags { 274 kUsesSampleCoords_Flag = 0x001, 275 kAllowColorFilter_Flag = 0x002, 276 kAllowShader_Flag = 0x004, 277 kAllowBlender_Flag = 0x008, 278 kSamplesOutsideMain_Flag = 0x010, 279 kUsesColorTransform_Flag = 0x020, 280 kAlwaysOpaque_Flag = 0x040, 281 kAlphaUnchanged_Flag = 0x080, 282 kDisableOptimization_Flag = 0x100, 283 }; 284 285 SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram, 286 const Options& options, 287 const SkSL::FunctionDefinition& main, 288 std::vector<Uniform>&& uniforms, 289 std::vector<Child>&& children, 290 std::vector<SkSL::SampleUsage>&& sampleUsages, 291 uint32_t flags); 292 293 sk_sp<SkRuntimeEffect> makeUnoptimizedClone(); 294 295 static Result MakeFromSource(SkString sksl, const Options& options, SkSL::ProgramKind kind); 296 297 static Result MakeInternal(std::unique_ptr<SkSL::Program> program, 298 const Options& options, 299 SkSL::ProgramKind kind); 300 301 static SkSL::ProgramSettings MakeSettings(const Options& options); 302 hash()303 uint32_t hash() const { return fHash; } usesSampleCoords()304 bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); } samplesOutsideMain()305 bool samplesOutsideMain() const { return (fFlags & kSamplesOutsideMain_Flag); } usesColorTransform()306 bool usesColorTransform() const { return (fFlags & kUsesColorTransform_Flag); } alwaysOpaque()307 bool alwaysOpaque() const { return (fFlags & kAlwaysOpaque_Flag); } isAlphaUnchanged()308 bool isAlphaUnchanged() const { return (fFlags & kAlphaUnchanged_Flag); } 309 310 const SkSL::RP::Program* getRPProgram(SkSL::DebugTracePriv* debugTrace) const; 311 312 friend class GrSkSLFP; // usesColorTransform 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 uint32_t fStableKey; 321 322 std::unique_ptr<SkSL::Program> fBaseProgram; 323 std::unique_ptr<SkSL::RP::Program> fRPProgram; 324 mutable SkOnce fCompileRPProgramOnce; 325 const SkSL::FunctionDefinition& fMain; 326 std::vector<Uniform> fUniforms; 327 std::vector<Child> fChildren; 328 std::vector<SkSL::SampleUsage> fSampleUsages; 329 330 uint32_t fFlags; // Flags 331 }; 332 333 /** 334 * SkRuntimeEffectBuilder is a utility to simplify creating SkShader, SkColorFilter, and SkBlender 335 * objects from SkRuntimeEffects. 336 * 337 * NOTE: Like SkRuntimeEffect, this API is experimental and subject to change! 338 * 339 * Given an SkRuntimeEffect, the SkRuntimeEffectBuilder manages creating an input data block and 340 * provides named access to the 'uniform' variables in that block, as well as named access 341 * to a list of child shader slots. Usage: 342 * 343 * sk_sp<SkRuntimeEffect> effect = ...; 344 * SkRuntimeEffectBuilder builder(effect); 345 * builder.uniform("some_uniform_float") = 3.14f; 346 * builder.uniform("some_uniform_matrix") = SkM44::Rotate(...); 347 * builder.child("some_child_effect") = mySkImage->makeShader(...); 348 * ... 349 * sk_sp<SkShader> shader = builder.makeShader(nullptr, false); 350 * 351 * Upon calling makeShader, makeColorFilter, or makeBlender, the builder will check the validity 352 * of the SkSL to see if the entry point is correct. 353 * 354 * Note that SkRuntimeEffectBuilder is built entirely on the public API of SkRuntimeEffect, 355 * so can be used as-is or serve as inspiration for other interfaces or binding techniques. 356 */ 357 class SK_API SkRuntimeEffectBuilder { 358 public: SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)359 explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect) 360 : fEffect(std::move(effect)) 361 , fUniforms(SkData::MakeZeroInitialized(fEffect->uniformSize())) 362 , fChildren(fEffect->children().size()) {} SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms)363 explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms) 364 : fEffect(std::move(effect)) 365 , fUniforms(std::move(uniforms)) 366 , fChildren(fEffect->children().size()) {} 367 368 // This is currently required by Android Framework but may go away if that dependency 369 // can be removed. 370 SkRuntimeEffectBuilder(const SkRuntimeEffectBuilder&) = default; 371 372 struct BuilderUniform { 373 // Copy 'val' to this variable. No type conversion is performed - 'val' must be same 374 // size as expected by the effect. Information about the variable can be queried by 375 // looking at fVar. If the size is incorrect, no copy will be performed, and debug 376 // builds will abort. If this is the result of querying a missing variable, fVar will 377 // be nullptr, and assigning will also do nothing (and abort in debug builds). 378 template <typename T> 379 std::enable_if_t<std::is_trivially_copyable<T>::value, BuilderUniform&> operator=( 380 const T& val) { 381 if (!fVar) { 382 SkDEBUGFAIL("Assigning to missing variable"); 383 } else if (sizeof(val) != fVar->sizeInBytes()) { 384 SkDEBUGFAIL("Incorrect value size"); 385 } else { 386 memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), 387 &val, sizeof(val)); 388 } 389 return *this; 390 } 391 392 BuilderUniform& operator=(const SkMatrix& val) { 393 if (!fVar) { 394 SkDEBUGFAIL("Assigning to missing variable"); 395 } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { 396 SkDEBUGFAIL("Incorrect value size"); 397 } else { 398 float* data = SkTAddOffset<float>(fOwner->writableUniformData(), 399 (ptrdiff_t)fVar->offset); 400 data[0] = val.get(0); data[1] = val.get(3); data[2] = val.get(6); 401 data[3] = val.get(1); data[4] = val.get(4); data[5] = val.get(7); 402 data[6] = val.get(2); data[7] = val.get(5); data[8] = val.get(8); 403 } 404 return *this; 405 } 406 407 template <typename T> setBuilderUniform408 bool set(const T val[], const int count) { 409 static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable"); 410 if (!fVar) { 411 SkDEBUGFAIL("Assigning to missing variable"); 412 return false; 413 } else if (sizeof(T) * count != fVar->sizeInBytes()) { 414 SkDEBUGFAIL("Incorrect value size"); 415 return false; 416 } else { 417 memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), 418 val, sizeof(T) * count); 419 } 420 return true; 421 } 422 423 SkRuntimeEffectBuilder* fOwner; 424 const SkRuntimeEffect::Uniform* fVar; // nullptr if the variable was not found 425 }; 426 427 struct BuilderChild { 428 template <typename T> BuilderChild& operator=(sk_sp<T> val) { 429 if (!fChild) { 430 SkDEBUGFAIL("Assigning to missing child"); 431 } else { 432 fOwner->fChildren[(size_t)fChild->index] = std::move(val); 433 } 434 return *this; 435 } 436 437 BuilderChild& operator=(std::nullptr_t) { 438 if (!fChild) { 439 SkDEBUGFAIL("Assigning to missing child"); 440 } else { 441 fOwner->fChildren[(size_t)fChild->index] = SkRuntimeEffect::ChildPtr{}; 442 } 443 return *this; 444 } 445 446 SkRuntimeEffectBuilder* fOwner; 447 const SkRuntimeEffect::Child* fChild; // nullptr if the child was not found 448 }; 449 effect()450 const SkRuntimeEffect* effect() const { return fEffect.get(); } 451 uniform(std::string_view name)452 BuilderUniform uniform(std::string_view name) { return { this, fEffect->findUniform(name) }; } child(std::string_view name)453 BuilderChild child(std::string_view name) { return { this, fEffect->findChild(name) }; } 454 455 // Get access to the collated uniforms and children (in the order expected by APIs like 456 // makeShader on the effect): uniforms()457 sk_sp<const SkData> uniforms() const { return fUniforms; } children()458 SkSpan<const SkRuntimeEffect::ChildPtr> children() const { return fChildren; } 459 460 // Build methods, at this point checks are made to ensure the SkSL entry point `main` is correct 461 sk_sp<SkShader> makeShader(const SkMatrix* localMatrix = nullptr) const; 462 sk_sp<SkColorFilter> makeColorFilter() const; 463 sk_sp<SkBlender> makeBlender() const; 464 465 ~SkRuntimeEffectBuilder() = default; 466 467 protected: 468 SkRuntimeEffectBuilder() = delete; 469 470 SkRuntimeEffectBuilder(SkRuntimeEffectBuilder&&) = default; 471 472 SkRuntimeEffectBuilder& operator=(SkRuntimeEffectBuilder&&) = delete; 473 SkRuntimeEffectBuilder& operator=(const SkRuntimeEffectBuilder&) = delete; 474 475 private: writableUniformData()476 void* writableUniformData() { 477 if (!fUniforms->unique()) { 478 fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); 479 } 480 return fUniforms->writable_data(); 481 } 482 483 sk_sp<SkRuntimeEffect> fEffect; 484 sk_sp<SkData> fUniforms; 485 std::vector<SkRuntimeEffect::ChildPtr> fChildren; 486 487 friend class SkRuntimeImageFilter; 488 }; 489 490 /** 491 * DEPRECATED: Subclass logic has been moved to base class SkRuntimeEffectBuilder. 492 */ 493 using SkRuntimeShaderBuilder = SkRuntimeEffectBuilder; 494 using SkRuntimeColorFilterBuilder = SkRuntimeEffectBuilder; 495 using SkRuntimeBlendBuilder = SkRuntimeEffectBuilder; 496 497 #endif // SkRuntimeEffect_DEFINED 498