/* * Copyright 2020 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkRuntimeEffectPriv_DEFINED #define SkRuntimeEffectPriv_DEFINED #include "include/effects/SkRuntimeEffect.h" #include "include/private/SkColorData.h" #include "src/core/SkVM.h" #include #ifdef SK_ENABLE_SKSL namespace SkSL { class Context; class Variable; struct Program; } class SkCapabilities; struct SkColorSpaceXformSteps; class SkRuntimeEffectPriv { public: struct UniformsCallbackContext { const SkColorSpace* fDstColorSpace; }; // Private (experimental) API for creating runtime shaders with late-bound uniforms. // The callback must produce a uniform data blob of the correct size for the effect. // It is invoked at "draw" time (essentially, when a draw call is made against the canvas // using the resulting shader). There are no strong guarantees about timing. // Serializing the resulting shader will immediately invoke the callback (and record the // resulting uniforms). using UniformsCallback = std::function(const UniformsCallbackContext&)>; static sk_sp MakeDeferredShader(const SkRuntimeEffect* effect, UniformsCallback uniformsCallback, SkSpan children, const SkMatrix* localMatrix = nullptr); // Helper function when creating an effect for a GrSkSLFP that verifies an effect will // implement the constant output for constant input optimization flag. static bool SupportsConstantOutputForConstantInput(const SkRuntimeEffect* effect) { return effect->getFilterColorProgram(); } static uint32_t Hash(const SkRuntimeEffect& effect) { return effect.hash(); } static const SkSL::Program& Program(const SkRuntimeEffect& effect) { return *effect.fBaseProgram; } static SkRuntimeEffect::Options ES3Options() { SkRuntimeEffect::Options options; options.maxVersionAllowed = SkSL::Version::k300; return options; } static void AllowPrivateAccess(SkRuntimeEffect::Options* options) { options->allowPrivateAccess = true; } static SkRuntimeEffect::Uniform VarAsUniform(const SkSL::Variable&, const SkSL::Context&, size_t* offset); // If there are layout(color) uniforms then this performs color space transformation on the // color values and returns a new SkData. Otherwise, the original data is returned. static sk_sp TransformUniforms(SkSpan uniforms, sk_sp originalData, const SkColorSpaceXformSteps&); static sk_sp TransformUniforms(SkSpan uniforms, sk_sp originalData, const SkColorSpace* dstCS); static bool CanDraw(const SkCapabilities*, const SkSL::Program*); static bool CanDraw(const SkCapabilities*, const SkRuntimeEffect*); }; // These internal APIs for creating runtime effects vary from the public API in two ways: // // 1) they're used in contexts where it's not useful to receive an error message; // 2) they're cached. // // Users of the public SkRuntimeEffect::Make*() can of course cache however they like themselves; // keeping these APIs private means users will not be forced into our cache or cache policy. sk_sp SkMakeCachedRuntimeEffect( SkRuntimeEffect::Result (*make)(SkString sksl, const SkRuntimeEffect::Options&), SkString sksl); inline sk_sp SkMakeCachedRuntimeEffect( SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&), const char* sksl) { return SkMakeCachedRuntimeEffect(make, SkString{sksl}); } // Internal API that assumes (and asserts) that the shader code is valid, but does no internal // caching. Used when the caller will cache the result in a static variable. Ownership is passed to // the caller; the effect will be leaked if it the pointer is not stored or explicitly deleted. inline SkRuntimeEffect* SkMakeRuntimeEffect( SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&), const char* sksl, SkRuntimeEffect::Options options = SkRuntimeEffect::Options{}) { #if defined(SK_DEBUG) // Our SKSL snippets we embed in Skia should not have comments or excess indentation. // Removing them helps trim down code size and speeds up parsing if (SkStrContains(sksl, "//") || SkStrContains(sksl, " ")) { SkDEBUGFAILF("Found SkSL snippet that can be minified: \n %s\n", sksl); } #endif SkRuntimeEffectPriv::AllowPrivateAccess(&options); auto result = make(SkString{sksl}, options); if (!result.effect) { SK_ABORT("%s", result.errorText.c_str()); } return result.effect.release(); } /** * Runtime effects are often long lived & cached. Individual color filters or FPs created from them * and are often short-lived. However, color filters and FPs may need to operate on a single color * (on the CPU). This may be done at the paint level (eg, filter the paint color), or as part of * FP tree analysis. * * SkFilterColorProgram is an skvm program representing a (color filter) SkRuntimeEffect. It can * process a single color, without knowing the details of a particular instance (uniform values or * children). */ class SkFilterColorProgram { public: static std::unique_ptr Make(const SkRuntimeEffect* effect); SkPMColor4f eval(const SkPMColor4f& inColor, const void* uniformData, std::function evalChild) const; bool isAlphaUnchanged() const { return fAlphaUnchanged; } private: struct SampleCall { enum class Kind { kInputColor, // eg child.eval(inputColor) kImmediate, // eg child.eval(half4(1)) kPrevious, // eg child1.eval(child2.eval(...)) kUniform, // eg uniform half4 color; ... child.eval(color) }; int fChild; Kind fKind; union { SkPMColor4f fImm; // for kImmediate int fPrevious; // for kPrevious int fOffset; // for kUniform }; }; SkFilterColorProgram(skvm::Program program, std::vector sampleCalls, bool alphaUnchanged); skvm::Program fProgram; std::vector fSampleCalls; bool fAlphaUnchanged; }; #endif // SK_ENABLE_SKSL #endif // SkRuntimeEffectPriv_DEFINED