| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef GrSkSLFP_DEFINED |
| #define GrSkSLFP_DEFINED |
| |
| #include "include/core/SkRefCnt.h" |
| #include "include/effects/SkRuntimeEffect.h" |
| #include "include/gpu/GrContextOptions.h" |
| #include "src/gpu/GrFragmentProcessor.h" |
| |
| #include <atomic> |
| #include <utility> |
| #include <vector> |
| |
| class GrShaderCaps; |
| class SkData; |
| class SkRuntimeEffect; |
| |
| #ifdef SK_DEBUG |
| // UNIFORM_TYPE allows C++ types to be mapped onto SkRuntimeEffect::Uniform::Type |
| template <typename T> struct GrFPUniformType { |
| template <typename U> struct add_a_UNIFORM_TYPE_specialization_for {}; |
| static constexpr add_a_UNIFORM_TYPE_specialization_for<T> value = {}; |
| }; |
| #define UNIFORM_TYPE(T, E) \ |
| template <> struct GrFPUniformType<T> { \ |
| static constexpr SkRuntimeEffect::Uniform::Type value = SkRuntimeEffect::Uniform::Type::E; \ |
| } |
| |
| UNIFORM_TYPE(float, kFloat); |
| UNIFORM_TYPE(SkV2, kFloat2); |
| UNIFORM_TYPE(SkPMColor4f, kFloat4); |
| UNIFORM_TYPE(SkV4, kFloat4); |
| UNIFORM_TYPE(int, kInt); |
| |
| #undef UNIFORM_TYPE |
| #endif |
| |
| class GrSkSLFP : public GrFragmentProcessor { |
| public: |
| template <typename T> struct GrSpecializedUniform { |
| bool specialize; |
| T value; |
| }; |
| template <typename T> |
| static GrSpecializedUniform<T> Specialize(const T& value) { |
| return {true, value}; |
| } |
| template <typename T> |
| static GrSpecializedUniform<T> SpecializeIf(bool condition, const T& value) { |
| return {condition, value}; |
| } |
| |
| enum UniformFlags : uint8_t { |
| kSpecialize_Flag = 0x1, |
| }; |
| |
| /** |
| * Both factories support a single 'input' FP, as well as a collection of other 'child' FPs. |
| * The 'child' FPs correspond to the children declared in the effect's SkSL. The inputFP is |
| * optional, and intended for instances that have color filter semantics. This is an implicit |
| * child - if present, it's evaluated to produce the input color fed to the SkSL. Otherwise, |
| * the SkSL receives this FP's input color directly. |
| */ |
| |
| /** |
| * Creates a new fragment processor from an SkRuntimeEffect and a data blob containing values |
| * for all of the 'uniform' variables in the SkSL source. The layout of the uniforms blob is |
| * dictated by the SkRuntimeEffect. |
| */ |
| static std::unique_ptr<GrSkSLFP> MakeWithData( |
| sk_sp<SkRuntimeEffect> effect, |
| const char* name, |
| std::unique_ptr<GrFragmentProcessor> inputFP, |
| sk_sp<SkData> uniforms, |
| SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs); |
| |
| /* |
| * Constructs a GrSkSLFP from a series of name-value pairs, corresponding to the children and |
| * uniform data members of the effect's SkSL. |
| * The variable length args... must contain all of the children and uniforms expected. |
| * Each individual argument must be preceded by a name that matches the SkSL name of the value |
| * being set. For children, the next argument must be a std::unique_ptr<GrFragmentProcessor>. |
| * For uniforms, the next argument must be data of the correct size and type. |
| * |
| * For example, given: |
| * uniform shader input; |
| * uniform float scale; |
| * uniform half2 pt; |
| * half4 main() { ... } |
| * |
| * A call to GrSkSLFP would be formatted like: |
| * std::unique_ptr<GrFragmentProcessor> child = ...; |
| * float scaleVal = ...; |
| * SkV2 ptVal = ...; |
| * auto fp = GrSkSLFP::Make(effect, "my_effect", nullptr, |
| * "input", std::move(child), |
| * "scale", scaleVal, |
| * "pt", ptVal); |
| * |
| * The uniforms must appear in the correct order, as must the children. Technically, the two |
| * lists can be interleaved. In debug builds, the number, names, and sizes of all arguments are |
| * checked with assertions. In release builds, all checks are elided. In either case, the |
| * uniform data is directly copied into the footer allocated after the FP. |
| */ |
| template <typename... Args> |
| static std::unique_ptr<GrSkSLFP> Make(sk_sp<SkRuntimeEffect> effect, |
| const char* name, |
| std::unique_ptr<GrFragmentProcessor> inputFP, |
| Args&&... args) { |
| #ifdef SK_DEBUG |
| checkArgs(effect->fUniforms.begin(), |
| effect->fUniforms.end(), |
| effect->fChildren.begin(), |
| effect->fChildren.end(), |
| std::forward<Args>(args)...); |
| #endif |
| |
| size_t uniformPayloadSize = UniformPayloadSize(effect.get()); |
| std::unique_ptr<GrSkSLFP> fp(new (uniformPayloadSize) GrSkSLFP(std::move(effect), name)); |
| fp->appendArgs(fp->uniformData(), fp->uniformFlags(), std::forward<Args>(args)...); |
| if (inputFP) { |
| fp->setInput(std::move(inputFP)); |
| } |
| return fp; |
| } |
| |
| const char* name() const override { return fName; } |
| std::unique_ptr<GrFragmentProcessor> clone() const override; |
| |
| private: |
| GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name); |
| GrSkSLFP(const GrSkSLFP& other); |
| |
| void addChild(std::unique_ptr<GrFragmentProcessor> child); |
| void setInput(std::unique_ptr<GrFragmentProcessor> input); |
| |
| std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override; |
| |
| void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; |
| |
| bool onIsEqual(const GrFragmentProcessor&) const override; |
| |
| SkPMColor4f constantOutputForConstantInput(const SkPMColor4f&) const override; |
| |
| // An instance of GrSkSLFP is always allocated with a payload immediately following the FP. |
| // First the the values of all the uniforms, and then a set of flags (one per uniform). |
| static size_t UniformPayloadSize(const SkRuntimeEffect* effect) { |
| return effect->uniformSize() + effect->uniforms().count() * sizeof(UniformFlags); |
| } |
| |
| const uint8_t* uniformData() const { return reinterpret_cast<const uint8_t*>(this + 1); } |
| uint8_t* uniformData() { return reinterpret_cast< uint8_t*>(this + 1); } |
| |
| const UniformFlags* uniformFlags() const { |
| return reinterpret_cast<const UniformFlags*>(this->uniformData() + fUniformSize); |
| } |
| UniformFlags* uniformFlags() { |
| return reinterpret_cast<UniformFlags*>(this->uniformData() + fUniformSize); |
| } |
| |
| // Helpers to attach variadic template args to a newly constructed FP: |
| |
| void appendArgs(uint8_t* uniformDataPtr, UniformFlags* uniformFlagsPtr) { |
| // Base case -- no more args to append, so we're done |
| } |
| template <typename... Args> |
| void appendArgs(uint8_t* uniformDataPtr, |
| UniformFlags* uniformFlagsPtr, |
| const char* name, |
| std::unique_ptr<GrFragmentProcessor>&& child, |
| Args&&... remainder) { |
| // Child FP case -- register the child, then continue processing the remaining arguments. |
| // Children aren't "uniforms" here, so the data & flags pointers don't advance. |
| this->addChild(std::move(child)); |
| this->appendArgs(uniformDataPtr, uniformFlagsPtr, std::forward<Args>(remainder)...); |
| } |
| template <typename T, typename... Args> |
| void appendArgs(uint8_t* uniformDataPtr, |
| UniformFlags* uniformFlagsPtr, |
| const char* name, |
| const GrSpecializedUniform<T>& val, |
| Args&&... remainder) { |
| // Specialized uniform case -- This just handles the specialization logic. If we want to |
| // specialize on this particular value, set the flag. Then, continue processing the actual |
| // value (by just peeling off the wrapper). This lets our generic `const T&` case (below) |
| // handle copying the data into our uniform block, and advancing the per-value uniform |
| // data and flags pointers. |
| if (val.specialize) { |
| *uniformFlagsPtr = static_cast<UniformFlags>(*uniformFlagsPtr | kSpecialize_Flag); |
| } |
| this->appendArgs( |
| uniformDataPtr, uniformFlagsPtr, name, val.value, std::forward<Args>(remainder)...); |
| } |
| template <typename T, typename... Args> |
| void appendArgs(uint8_t* uniformDataPtr, |
| UniformFlags* uniformFlagsPtr, |
| const char* name, |
| const T& val, |
| Args&&... remainder) { |
| // Raw uniform value case -- We copy the supplied value into our uniform data area, |
| // then advance our uniform data and flags pointers. |
| memcpy(uniformDataPtr, &val, sizeof(val)); |
| uniformDataPtr += sizeof(val); |
| uniformFlagsPtr++; |
| this->appendArgs(uniformDataPtr, uniformFlagsPtr, std::forward<Args>(remainder)...); |
| } |
| |
| #ifdef SK_DEBUG |
| using child_iterator = std::vector<SkRuntimeEffect::Child>::const_iterator; |
| using uniform_iterator = std::vector<SkRuntimeEffect::Uniform>::const_iterator; |
| |
| // Validates that all args passed to the template factory have the right names, sizes, and types |
| static void checkArgs(uniform_iterator uIter, |
| uniform_iterator uEnd, |
| child_iterator cIter, |
| child_iterator cEnd) { |
| SkASSERTF(uIter == uEnd, "Expected more uniforms, starting with '%s'", uIter->name.c_str()); |
| SkASSERTF(cIter == cEnd, "Expected more children, starting with '%s'", cIter->name.c_str()); |
| } |
| template <typename... Args> |
| static void checkArgs(uniform_iterator uIter, |
| uniform_iterator uEnd, |
| child_iterator cIter, |
| child_iterator cEnd, |
| const char* name, |
| std::unique_ptr<GrFragmentProcessor>&& child, |
| Args&&... remainder) { |
| // NOTE: This function (necessarily) gets an rvalue reference to child, but deliberately |
| // does not use it. We leave it intact, and our caller (Make) will pass another rvalue |
| // reference to appendArgs, which will then move it to call addChild. |
| SkASSERTF(cIter != cEnd, "Too many children, wasn't expecting '%s'", name); |
| SkASSERTF(cIter->name.equals(name), |
| "Expected child '%s', got '%s' instead", |
| cIter->name.c_str(), name); |
| checkArgs(uIter, uEnd, ++cIter, cEnd, std::forward<Args>(remainder)...); |
| } |
| template <typename T, typename... Args> |
| static void checkArgs(uniform_iterator uIter, |
| uniform_iterator uEnd, |
| child_iterator cIter, |
| child_iterator cEnd, |
| const char* name, |
| const GrSpecializedUniform<T>& val, |
| Args&&... remainder) { |
| static_assert(!std::is_array<T>::value); // No specializing arrays |
| checkArgs(uIter, uEnd, cIter, cEnd, name, val.value, std::forward<Args>(remainder)...); |
| } |
| template <typename T, typename... Args> |
| static void checkArgs(uniform_iterator uIter, |
| uniform_iterator uEnd, |
| child_iterator cIter, |
| child_iterator cEnd, |
| const char* name, |
| const T& val, |
| Args&&... remainder) { |
| SkASSERTF(uIter != uEnd, "Too many uniforms, wasn't expecting '%s'", name); |
| SkASSERTF(uIter->name.equals(name), |
| "Expected uniform '%s', got '%s' instead", |
| uIter->name.c_str(), name); |
| SkASSERTF(uIter->sizeInBytes() == sizeof(val), |
| "Expected uniform '%s' to be %zu bytes, got %zu instead", |
| name, uIter->sizeInBytes(), sizeof(val)); |
| SkASSERTF(GrFPUniformType<T>::value == uIter->type, |
| "Wrong type for uniform '%s'", |
| name); |
| checkArgs(++uIter, uEnd, cIter, cEnd, std::forward<Args>(remainder)...); |
| } |
| #endif |
| |
| sk_sp<SkRuntimeEffect> fEffect; |
| const char* fName; |
| uint32_t fUniformSize; |
| int fInputChildIndex = -1; |
| |
| GR_DECLARE_FRAGMENT_PROCESSOR_TEST |
| |
| using INHERITED = GrFragmentProcessor; |
| |
| friend class GrGLSLSkSLFP; |
| |
| friend class GrSkSLFPFactory; |
| }; |
| |
| #endif |