/* * Copyright 2019 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkRuntimeEffect_DEFINED #define SkRuntimeEffect_DEFINED #include "include/core/SkData.h" #include "include/core/SkImageInfo.h" #include "include/core/SkMatrix.h" #include "include/core/SkShader.h" #include "include/core/SkString.h" #include "include/private/SkOnce.h" #include "include/private/SkSLSampleUsage.h" #include class GrFragmentProcessor; class GrRecordingContext; class SkColorFilter; class SkImage; class SkShader; namespace SkSL { class FunctionDefinition; struct Program; } // namespace SkSL namespace skvm { class Program; } // namespace skvm /* * SkRuntimeEffect supports creating custom SkShader and SkColorFilter objects using Skia's SkSL * shading language. * * NOTE: This API is experimental and subject to change. */ class SK_API SkRuntimeEffect : public SkRefCnt { public: struct Uniform { enum class Type { kFloat, kFloat2, kFloat3, kFloat4, kFloat2x2, kFloat3x3, kFloat4x4, }; enum Flags { kArray_Flag = 0x1, kMarker_Flag = 0x2, kMarkerNormals_Flag = 0x4, kSRGBUnpremul_Flag = 0x8, }; SkString name; size_t offset; Type type; int count; uint32_t flags; uint32_t marker; bool isArray() const { return SkToBool(this->flags & kArray_Flag); } size_t sizeInBytes() const; }; struct Varying { SkString name; int width; // 1 - 4 (floats) }; struct Options { // Sets an upper limit on the acceptable amount of code growth from inlining. // By default, runtime effects don't run the inliner directly. int inlineThreshold = 0; }; // If the effect is compiled successfully, `effect` will be non-null. // Otherwise, `errorText` will contain the reason for failure. struct Result { sk_sp effect; SkString errorText; }; static Result Make(SkString sksl, const Options& options); // We can't use a default argument for `options` due to a bug in Clang. // https://bugs.llvm.org/show_bug.cgi?id=36684 static Result Make(SkString sksl) { return Make(std::move(sksl), Options{}); } sk_sp makeShader(sk_sp uniforms, sk_sp children[], size_t childCount, const SkMatrix* localMatrix, bool isOpaque) const; sk_sp makeImage(GrRecordingContext*, sk_sp uniforms, sk_sp children[], size_t childCount, const SkMatrix* localMatrix, SkImageInfo resultInfo, bool mipmapped) const; sk_sp makeColorFilter(sk_sp uniforms) const; sk_sp makeColorFilter(sk_sp uniforms, sk_sp children[], size_t childCount) const; const SkString& source() const { return fSkSL; } template class ConstIterable { public: ConstIterable(const std::vector& vec) : fVec(vec) {} using const_iterator = typename std::vector::const_iterator; const_iterator begin() const { return fVec.begin(); } const_iterator end() const { return fVec.end(); } size_t count() const { return fVec.size(); } private: const std::vector& fVec; }; // Combined size of all 'uniform' variables. When calling makeColorFilter or makeShader, // provide an SkData of this size, containing values for all of those variables. size_t uniformSize() const; ConstIterable uniforms() const { return ConstIterable(fUniforms); } ConstIterable children() const { return ConstIterable(fChildren); } ConstIterable varyings() const { return ConstIterable(fVaryings); } // Returns pointer to the named uniform variable's description, or nullptr if not found const Uniform* findUniform(const char* name) const; // Returns index of the named child, or -1 if not found int findChild(const char* name) const; static void RegisterFlattenables(); ~SkRuntimeEffect() override; #if SK_SUPPORT_GPU // For internal use. std::unique_ptr makeFP(GrRecordingContext*, sk_sp uniforms, std::unique_ptr children[], size_t childCount) const; #endif private: SkRuntimeEffect(SkString sksl, std::unique_ptr baseProgram, const SkSL::FunctionDefinition& main, std::vector&& uniforms, std::vector&& children, std::vector&& sampleUsages, std::vector&& varyings, bool usesSampleCoords, bool allowColorFilter); uint32_t hash() const { return fHash; } bool usesSampleCoords() const { return fUsesSampleCoords; } const skvm::Program* getFilterColorProgram(); #if SK_SUPPORT_GPU friend class GrSkSLFP; // fBaseProgram, fSampleUsages friend class GrGLSLSkSLFP; // #endif friend class SkRTShader; // fBaseProgram, fMain friend class SkRuntimeColorFilter; // uint32_t fHash; SkString fSkSL; std::unique_ptr fBaseProgram; const SkSL::FunctionDefinition& fMain; std::vector fUniforms; std::vector fChildren; std::vector fSampleUsages; std::vector fVaryings; SkOnce fColorFilterProgramOnce; std::unique_ptr fColorFilterProgram; bool fUsesSampleCoords; bool fAllowColorFilter; }; /** Base class for SkRuntimeShaderBuilder, defined below. */ template class SkRuntimeEffectBuilder { public: struct BuilderUniform { // Copy 'val' to this variable. No type conversion is performed - 'val' must be same // size as expected by the effect. Information about the variable can be queried by // looking at fVar. If the size is incorrect, no copy will be performed, and debug // builds will abort. If this is the result of querying a missing variable, fVar will // be nullptr, and assigning will also do nothing (and abort in debug builds). template std::enable_if_t::value, BuilderUniform&> operator=( const T& val) { if (!fVar) { SkDEBUGFAIL("Assigning to missing variable"); } else if (sizeof(val) != fVar->sizeInBytes()) { SkDEBUGFAIL("Incorrect value size"); } else { memcpy(SkTAddOffset(fOwner->writableUniformData(), fVar->offset), &val, sizeof(val)); } return *this; } BuilderUniform& operator=(const SkMatrix& val) { if (!fVar) { SkDEBUGFAIL("Assigning to missing variable"); } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { SkDEBUGFAIL("Incorrect value size"); } else { float* data = SkTAddOffset(fOwner->writableUniformData(), fVar->offset); data[0] = val.get(0); data[1] = val.get(3); data[2] = val.get(6); data[3] = val.get(1); data[4] = val.get(4); data[5] = val.get(7); data[6] = val.get(2); data[7] = val.get(5); data[8] = val.get(8); } return *this; } template bool set(const T val[], const int count) { static_assert(std::is_trivially_copyable::value, "Value must be trivial copyable"); if (!fVar) { SkDEBUGFAIL("Assigning to missing variable"); return false; } else if (sizeof(T) * count != fVar->sizeInBytes()) { SkDEBUGFAIL("Incorrect value size"); return false; } else { memcpy(SkTAddOffset(fOwner->writableUniformData(), fVar->offset), val, sizeof(T) * count); } return true; } SkRuntimeEffectBuilder* fOwner; const SkRuntimeEffect::Uniform* fVar; // nullptr if the variable was not found }; struct BuilderChild { template BuilderChild& operator=(C&& val) { if (fIndex < 0) { SkDEBUGFAIL("Assigning to missing child"); } else { fOwner->fChildren[fIndex] = std::forward(val); } return *this; } SkRuntimeEffectBuilder* fOwner; int fIndex; // -1 if the child was not found }; const SkRuntimeEffect* effect() const { return fEffect.get(); } BuilderUniform uniform(const char* name) { return { this, fEffect->findUniform(name) }; } BuilderChild child(const char* name) { return { this, fEffect->findChild(name) }; } protected: explicit SkRuntimeEffectBuilder(sk_sp effect) : fEffect(std::move(effect)) , fUniforms(SkData::MakeUninitialized(fEffect->uniformSize())) , fChildren(fEffect->children().count()) {} SkRuntimeEffectBuilder(SkRuntimeEffectBuilder&&) = default; SkRuntimeEffectBuilder(const SkRuntimeEffectBuilder&) = delete; SkRuntimeEffectBuilder& operator=(SkRuntimeEffectBuilder&&) = default; SkRuntimeEffectBuilder& operator=(const SkRuntimeEffectBuilder&) = delete; sk_sp uniforms() { return fUniforms; } Child* children() { return fChildren.data(); } size_t numChildren() { return fChildren.size(); } private: void* writableUniformData() { if (!fUniforms->unique()) { fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); } return fUniforms->writable_data(); } sk_sp fEffect; sk_sp fUniforms; std::vector fChildren; }; /** * SkRuntimeShaderBuilder is a utility to simplify creating SkShader objects from SkRuntimeEffects. * * NOTE: Like SkRuntimeEffect, this API is experimental and subject to change! * * Given an SkRuntimeEffect, the SkRuntimeShaderBuilder manages creating an input data block and * provides named access to the 'uniform' variables in that block, as well as named access * to a list of child shader slots. Usage: * * sk_sp effect = ...; * SkRuntimeShaderBuilder builder(effect); * builder.uniform("some_uniform_float") = 3.14f; * builder.uniform("some_uniform_matrix") = SkM44::Rotate(...); * builder.child("some_child_effect") = mySkImage->makeShader(...); * ... * sk_sp shader = builder.makeShader(nullptr, false); * * Note that SkRuntimeShaderBuilder is built entirely on the public API of SkRuntimeEffect, * so can be used as-is or serve as inspiration for other interfaces or binding techniques. */ class SK_API SkRuntimeShaderBuilder : public SkRuntimeEffectBuilder> { public: explicit SkRuntimeShaderBuilder(sk_sp); ~SkRuntimeShaderBuilder(); sk_sp makeShader(const SkMatrix* localMatrix, bool isOpaque); sk_sp makeImage(GrRecordingContext*, const SkMatrix* localMatrix, SkImageInfo resultInfo, bool mipmapped); private: using INHERITED = SkRuntimeEffectBuilder>; }; #endif