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/private/SkOnce.h" 20 #include "include/private/SkSLSampleUsage.h" 21 #include "include/private/SkTOptional.h" 22 23 #include <string> 24 #include <vector> 25 26 #ifdef SK_ENABLE_SKSL 27 28 class GrRecordingContext; 29 class SkFilterColorProgram; 30 class SkImage; 31 class SkRuntimeImageFilter; 32 33 namespace SkSL { 34 class DebugTrace; 35 class ErrorReporter; 36 class FunctionDefinition; 37 struct Program; 38 enum class ProgramKind : int8_t; 39 struct ProgramSettings; 40 } // namespace SkSL 41 42 namespace skvm { 43 class Program; 44 } // namespace skvm 45 46 /* 47 * SkRuntimeEffect supports creating custom SkShader and SkColorFilter objects using Skia's SkSL 48 * shading language. 49 * 50 * NOTE: This API is experimental and subject to change. 51 */ 52 class SK_API SkRuntimeEffect : public SkRefCnt { 53 public: 54 // Reflected description of a uniform variable in the effect's SkSL 55 struct Uniform { 56 enum class Type { 57 kFloat, 58 kFloat2, 59 kFloat3, 60 kFloat4, 61 kFloat2x2, 62 kFloat3x3, 63 kFloat4x4, 64 kInt, 65 kInt2, 66 kInt3, 67 kInt4, 68 }; 69 70 enum Flags { 71 // Uniform is an declared as an array. 'count' contains array length. 72 kArray_Flag = 0x1, 73 74 // Uniform is declared with layout(color). Colors should be supplied as unpremultiplied, 75 // extended-range (unclamped) sRGB (ie SkColor4f). The uniform will be automatically 76 // transformed to unpremultiplied extended-range working-space colors. 77 kColor_Flag = 0x2, 78 }; 79 80 SkString name; 81 size_t offset; 82 Type type; 83 int count; 84 uint32_t flags; 85 isArrayUniform86 bool isArray() const { return SkToBool(this->flags & kArray_Flag); } isColorUniform87 bool isColor() const { return SkToBool(this->flags & kColor_Flag); } 88 size_t sizeInBytes() const; 89 }; 90 91 // Reflected description of a uniform child (shader or colorFilter) in the effect's SkSL 92 enum class ChildType { 93 kShader, 94 kColorFilter, 95 kBlender, 96 }; 97 98 struct Child { 99 SkString name; 100 ChildType type; 101 int index; 102 }; 103 104 class Options { 105 public: 106 // For testing purposes, completely disable the inliner. (Normally, Runtime Effects don't 107 // run the inliner directly, but they still get an inlining pass once they are painted.) 108 bool forceNoInline = false; 109 110 private: 111 friend class SkRuntimeEffect; 112 friend class SkRuntimeEffectPriv; 113 114 // This flag lifts the ES2 restrictions on Runtime Effects that are gated by the 115 // `strictES2Mode` check. Be aware that the software renderer and pipeline-stage effect are 116 // still largely ES3-unaware and can still fail or crash if post-ES2 features are used. 117 // This is only intended for use by tests and certain internally created effects. 118 bool enforceES2Restrictions = true; 119 120 // Similarly: Public SkSL does not allow access to sk_FragCoord. The semantics of that 121 // variable are confusing, and expose clients to implementation details of saveLayer and 122 // image filters. 123 bool allowFragCoord = false; 124 }; 125 126 // If the effect is compiled successfully, `effect` will be non-null. 127 // Otherwise, `errorText` will contain the reason for failure. 128 struct Result { 129 sk_sp<SkRuntimeEffect> effect; 130 SkString errorText; 131 }; 132 133 // MakeForColorFilter and MakeForShader verify that the SkSL code is valid for those stages of 134 // the Skia pipeline. In all of the signatures described below, color parameters and return 135 // values are flexible. They are listed as being 'vec4', but they can also be 'half4' or 136 // 'float4'. ('vec4' is an alias for 'float4'). 137 138 // We can't use a default argument for `options` due to a bug in Clang. 139 // https://bugs.llvm.org/show_bug.cgi?id=36684 140 141 // Color filter SkSL requires an entry point that looks like: 142 // vec4 main(vec4 inColor) { ... } 143 static Result MakeForColorFilter(SkString sksl, const Options&); MakeForColorFilter(SkString sksl)144 static Result MakeForColorFilter(SkString sksl) { 145 return MakeForColorFilter(std::move(sksl), Options{}); 146 } 147 148 // Shader SkSL requires an entry point that looks like: 149 // vec4 main(vec2 inCoords) { ... } 150 // -or- 151 // vec4 main(vec2 inCoords, vec4 inColor) { ... } 152 // 153 // Most shaders don't use the input color, so that parameter is optional. 154 static Result MakeForShader(SkString sksl, const Options&); MakeForShader(SkString sksl)155 static Result MakeForShader(SkString sksl) { 156 return MakeForShader(std::move(sksl), Options{}); 157 } 158 159 // Blend SkSL requires an entry point that looks like: 160 // vec4 main(vec4 srcColor, vec4 dstColor) { ... } 161 static Result MakeForBlender(SkString sksl, const Options&); MakeForBlender(SkString sksl)162 static Result MakeForBlender(SkString sksl) { 163 return MakeForBlender(std::move(sksl), Options{}); 164 } 165 166 // DSL entry points 167 static Result MakeForColorFilter(std::unique_ptr<SkSL::Program> program, const Options&); 168 static Result MakeForColorFilter(std::unique_ptr<SkSL::Program> program); 169 170 static Result MakeForShader(std::unique_ptr<SkSL::Program> program, const Options&); 171 static Result MakeForShader(std::unique_ptr<SkSL::Program> program); 172 static sk_sp<SkRuntimeEffect> MakeForShader(std::unique_ptr<SkSL::Program> program, 173 const Options&, SkSL::ErrorReporter* errors); 174 175 176 static Result MakeForBlender(std::unique_ptr<SkSL::Program> program, const Options&); 177 static Result MakeForBlender(std::unique_ptr<SkSL::Program> program); 178 179 // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child 180 class ChildPtr { 181 public: 182 ChildPtr() = default; ChildPtr(sk_sp<SkShader> s)183 ChildPtr(sk_sp<SkShader> s) : fChild(std::move(s)) {} ChildPtr(sk_sp<SkColorFilter> cf)184 ChildPtr(sk_sp<SkColorFilter> cf) : fChild(std::move(cf)) {} ChildPtr(sk_sp<SkBlender> b)185 ChildPtr(sk_sp<SkBlender> b) : fChild(std::move(b)) {} 186 187 std::optional<ChildType> type() const; 188 189 SkShader* shader() const; 190 SkColorFilter* colorFilter() const; 191 SkBlender* blender() const; flattenable()192 SkFlattenable* flattenable() const { return fChild.get(); } 193 194 private: 195 sk_sp<SkFlattenable> fChild; 196 }; 197 198 sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms, 199 sk_sp<SkShader> children[], 200 size_t childCount, 201 const SkMatrix* localMatrix = nullptr) const; 202 sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms, 203 SkSpan<ChildPtr> children, 204 const SkMatrix* localMatrix = nullptr) const; 205 206 sk_sp<SkImage> makeImage(GrRecordingContext*, 207 sk_sp<SkData> uniforms, 208 SkSpan<ChildPtr> children, 209 const SkMatrix* localMatrix, 210 SkImageInfo resultInfo, 211 bool mipmapped) const; 212 213 sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms) const; 214 sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms, 215 sk_sp<SkColorFilter> children[], 216 size_t childCount) const; 217 sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms, 218 SkSpan<ChildPtr> children) const; 219 220 sk_sp<SkBlender> makeBlender(sk_sp<SkData> uniforms, SkSpan<ChildPtr> children = {}) const; 221 222 /** 223 * Creates a new Runtime Effect patterned after an already-existing one. The new shader behaves 224 * like the original, but also creates a debug trace of its execution at the requested 225 * coordinate. After painting with this shader, the associated DebugTrace object will contain a 226 * shader execution trace. Call `writeTrace` on the debug trace object to generate a full trace 227 * suitable for a debugger, or call `dump` to emit a human-readable trace. 228 * 229 * Debug traces are only supported on a raster (non-GPU) canvas. 230 231 * Debug traces are currently only supported on shaders. Color filter and blender tracing is a 232 * work-in-progress. 233 */ 234 struct TracedShader { 235 sk_sp<SkShader> shader; 236 sk_sp<SkSL::DebugTrace> debugTrace; 237 }; 238 static TracedShader MakeTraced(sk_sp<SkShader> shader, const SkIPoint& traceCoord); 239 240 // Returns the SkSL source of the runtime effect shader. 241 const std::string& source() const; 242 243 // Combined size of all 'uniform' variables. When calling makeColorFilter or makeShader, 244 // provide an SkData of this size, containing values for all of those variables. 245 size_t uniformSize() const; 246 uniforms()247 SkSpan<const Uniform> uniforms() const { return SkMakeSpan(fUniforms); } children()248 SkSpan<const Child> children() const { return SkMakeSpan(fChildren); } 249 250 // Returns pointer to the named uniform variable's description, or nullptr if not found 251 const Uniform* findUniform(const char* name) const; 252 253 // Returns pointer to the named child's description, or nullptr if not found 254 const Child* findChild(const char* name) const; 255 256 static void RegisterFlattenables(); 257 ~SkRuntimeEffect() override; 258 259 private: 260 enum Flags { 261 kUsesSampleCoords_Flag = 0x01, 262 kAllowColorFilter_Flag = 0x02, 263 kAllowShader_Flag = 0x04, 264 kAllowBlender_Flag = 0x08, 265 kSamplesOutsideMain_Flag = 0x10, 266 kUsesColorTransform_Flag = 0x20, 267 kAlwaysOpaque_Flag = 0x40, 268 }; 269 270 SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram, 271 const Options& options, 272 const SkSL::FunctionDefinition& main, 273 std::vector<Uniform>&& uniforms, 274 std::vector<Child>&& children, 275 std::vector<SkSL::SampleUsage>&& sampleUsages, 276 uint32_t flags); 277 278 sk_sp<SkRuntimeEffect> makeUnoptimizedClone(); 279 280 static Result MakeFromSource(SkString sksl, const Options& options, SkSL::ProgramKind kind); 281 282 static Result MakeFromDSL(std::unique_ptr<SkSL::Program> program, 283 const Options& options, 284 SkSL::ProgramKind kind); 285 286 static sk_sp<SkRuntimeEffect> MakeFromDSL(std::unique_ptr<SkSL::Program> program, 287 const Options& options, 288 SkSL::ProgramKind kind, 289 SkSL::ErrorReporter* errors); 290 291 static Result MakeInternal(std::unique_ptr<SkSL::Program> program, 292 const Options& options, 293 SkSL::ProgramKind kind); 294 295 static SkSL::ProgramSettings MakeSettings(const Options& options, bool optimize); 296 hash()297 uint32_t hash() const { return fHash; } usesSampleCoords()298 bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); } allowShader()299 bool allowShader() const { return (fFlags & kAllowShader_Flag); } allowColorFilter()300 bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); } allowBlender()301 bool allowBlender() const { return (fFlags & kAllowBlender_Flag); } samplesOutsideMain()302 bool samplesOutsideMain() const { return (fFlags & kSamplesOutsideMain_Flag); } usesColorTransform()303 bool usesColorTransform() const { return (fFlags & kUsesColorTransform_Flag); } alwaysOpaque()304 bool alwaysOpaque() const { return (fFlags & kAlwaysOpaque_Flag); } 305 306 const SkFilterColorProgram* getFilterColorProgram(); 307 308 #if SK_SUPPORT_GPU 309 friend class GrSkSLFP; // fBaseProgram, fSampleUsages 310 friend class GrGLSLSkSLFP; // 311 #endif 312 313 friend class SkRTShader; // fBaseProgram, fMain 314 friend class SkRuntimeBlender; // 315 friend class SkRuntimeColorFilter; // 316 317 friend class SkFilterColorProgram; 318 friend class SkRuntimeEffectPriv; 319 320 uint32_t fHash; 321 322 std::unique_ptr<SkSL::Program> fBaseProgram; 323 const SkSL::FunctionDefinition& fMain; 324 std::vector<Uniform> fUniforms; 325 std::vector<Child> fChildren; 326 std::vector<SkSL::SampleUsage> fSampleUsages; 327 328 std::unique_ptr<SkFilterColorProgram> fFilterColorProgram; 329 330 uint32_t fFlags; // Flags 331 }; 332 333 /** Base class for SkRuntimeShaderBuilder, defined below. */ 334 class SkRuntimeEffectBuilder { 335 public: 336 struct BuilderUniform { 337 // Copy 'val' to this variable. No type conversion is performed - 'val' must be same 338 // size as expected by the effect. Information about the variable can be queried by 339 // looking at fVar. If the size is incorrect, no copy will be performed, and debug 340 // builds will abort. If this is the result of querying a missing variable, fVar will 341 // be nullptr, and assigning will also do nothing (and abort in debug builds). 342 template <typename T> 343 std::enable_if_t<std::is_trivially_copyable<T>::value, BuilderUniform&> operator=( 344 const T& val) { 345 if (!fVar) { 346 SkDEBUGFAIL("Assigning to missing variable"); 347 } else if (sizeof(val) != fVar->sizeInBytes()) { 348 SkDEBUGFAIL("Incorrect value size"); 349 } else { 350 memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), 351 &val, sizeof(val)); 352 } 353 return *this; 354 } 355 356 BuilderUniform& operator=(const SkMatrix& val) { 357 if (!fVar) { 358 SkDEBUGFAIL("Assigning to missing variable"); 359 } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { 360 SkDEBUGFAIL("Incorrect value size"); 361 } else { 362 float* data = SkTAddOffset<float>(fOwner->writableUniformData(), 363 (ptrdiff_t)fVar->offset); 364 data[0] = val.get(0); data[1] = val.get(3); data[2] = val.get(6); 365 data[3] = val.get(1); data[4] = val.get(4); data[5] = val.get(7); 366 data[6] = val.get(2); data[7] = val.get(5); data[8] = val.get(8); 367 } 368 return *this; 369 } 370 371 template <typename T> setBuilderUniform372 bool set(const T val[], const int count) { 373 static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable"); 374 if (!fVar) { 375 SkDEBUGFAIL("Assigning to missing variable"); 376 return false; 377 } else if (sizeof(T) * count != fVar->sizeInBytes()) { 378 SkDEBUGFAIL("Incorrect value size"); 379 return false; 380 } else { 381 memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), 382 val, sizeof(T) * count); 383 } 384 return true; 385 } 386 387 SkRuntimeEffectBuilder* fOwner; 388 const SkRuntimeEffect::Uniform* fVar; // nullptr if the variable was not found 389 }; 390 391 struct BuilderChild { 392 template <typename T> BuilderChild& operator=(sk_sp<T> val) { 393 if (!fChild) { 394 SkDEBUGFAIL("Assigning to missing child"); 395 } else { 396 fOwner->fChildren[(size_t)fChild->index] = std::move(val); 397 } 398 return *this; 399 } 400 401 BuilderChild& operator=(std::nullptr_t) { 402 if (!fChild) { 403 SkDEBUGFAIL("Assigning to missing child"); 404 } else { 405 fOwner->fChildren[(size_t)fChild->index] = SkRuntimeEffect::ChildPtr{}; 406 } 407 return *this; 408 } 409 410 SkRuntimeEffectBuilder* fOwner; 411 const SkRuntimeEffect::Child* fChild; // nullptr if the child was not found 412 }; 413 effect()414 const SkRuntimeEffect* effect() const { return fEffect.get(); } 415 uniform(const char * name)416 BuilderUniform uniform(const char* name) { return { this, fEffect->findUniform(name) }; } child(const char * name)417 BuilderChild child(const char* name) { 418 const SkRuntimeEffect::Child* child = fEffect->findChild(name); 419 return { this, child }; 420 } 421 422 protected: 423 SkRuntimeEffectBuilder() = delete; SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)424 explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect) 425 : fEffect(std::move(effect)) 426 , fUniforms(SkData::MakeZeroInitialized(fEffect->uniformSize())) 427 , fChildren(fEffect->children().size()) {} SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms)428 explicit SkRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms) 429 : fEffect(std::move(effect)) 430 , fUniforms(std::move(uniforms)) 431 , fChildren(fEffect->children().size()) {} 432 433 SkRuntimeEffectBuilder(SkRuntimeEffectBuilder&&) = default; 434 SkRuntimeEffectBuilder(const SkRuntimeEffectBuilder&) = default; 435 436 SkRuntimeEffectBuilder& operator=(SkRuntimeEffectBuilder&&) = delete; 437 SkRuntimeEffectBuilder& operator=(const SkRuntimeEffectBuilder&) = delete; 438 uniforms()439 sk_sp<SkData> uniforms() { return fUniforms; } children()440 SkRuntimeEffect::ChildPtr* children() { return fChildren.data(); } numChildren()441 size_t numChildren() { return fChildren.size(); } 442 443 private: writableUniformData()444 void* writableUniformData() { 445 if (!fUniforms->unique()) { 446 fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); 447 } 448 return fUniforms->writable_data(); 449 } 450 451 sk_sp<SkRuntimeEffect> fEffect; 452 sk_sp<SkData> fUniforms; 453 std::vector<SkRuntimeEffect::ChildPtr> fChildren; 454 }; 455 456 /** 457 * SkRuntimeShaderBuilder is a utility to simplify creating SkShader objects from SkRuntimeEffects. 458 * 459 * NOTE: Like SkRuntimeEffect, this API is experimental and subject to change! 460 * 461 * Given an SkRuntimeEffect, the SkRuntimeShaderBuilder manages creating an input data block and 462 * provides named access to the 'uniform' variables in that block, as well as named access 463 * to a list of child shader slots. Usage: 464 * 465 * sk_sp<SkRuntimeEffect> effect = ...; 466 * SkRuntimeShaderBuilder builder(effect); 467 * builder.uniform("some_uniform_float") = 3.14f; 468 * builder.uniform("some_uniform_matrix") = SkM44::Rotate(...); 469 * builder.child("some_child_effect") = mySkImage->makeShader(...); 470 * ... 471 * sk_sp<SkShader> shader = builder.makeShader(nullptr, false); 472 * 473 * Note that SkRuntimeShaderBuilder is built entirely on the public API of SkRuntimeEffect, 474 * so can be used as-is or serve as inspiration for other interfaces or binding techniques. 475 */ 476 class SK_API SkRuntimeShaderBuilder : public SkRuntimeEffectBuilder { 477 public: 478 explicit SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect>); 479 // This is currently required by Android Framework but may go away if that dependency 480 // can be removed. 481 SkRuntimeShaderBuilder(const SkRuntimeShaderBuilder&) = default; 482 ~SkRuntimeShaderBuilder(); 483 484 sk_sp<SkShader> makeShader(const SkMatrix* localMatrix = nullptr); 485 sk_sp<SkImage> makeImage(GrRecordingContext*, 486 const SkMatrix* localMatrix, 487 SkImageInfo resultInfo, 488 bool mipmapped); 489 490 private: 491 using INHERITED = SkRuntimeEffectBuilder; 492 SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms)493 explicit SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms) 494 : INHERITED(std::move(effect), std::move(uniforms)) {} 495 496 friend class SkRuntimeImageFilter; 497 }; 498 499 /** 500 * SkRuntimeBlendBuilder is a utility to simplify creation and uniform setup of runtime blenders. 501 */ 502 class SK_API SkRuntimeBlendBuilder : public SkRuntimeEffectBuilder { 503 public: 504 explicit SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect>); 505 ~SkRuntimeBlendBuilder(); 506 507 SkRuntimeBlendBuilder(const SkRuntimeBlendBuilder&) = delete; 508 SkRuntimeBlendBuilder& operator=(const SkRuntimeBlendBuilder&) = delete; 509 510 sk_sp<SkBlender> makeBlender(); 511 512 private: 513 using INHERITED = SkRuntimeEffectBuilder; 514 }; 515 516 #endif // SK_ENABLE_SKSL 517 518 #endif // SkRuntimeEffect_DEFINED 519