Reduce size of ChildPtr object.

We take advantage of their shared SkFlattenable lineage to store all
three types of object in the same sk_sp, and check their
Flattenable type to distinguish between them. Accessors allow us to
mimic the coding style of having three distinct fields.

Change-Id: I16426377f10fa2abffbf09bb7998c968614433df
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/435019
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index a5c3e3b..08c8e8d 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -18,6 +18,7 @@
 #include "include/core/SkString.h"
 #include "include/private/SkOnce.h"
 #include "include/private/SkSLSampleUsage.h"
+#include "include/private/SkTOptional.h"
 
 #include <string>
 #include <vector>
@@ -78,16 +79,16 @@
     };
 
     // Reflected description of a uniform child (shader or colorFilter) in the effect's SkSL
-    struct Child {
-        enum class Type {
-            kShader,
-            kColorFilter,
-            kBlender,
-        };
+    enum class ChildType {
+        kShader,
+        kColorFilter,
+        kBlender,
+    };
 
-        SkString name;
-        Type     type;
-        int      index;
+    struct Child {
+        SkString  name;
+        ChildType type;
+        int       index;
     };
 
     class Options {
@@ -163,14 +164,22 @@
     static Result MakeForBlender(std::unique_ptr<SkSL::Program> program);
 
     // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child
-    struct ChildPtr {
+    class ChildPtr {
+    public:
         ChildPtr() = default;
-        ChildPtr(sk_sp<SkShader> s) : shader(std::move(s)) {}
-        ChildPtr(sk_sp<SkColorFilter> cf) : colorFilter(std::move(cf)) {}
-        ChildPtr(sk_sp<SkBlender> b) : blender(std::move(b)) {}
-        sk_sp<SkShader> shader;
-        sk_sp<SkColorFilter> colorFilter;
-        sk_sp<SkBlender> blender;
+        ChildPtr(sk_sp<SkShader> s) : fChild(std::move(s)) {}
+        ChildPtr(sk_sp<SkColorFilter> cf) : fChild(std::move(cf)) {}
+        ChildPtr(sk_sp<SkBlender> b) : fChild(std::move(b)) {}
+
+        skstd::optional<ChildType> type() const;
+
+        SkShader* shader() const;
+        SkColorFilter* colorFilter() const;
+        SkBlender* blender() const;
+        SkFlattenable* flattenable() const { return fChild.get(); }
+
+    private:
+        sk_sp<SkFlattenable> fChild;
     };
 
     sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms,
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index d137599..2c9794e 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -44,6 +44,8 @@
 
 #include <algorithm>
 
+using ChildType = SkRuntimeEffect::ChildType;
+
 #ifdef SK_ENABLE_SKSL
 
 namespace SkSL {
@@ -128,11 +130,11 @@
     return false;
 }
 
-static SkRuntimeEffect::Child::Type child_type(const SkSL::Type& type) {
+static ChildType child_type(const SkSL::Type& type) {
     switch (type.typeKind()) {
-        case SkSL::Type::TypeKind::kBlender:     return SkRuntimeEffect::Child::Type::kBlender;
-        case SkSL::Type::TypeKind::kColorFilter: return SkRuntimeEffect::Child::Type::kColorFilter;
-        case SkSL::Type::TypeKind::kShader:      return SkRuntimeEffect::Child::Type::kShader;
+        case SkSL::Type::TypeKind::kBlender:     return ChildType::kBlender;
+        case SkSL::Type::TypeKind::kColorFilter: return ChildType::kColorFilter;
+        case SkSL::Type::TypeKind::kShader:      return ChildType::kShader;
         default: SkUNREACHABLE;
     }
 }
@@ -143,26 +145,13 @@
     if (reflected.size() != effectPtrs.size()) {
         return false;
     }
+
     // Verify that each child object's type matches its declared type in the SkSL.
     for (size_t i = 0; i < effectPtrs.size(); ++i) {
-        switch (reflected[i].type) {
-            case SkRuntimeEffect::Child::Type::kShader:
-                if (effectPtrs[i].colorFilter || effectPtrs[i].blender) {
-                    return false;
-                }
-                continue;
-            case SkRuntimeEffect::Child::Type::kColorFilter:
-                if (effectPtrs[i].shader || effectPtrs[i].blender) {
-                    return false;
-                }
-                continue;
-            case SkRuntimeEffect::Child::Type::kBlender:
-                if (effectPtrs[i].shader || effectPtrs[i].colorFilter) {
-                    return false;
-                }
-                continue;
+        skstd::optional<ChildType> effectType = effectPtrs[i].type();
+        if (effectType && effectType != reflected[i].type) {
+            return false;
         }
-        SkDEBUGFAILF("unrecognized SkSL child type %d", (int)reflected[i].type);
     }
     return true;
 }
@@ -179,11 +168,11 @@
     children->reserve_back(childCount);
 
     for (const auto& child : effect->children()) {
-        if (child.type == SkRuntimeEffect::Child::Type::kShader) {
+        if (child.type == ChildType::kShader) {
             children->emplace_back(buffer.readShader());
-        } else if (child.type == SkRuntimeEffect::Child::Type::kColorFilter) {
+        } else if (child.type == ChildType::kColorFilter) {
             children->emplace_back(buffer.readColorFilter());
-        } else if (child.type == SkRuntimeEffect::Child::Type::kBlender) {
+        } else if (child.type == ChildType::kBlender) {
             children->emplace_back(buffer.readBlender());
         } else {
             return false;
@@ -197,10 +186,7 @@
                                 const std::vector<SkRuntimeEffect::ChildPtr>& children) {
     buffer.write32(children.size());
     for (const auto& child : children) {
-        buffer.writeFlattenable(
-                child.shader ? (const SkFlattenable*)child.shader.get() :
-           child.colorFilter ? (const SkFlattenable*)child.colorFilter.get() :
-                               (const SkFlattenable*)child.blender.get());
+        buffer.writeFlattenable(child.flattenable());
     }
 }
 
@@ -585,7 +571,7 @@
     if (!std::all_of(effect->fChildren.begin(),
                      effect->fChildren.end(),
                      [](const SkRuntimeEffect::Child& c) {
-                         return c.type == SkRuntimeEffect::Child::Type::kColorFilter;
+                         return c.type == ChildType::kColorFilter;
                      })) {
         return nullptr;
     }
@@ -786,16 +772,17 @@
                                  const GrFPArgs& childArgs) {
     SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
     for (const auto& child : children) {
-        if (child.shader) {
+        skstd::optional<ChildType> type = child.type();
+        if (type == ChildType::kShader) {
             // Convert a SkShader into a child FP.
-            auto childFP = as_SB(child.shader)->asFragmentProcessor(childArgs);
+            auto childFP = as_SB(child.shader())->asFragmentProcessor(childArgs);
             if (!childFP) {
                 return GrFPFailure(std::move(inputFP));
             }
             childFPs.push_back(std::move(childFP));
-        } else if (child.colorFilter) {
+        } else if (type == ChildType::kColorFilter) {
             // Convert a SkColorFilter into a child FP.
-            auto [success, childFP] = as_CFB(child.colorFilter)
+            auto [success, childFP] = as_CFB(child.colorFilter())
                                               ->asFragmentProcessor(/*inputFP=*/nullptr,
                                                                     childArgs.fContext,
                                                                     *childArgs.fDstColorInfo);
@@ -803,11 +790,11 @@
                 return GrFPFailure(std::move(inputFP));
             }
             childFPs.push_back(std::move(childFP));
-        } else if (child.blender) {
+        } else if (type == ChildType::kBlender) {
             // Convert a SkBlender into a child FP.
-            auto childFP = as_BB(child.blender)->asFragmentProcessor(/*srcFP=*/nullptr,
-                                                                     /*dstFP=*/nullptr,
-                                                                     childArgs);
+            auto childFP = as_BB(child.blender())->asFragmentProcessor(/*srcFP=*/nullptr,
+                                                                       /*dstFP=*/nullptr,
+                                                                       childArgs);
             if (!childFP) {
                 return GrFPFailure(std::move(inputFP));
             }
@@ -868,7 +855,7 @@
         SkASSERT(inputs);
 
         auto sampleShader = [&](int ix, skvm::Coord coord) {
-            if (SkShader* shader = fChildren[ix].shader.get()) {
+            if (SkShader* shader = fChildren[ix].shader()) {
                 SkSimpleMatrixProvider mats{SkMatrix::I()};
                 return as_SB(shader)->program(p, coord, coord, c, mats, /*localM=*/nullptr,
                                               colorInfo, uniforms, alloc);
@@ -876,13 +863,13 @@
             return c;
         };
         auto sampleColorFilter = [&](int ix, skvm::Color color) {
-            if (SkColorFilter* colorFilter = fChildren[ix].colorFilter.get()) {
+            if (SkColorFilter* colorFilter = fChildren[ix].colorFilter()) {
                 return as_CFB(colorFilter)->program(p, color, colorInfo, uniforms, alloc);
             }
             return color;
         };
         auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
-            if (SkBlender* blender = fChildren[ix].blender.get()) {
+            if (SkBlender* blender = fChildren[ix].blender()) {
                 return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
             }
             return blend(SkBlendMode::kSrcOver, src, dst);
@@ -916,10 +903,12 @@
             const auto& child = fChildren[index];
 
             // SkFilterColorProgram::Make has guaranteed that any children will be color filters.
-            SkASSERT(!child.shader);
-            SkASSERT(!child.blender);
-            return child.colorFilter ? as_CFB(child.colorFilter)->onFilterColor4f(inColor, dstCS)
-                                     : inColor;
+            SkASSERT(!child.shader());
+            SkASSERT(!child.blender());
+            if (SkColorFilter* colorFilter = child.colorFilter()) {
+                return as_CFB(colorFilter)->onFilterColor4f(inColor, dstCS);
+            }
+            return inColor;
         };
 
         return program->eval(color, inputs->data(), evalChild);
@@ -1047,7 +1036,7 @@
         local = SkShaderBase::ApplyMatrix(p,inv,local,uniforms);
 
         auto sampleShader = [&](int ix, skvm::Coord coord) {
-            if (SkShader* shader = fChildren[ix].shader.get()) {
+            if (SkShader* shader = fChildren[ix].shader()) {
                 SkOverrideDeviceMatrixProvider mats{matrices, SkMatrix::I()};
                 return as_SB(shader)->program(p, device, coord, paint, mats, /*localM=*/nullptr,
                                               colorInfo, uniforms, alloc);
@@ -1055,13 +1044,13 @@
             return paint;
         };
         auto sampleColorFilter = [&](int ix, skvm::Color color) {
-            if (SkColorFilter* colorFilter = fChildren[ix].colorFilter.get()) {
+            if (SkColorFilter* colorFilter = fChildren[ix].colorFilter()) {
                 return as_CFB(colorFilter)->program(p, color, colorInfo, uniforms, alloc);
             }
             return color;
         };
         auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
-            if (SkBlender* blender = fChildren[ix].blender.get()) {
+            if (SkBlender* blender = fChildren[ix].blender()) {
                 return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
             }
             return blend(SkBlendMode::kSrcOver, src, dst);
@@ -1157,7 +1146,7 @@
         SkASSERT(inputs);
 
         auto sampleShader = [&](int ix, skvm::Coord coord) {
-            if (SkShader* shader = fChildren[ix].shader.get()) {
+            if (SkShader* shader = fChildren[ix].shader()) {
                 SkSimpleMatrixProvider mats{SkMatrix::I()};
                 return as_SB(shader)->program(p, coord, coord, src, mats, /*localM=*/nullptr,
                                               colorInfo, uniforms, alloc);
@@ -1165,13 +1154,13 @@
             return src;
         };
         auto sampleColorFilter = [&](int ix, skvm::Color color) {
-            if (SkColorFilter* colorFilter = fChildren[ix].colorFilter.get()) {
+            if (SkColorFilter* colorFilter = fChildren[ix].colorFilter()) {
                 return as_CFB(colorFilter)->program(p, color, colorInfo, uniforms, alloc);
             }
             return color;
         };
         auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
-            if (SkBlender* blender = fChildren[ix].blender.get()) {
+            if (SkBlender* blender = fChildren[ix].blender()) {
                 return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
             }
             return blend(SkBlendMode::kSrcOver, src, dst);
@@ -1303,10 +1292,11 @@
         SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
         for (size_t i = 0; i < children.size(); ++i) {
             // TODO: add support for other types of child effects
-            if (!children[i].shader) {
+            if (SkShader* shader = children[i].shader()) {
+                childFPs.push_back(as_SB(shader)->asFragmentProcessor(args));
+            } else {
                 return nullptr;
             }
-            childFPs.push_back(as_SB(children[i].shader)->asFragmentProcessor(args));
         }
         auto fp = GrSkSLFP::MakeWithData(sk_ref_sp(this),
                                          "runtime_image",
@@ -1408,6 +1398,42 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+skstd::optional<ChildType> SkRuntimeEffect::ChildPtr::type() const {
+    if (fChild) {
+        switch (fChild->getFlattenableType()) {
+            case SkFlattenable::kSkShader_Type:
+                return ChildType::kShader;
+            case SkFlattenable::kSkColorFilter_Type:
+                return ChildType::kColorFilter;
+            case SkFlattenable::kSkBlender_Type:
+                return ChildType::kBlender;
+            default:
+                break;
+        }
+    }
+    return skstd::nullopt;
+}
+
+SkShader* SkRuntimeEffect::ChildPtr::shader() const {
+    return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkShader_Type)
+                   ? static_cast<SkShader*>(fChild.get())
+                   : nullptr;
+}
+
+SkColorFilter* SkRuntimeEffect::ChildPtr::colorFilter() const {
+    return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkColorFilter_Type)
+                   ? static_cast<SkColorFilter*>(fChild.get())
+                   : nullptr;
+}
+
+SkBlender* SkRuntimeEffect::ChildPtr::blender() const {
+    return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkBlender_Type)
+                   ? static_cast<SkBlender*>(fChild.get())
+                   : nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 void SkRuntimeEffect::RegisterFlattenables() {
     SK_REGISTER_FLATTENABLE(SkRuntimeColorFilter);
     SK_REGISTER_FLATTENABLE(SkRTShader);