| /* |
| * 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/core/SkRefCnt.h" |
| #include "include/core/SkString.h" |
| #include "include/effects/SkRuntimeEffect.h" |
| #include "include/private/SkSLSampleUsage.h" |
| #include "include/private/base/SkAssert.h" |
| #include "include/private/base/SkSpan_impl.h" |
| #include "include/private/base/SkTArray.h" |
| #include "src/shaders/SkShaderBase.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <functional> |
| #include <memory> |
| #include <vector> |
| |
| #ifdef SK_ENABLE_SKSL |
| #include "include/sksl/SkSLVersion.h" |
| |
| #ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE |
| #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h" |
| #endif |
| |
| #ifdef SK_ENABLE_SKVM |
| #include "src/sksl/codegen/SkSLVMCodeGenerator.h" |
| #endif |
| |
| class SkArenaAlloc; |
| class SkColorSpace; |
| class SkData; |
| class SkMatrix; |
| class SkReadBuffer; |
| class SkShader; |
| class SkWriteBuffer; |
| struct SkStageRec; |
| |
| 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<sk_sp<const SkData>(const UniformsCallbackContext&)>; |
| static sk_sp<SkShader> MakeDeferredShader(const SkRuntimeEffect* effect, |
| UniformsCallback uniformsCallback, |
| SkSpan<SkRuntimeEffect::ChildPtr> children, |
| const SkMatrix* localMatrix = nullptr); |
| |
| // Helper function when creating an effect for a GrSkSLFP that verifies an effect will |
| // implement the GrFragmentProcessor "constant output for constant input" optimization flag. |
| static bool SupportsConstantOutputForConstantInput(const SkRuntimeEffect* effect) { |
| // This optimization is only implemented for color filters without any children. |
| if (!effect->allowColorFilter() || !effect->children().empty()) { |
| return false; |
| } |
| #if defined(SK_ENABLE_SKVM) |
| return effect->getFilterColorProgram(); |
| #else |
| return true; |
| #endif |
| } |
| |
| 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<const SkData> TransformUniforms(SkSpan<const SkRuntimeEffect::Uniform> uniforms, |
| sk_sp<const SkData> originalData, |
| const SkColorSpaceXformSteps&); |
| static sk_sp<const SkData> TransformUniforms(SkSpan<const SkRuntimeEffect::Uniform> uniforms, |
| sk_sp<const SkData> originalData, |
| const SkColorSpace* dstCS); |
| static SkSpan<const float> UniformsAsSpan( |
| SkSpan<const SkRuntimeEffect::Uniform> uniforms, |
| sk_sp<const SkData> originalData, |
| bool alwaysCopyIntoAlloc, |
| const SkColorSpace* destColorSpace, |
| SkArenaAlloc* alloc); |
| |
| static bool CanDraw(const SkCapabilities*, const SkSL::Program*); |
| static bool CanDraw(const SkCapabilities*, const SkRuntimeEffect*); |
| |
| static bool ReadChildEffects(SkReadBuffer& buffer, |
| const SkRuntimeEffect* effect, |
| skia_private::TArray<SkRuntimeEffect::ChildPtr>* children); |
| static void WriteChildEffects(SkWriteBuffer &buffer, |
| const std::vector<SkRuntimeEffect::ChildPtr> &children); |
| |
| #ifdef SK_ENABLE_SKVM |
| static std::vector<skvm::Val> MakeSkVMUniforms(skvm::Builder*, |
| skvm::Uniforms*, |
| size_t inputSize, |
| const SkData& inputs); |
| #endif |
| |
| #if defined(SK_GRAPHITE) |
| static void AddChildrenToKey(SkSpan<const SkRuntimeEffect::ChildPtr> children, |
| SkSpan<const SkRuntimeEffect::Child> childInfo, |
| const skgpu::graphite::KeyContext& keyContext, |
| skgpu::graphite::PaintParamsKeyBuilder* builder, |
| skgpu::graphite::PipelineDataGatherer* gatherer); |
| #endif |
| }; |
| |
| // 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<SkRuntimeEffect> SkMakeCachedRuntimeEffect( |
| SkRuntimeEffect::Result (*make)(SkString sksl, const SkRuntimeEffect::Options&), |
| SkString sksl); |
| |
| inline sk_sp<SkRuntimeEffect> 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(); |
| } |
| |
| #ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE |
| class RuntimeEffectRPCallbacks : public SkSL::RP::Callbacks { |
| public: |
| RuntimeEffectRPCallbacks(const SkStageRec& s, |
| const SkShaderBase::MatrixRec& m, |
| SkSpan<const SkRuntimeEffect::ChildPtr> c, |
| SkSpan<const SkSL::SampleUsage> u) |
| : fStage(s), fMatrix(m), fChildren(c), fSampleUsages(u) {} |
| |
| bool appendShader(int index) override; |
| bool appendColorFilter(int index) override; |
| bool appendBlender(int index) override; |
| |
| // TODO: If an effect calls these intrinsics more than once, we could cache and re-use the steps |
| // object(s), rather than re-creating them in the arena repeatedly. |
| void toLinearSrgb(const void* color) override; |
| |
| void fromLinearSrgb(const void* color) override; |
| |
| private: |
| void applyColorSpaceXform(const SkColorSpaceXformSteps& tempXform, const void* color); |
| |
| const SkStageRec& fStage; |
| const SkShaderBase::MatrixRec& fMatrix; |
| SkSpan<const SkRuntimeEffect::ChildPtr> fChildren; |
| SkSpan<const SkSL::SampleUsage> fSampleUsages; |
| }; |
| #endif // SK_ENABLE_SKSL_IN_RASTER_PIPELINE |
| |
| #if defined(SK_ENABLE_SKVM) |
| class RuntimeEffectVMCallbacks : public SkSL::SkVMCallbacks { |
| public: |
| RuntimeEffectVMCallbacks(skvm::Builder* builder, |
| skvm::Uniforms* uniforms, |
| SkArenaAlloc* alloc, |
| const std::vector<SkRuntimeEffect::ChildPtr>& children, |
| const SkShaderBase::MatrixRec& mRec, |
| skvm::Color inColor, |
| const SkColorInfo& colorInfo) |
| : fBuilder(builder) |
| , fUniforms(uniforms) |
| , fAlloc(alloc) |
| , fChildren(children) |
| , fMRec(mRec) |
| , fInColor(inColor) |
| , fColorInfo(colorInfo) {} |
| |
| skvm::Color sampleShader(int ix, skvm::Coord coord) override; |
| |
| skvm::Color sampleColorFilter(int ix, skvm::Color color) override; |
| |
| skvm::Color sampleBlender(int ix, skvm::Color src, skvm::Color dst) override; |
| |
| skvm::Color toLinearSrgb(skvm::Color color) override; |
| |
| skvm::Color fromLinearSrgb(skvm::Color color) override; |
| |
| skvm::Builder* fBuilder; |
| skvm::Uniforms* fUniforms; |
| SkArenaAlloc* fAlloc; |
| const std::vector<SkRuntimeEffect::ChildPtr>& fChildren; |
| const SkShaderBase::MatrixRec& fMRec; |
| const skvm::Color fInColor; |
| const SkColorInfo& fColorInfo; |
| }; |
| #endif // defined(SK_ENABLE_SKVM) |
| |
| #endif // SK_ENABLE_SKSL |
| |
| #endif // SkRuntimeEffectPriv_DEFINED |