/* * 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/SkMatrix.h" #include "include/core/SkString.h" #include "include/private/GrTypesPriv.h" #include "include/private/SkSLSampleUsage.h" #include #include #if SK_SUPPORT_GPU #include "include/gpu/GrContextOptions.h" #endif class GrShaderCaps; class SkColorFilter; class SkShader; namespace SkSL { class ByteCode; struct PipelineStageArgs; struct Program; class SharedCompiler; } // namespace SkSL /* * 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 Variable { enum class Qualifier { kUniform, kIn, }; enum class Type { kBool, kInt, kFloat, kFloat2, kFloat3, kFloat4, kFloat2x2, kFloat3x3, kFloat4x4, }; enum Flags { kArray_Flag = 0x1, kMarker_Flag = 0x2, kMarkerNormals_Flag = 0x4, kSRGBUnpremul_Flag = 0x8, }; SkString fName; size_t fOffset; Qualifier fQualifier; Type fType; GrSLType fGPUType; int fCount; uint32_t fFlags; uint32_t fMarker; bool isArray() const { return SkToBool(fFlags & kArray_Flag); } size_t sizeInBytes() const; }; struct Varying { SkString fName; int fWidth; // 1 - 4 (floats) }; // [Effect, ErrorText] // If successful, Effect != nullptr, otherwise, ErrorText contains the reason for failure. using EffectResult = std::tuple, SkString>; static EffectResult Make(SkString sksl); sk_sp makeShader(sk_sp inputs, sk_sp children[], size_t childCount, const SkMatrix* localMatrix, bool isOpaque); sk_sp makeColorFilter(sk_sp inputs); sk_sp makeColorFilter(sk_sp inputs, sk_sp children[], size_t childCount); const SkString& source() const { return fSkSL; } uint32_t hash() const { return fHash; } 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 'in' and 'uniform' variables. When calling makeColorFilter or // makeShader, provide an SkData of this size, containing values for all of those variables. size_t inputSize() const; ConstIterable inputs() const { return ConstIterable(fInAndUniformVars); } ConstIterable children() const { return ConstIterable(fChildren); } ConstIterable varyings() const { return ConstIterable(fVaryings); } // Returns pointer to the named in/uniform variable's description, or nullptr if not found const Variable* findInput(const char* name) const; // Returns index of the named child, or -1 if not found int findChild(const char* name) const; bool usesSampleCoords() const { return fUsesSampleCoords; } static void RegisterFlattenables(); ~SkRuntimeEffect() override; private: SkRuntimeEffect(SkString sksl, std::unique_ptr baseProgram, std::vector&& inAndUniformVars, std::vector&& children, std::vector&& sampleUsages, std::vector&& varyings, size_t uniformSize, bool usesSampleCoords, bool allowColorFilter); using SpecializeResult = std::tuple, SkString>; SpecializeResult specialize(SkSL::Program& baseProgram, const void* inputs, const SkSL::SharedCompiler&) const; #if SK_SUPPORT_GPU friend class GrSkSLFP; // toPipelineStage friend class GrGLSLSkSLFP; // fSampleUsages // This re-compiles the program from scratch, using the supplied shader caps. // This is necessary to get the correct values of settings. bool toPipelineStage(const void* inputs, const GrShaderCaps* shaderCaps, GrContextOptions::ShaderErrorHandler* errorHandler, SkSL::PipelineStageArgs* outArgs); #endif friend class SkRTShader; // toByteCode & uniformSize friend class SkRuntimeColorFilter; // // [ByteCode, ErrorText] // If successful, ByteCode != nullptr, otherwise, ErrorText contains the reason for failure. using ByteCodeResult = std::tuple, SkString>; ByteCodeResult toByteCode(const void* inputs) const; // Combined size of just the 'uniform' variables. size_t uniformSize() const { return fUniformSize; } uint32_t fHash; SkString fSkSL; std::unique_ptr fBaseProgram; std::vector fInAndUniformVars; std::vector fChildren; std::vector fSampleUsages; std::vector fVaryings; size_t fUniformSize; bool fUsesSampleCoords; bool fAllowColorFilter; }; /** * 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 'in' and '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.input("some_uniform_float") = 3.14f; * builder.input("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. */ struct SkRuntimeShaderBuilder { SkRuntimeShaderBuilder(sk_sp); ~SkRuntimeShaderBuilder(); struct BuilderInput { // 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, BuilderInput&> 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->fInputs->writable_data(), fVar->fOffset), &val, sizeof(val)); } return *this; } BuilderInput& 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->fInputs->writable_data(), fVar->fOffset); 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; } SkRuntimeShaderBuilder* fOwner; const SkRuntimeEffect::Variable* fVar; // nullptr if the variable was not found }; struct BuilderChild { BuilderChild& operator=(const sk_sp& val); SkRuntimeShaderBuilder* fOwner; int fIndex; // -1 if the child was not found }; BuilderInput input(const char* name) { return { this, fEffect->findInput(name) }; } BuilderChild child(const char* name) { return { this, fEffect->findChild(name) }; } sk_sp makeShader(const SkMatrix* localMatrix, bool isOpaque); sk_sp fEffect; sk_sp fInputs; std::vector> fChildren; }; #endif