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