Add support for child effects on SkRuntimeBlender.

This will allow runtime blenders to leverage existing color filters or
shaders when computing a blend.

Change-Id: I743d5fc6d83a92cd190cb6bfd1c79789d8be2089
Bug: skia:12249
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/430658
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 cecfedf..b879e67 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -194,7 +194,7 @@
     sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms,
                                          SkSpan<ChildPtr> children) const;
 
-    sk_sp<SkBlender> makeBlender(sk_sp<SkData> uniforms) const;
+    sk_sp<SkBlender> makeBlender(sk_sp<SkData> uniforms, SkSpan<ChildPtr> children = {}) const;
 
     const std::string& source() const;
 
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 4420576..0bc3d1a 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -264,12 +264,6 @@
 
             // Child effects that can be sampled ('shader' or 'colorFilter')
             if (varType.isEffectChild()) {
-                // Runtime blends currently don't support child effects.
-                if (kind == SkSL::ProgramKind::kRuntimeBlender) {
-                    RETURN_FAILURE("'%s' is not allowed in runtime blend",
-                                   varType.displayName().c_str());
-                }
-
                 Child c;
                 c.name  = SkString(var.name());
                 c.type  = child_type(varType);
@@ -722,6 +716,7 @@
                                  const char* name,
                                  sk_sp<SkData> uniforms,
                                  std::unique_ptr<GrFragmentProcessor> inputFP,
+                                 std::unique_ptr<GrFragmentProcessor> destColorFP,
                                  SkSpan<const SkRuntimeEffect::ChildPtr> children,
                                  const GrFPArgs& childArgs) {
     SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
@@ -748,7 +743,7 @@
     auto fp = GrSkSLFP::MakeWithData(std::move(effect),
                                      name,
                                      std::move(inputFP),
-                                     /*destColorFP=*/nullptr,
+                                     std::move(destColorFP),
                                      std::move(uniforms),
                                      SkMakeSpan(childFPs));
     SkASSERT(fp);
@@ -778,6 +773,7 @@
                               "runtime_color_filter",
                               std::move(uniforms),
                               std::move(inputFP),
+                              /*destColorFP=*/nullptr,
                               SkMakeSpan(fChildren),
                               childArgs);
     }
@@ -938,6 +934,7 @@
                                             "runtime_shader",
                                             std::move(uniforms),
                                             /*inputFP=*/nullptr,
+                                            /*destColorFP=*/nullptr,
                                             SkMakeSpan(fChildren),
                                             childArgs);
         if (!success) {
@@ -1090,9 +1087,12 @@
 
 class SkRuntimeBlender : public SkBlenderBase {
 public:
-    SkRuntimeBlender(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms)
+    SkRuntimeBlender(sk_sp<SkRuntimeEffect> effect,
+                     sk_sp<SkData> uniforms,
+                     SkSpan<SkRuntimeEffect::ChildPtr> children)
             : fEffect(std::move(effect))
-            , fUniforms(std::move(uniforms)) {}
+            , fUniforms(std::move(uniforms))
+            , fChildren(children.begin(), children.end()) {}
 
     SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
 
@@ -1103,6 +1103,19 @@
                                                     colorInfo.colorSpace());
         SkASSERT(inputs);
 
+        auto sampleChild = [&](int index, skvm::Coord coord, skvm::Color color) {
+            const SkRuntimeEffect::ChildPtr& effect = fChildren[index];
+            if (effect.shader) {
+                SkSimpleMatrixProvider mats{SkMatrix::I()};
+                return as_SB(effect.shader)->program(p, coord, coord, color, mats,
+                                                    /*localM=*/nullptr, colorInfo, uniforms, alloc);
+            }
+            if (effect.colorFilter) {
+                return as_CFB(effect.colorFilter)->program(p, color, colorInfo, uniforms, alloc);
+            }
+            return color;
+        };
+
         const size_t uniformCount = fEffect->uniformSize() / 4;
         std::vector<skvm::Val> uniform;
         uniform.reserve(uniformCount);
@@ -1115,8 +1128,7 @@
         // Emit the blend function as an SkVM program.
         skvm::Coord zeroCoord = {p->splat(0.0f), p->splat(0.0f)};
         return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, SkMakeSpan(uniform),
-                                   /*device=*/zeroCoord, /*local=*/zeroCoord,
-                                   src, dst, /*sampleChild=*/nullptr);
+                                  /*device=*/zeroCoord, /*local=*/zeroCoord, src, dst, sampleChild);
     }
 
 #if SK_SUPPORT_GPU
@@ -1127,13 +1139,15 @@
         sk_sp<SkData> uniforms = get_xformed_uniforms(fEffect.get(), fUniforms,
                                                       args.fDstColorInfo->colorSpace());
         SkASSERT(uniforms);
+        auto [success, fp] = make_effect_fp(fEffect,
+                                            "runtime_blender",
+                                            std::move(uniforms),
+                                            std::move(srcFP),
+                                            std::move(dstFP),
+                                            SkMakeSpan(fChildren),
+                                            args);
 
-        return GrSkSLFP::MakeWithData(fEffect,
-                                      "runtime_blender",
-                                      std::move(srcFP),
-                                      std::move(dstFP),
-                                      std::move(uniforms),
-                                      /*childFPs=*/{});
+        return success ? std::move(fp) : nullptr;
     }
 #endif
 
@@ -1149,6 +1163,7 @@
 
     sk_sp<SkRuntimeEffect> fEffect;
     sk_sp<SkData> fUniforms;
+    std::vector<SkRuntimeEffect::ChildPtr> fChildren;
 };
 
 sk_sp<SkFlattenable> SkRuntimeBlender::CreateProc(SkReadBuffer& buffer) {
@@ -1312,17 +1327,21 @@
     return this->makeColorFilter(std::move(uniforms), /*children=*/{});
 }
 
-sk_sp<SkBlender> SkRuntimeEffect::makeBlender(sk_sp<SkData> uniforms) const {
+sk_sp<SkBlender> SkRuntimeEffect::makeBlender(sk_sp<SkData> uniforms,
+                                              SkSpan<ChildPtr> children) const {
     if (!this->allowBlender()) {
         return nullptr;
     }
+    if (!verify_child_effects(fChildren, children)) {
+        return nullptr;
+    }
     if (!uniforms) {
         uniforms = SkData::MakeEmpty();
     }
-    if (uniforms->size() != this->uniformSize() || !fChildren.empty()) {
+    if (uniforms->size() != this->uniformSize()) {
         return nullptr;
     }
-    return sk_sp<SkBlender>(new SkRuntimeBlender(sk_ref_sp(this), std::move(uniforms)));
+    return sk_sp<SkBlender>(new SkRuntimeBlender(sk_ref_sp(this), std::move(uniforms), children));
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1363,7 +1382,8 @@
 SkRuntimeBlendBuilder::~SkRuntimeBlendBuilder() = default;
 
 sk_sp<SkBlender> SkRuntimeBlendBuilder::makeBlender() {
-    return this->effect()->makeBlender(this->uniforms());
+    return this->effect()->makeBlender(this->uniforms(),
+                                       SkMakeSpan(this->children(), this->numChildren()));
 }
 
 #endif  // SK_ENABLE_SKSL
diff --git a/src/sksl/generated/sksl_rt_blend.dehydrated.sksl b/src/sksl/generated/sksl_rt_blend.dehydrated.sksl
index 530f37f..16fbb2c 100644
--- a/src/sksl/generated/sksl_rt_blend.dehydrated.sksl
+++ b/src/sksl/generated/sksl_rt_blend.dehydrated.sksl
@@ -1,5 +1,36 @@
-static uint8_t SKSL_INCLUDE_sksl_rt_blend[] = {0,0,
-47,0,0,0,0,
+static uint8_t SKSL_INCLUDE_sksl_rt_blend[] = {56,0,
+1,115,
+6,115,104,97,100,101,114,
+6,99,111,111,114,100,115,
+6,102,108,111,97,116,50,
+6,115,97,109,112,108,101,
+5,104,97,108,102,52,
+1,102,
+11,99,111,108,111,114,70,105,108,116,101,114,
+5,99,111,108,111,114,
+47,7,0,
+51,1,0,
+16,2,0,
+48,2,0,4,0,3,
+51,3,0,
+16,11,0,
+48,4,0,18,0,3,
+28,5,0,
+16,25,0,2,1,0,3,0,
+48,6,0,32,0,
+51,7,0,
+16,38,0,
+48,8,0,40,0,3,
+51,9,0,
+16,52,0,
+45,6,0,3,
+50,10,0,2,
+45,5,0,
+28,11,0,
+16,25,0,2,7,0,9,0,
+45,6,0,
+45,11,0,1,0,
+5,0,
 19,
 20,};
 static constexpr size_t SKSL_INCLUDE_sksl_rt_blend_LENGTH = sizeof(SKSL_INCLUDE_sksl_rt_blend);
diff --git a/src/sksl/sksl_rt_blend.sksl b/src/sksl/sksl_rt_blend.sksl
index f6fe915..06b4a42 100644
--- a/src/sksl/sksl_rt_blend.sksl
+++ b/src/sksl/sksl_rt_blend.sksl
@@ -1 +1,2 @@
-// TODO(skia:12080): if any blend-specific program elements are needed, add them here.
+half4 sample(shader s, float2 coords);
+half4 sample(colorFilter f, half4 color);
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index 205520e..ec784e5 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -176,8 +176,8 @@
                         errorText.c_str());
     };
 
-    // Color filters must use the 'half4 main(half4, half4)' signature. Any mixture of
-    // float4/vec4/half4 is allowed.
+    // Blenders must use the 'half4 main(half4, half4)' signature. Any mixture of float4/vec4/half4
+    // is allowed.
     test_valid("half4  main(half4  s, half4  d) { return s; }");
     test_valid("float4 main(float4 s, float4 d) { return d; }");
     test_valid("float4 main(half4  s, float4 d) { return s; }");
@@ -202,11 +202,44 @@
     test_invalid("half4 main(half4 s, half4 d) { return sk_FragCoord.xy01; }",
                  "unknown identifier");
 
-    // Child shaders are currently unsupported in blends
-    test_invalid("uniform shader sh; half4 main(half4 s, half4 d) { return s; }",
-                 "'shader' is not allowed in runtime blend");
-    test_invalid("uniform shader sh; half4 main(half4 s, half4 d) { return sample(sh, s.rg); }",
-                 "unknown identifier 'sample'");
+    // Sampling a child shader requires that we pass explicit coords
+    test_valid("uniform shader child;"
+               "half4 main(half4 s, half4 d) { return sample(child, s.rg); }");
+    // Trying to pass a color as well. (Works internally with FPs, but not in runtime effects).
+    test_invalid("uniform shader child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, s.rg, d); }",
+                 "no match for sample(shader, half2, half4)");
+
+    // Shader with just a color
+    test_invalid("uniform shader child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, s); }",
+                 "no match for sample(shader, half4)");
+    // Coords and color in a different order
+    test_invalid("uniform shader child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, s, d.rg); }",
+                 "no match for sample(shader, half4, half2)");
+
+    // Older variants that are no longer allowed
+    test_invalid("uniform shader child;"
+                 "half4 main(half4 s, half4 d) { return sample(child); }",
+                 "no match for sample(shader)");
+    test_invalid("uniform shader child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, float3x3(1)); }",
+                 "no match for sample(shader, float3x3)");
+
+    // Sampling a colorFilter requires a color. No other signatures are valid.
+    test_valid("uniform colorFilter child;"
+               "half4 main(half4 s, half4 d) { return sample(child, d); }");
+
+    test_invalid("uniform colorFilter child;"
+                 "half4 main(half4 s, half4 d) { return sample(child); }",
+                 "sample(colorFilter)");
+    test_invalid("uniform colorFilter child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, d.rg); }",
+                 "sample(colorFilter, half2)");
+    test_invalid("uniform colorFilter child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, d.rg, s); }",
+                 "sample(colorFilter, half2, half4)");
 }
 
 DEF_TEST(SkRuntimeEffectForShader, r) {
@@ -406,6 +439,10 @@
         return fBuilder->uniform(name);
     }
 
+    SkRuntimeBlendBuilder::BuilderChild child(const char* name) {
+        return fBuilder->child(name);
+    }
+
     void test(std::array<GrColor, 4> expected, PreTestFn preTestCallback = nullptr) {
         auto blender = fBuilder->makeBlender();
         if (!blender) {
@@ -552,6 +589,7 @@
     REPORTER_ASSERT(r, surface);
     TestBlend effect(r, surface);
 
+    using float2 = std::array<float, 2>;
     using float4 = std::array<float, 4>;
     using int4 = std::array<int, 4>;
 
@@ -600,6 +638,40 @@
     effect.test(0x00000000);
     effect.build("half4 main(half4 s, half4 d) { return half4(2); }");
     effect.test(0xFFFFFFFF);
+
+    //
+    // Sampling children
+    //
+
+    // Sampling a null child should return the paint color
+    effect.build("uniform shader child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, s.rg); }");
+    effect.child("child") = nullptr;
+    effect.test(0xFF00FFFF,
+                [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
+
+    // Sampling a shader at various coordinates
+    effect.build("uniform shader child;"
+                 "uniform half2 pos;"
+                 "half4 main(half4 s, half4 d) { return sample(child, pos); }");
+    effect.child("child") = make_RGBW_shader();
+    effect.uniform("pos") = float2{0, 0};
+    effect.test(0xFF0000FF);
+
+    effect.uniform("pos") = float2{1, 0};
+    effect.test(0xFF00FF00);
+
+    effect.uniform("pos") = float2{0, 1};
+    effect.test(0xFFFF0000);
+
+    effect.uniform("pos") = float2{1, 1};
+    effect.test(0xFFFFFFFF);
+
+    // Sampling a color filter
+    effect.build("uniform colorFilter child;"
+                 "half4 main(half4 s, half4 d) { return sample(child, half4(1)); }");
+    effect.child("child") = SkColorFilters::Blend(0xFF012345, SkBlendMode::kSrc);
+    effect.test(0xFF452301);
 }
 
 DEF_TEST(SkRuntimeEffect_Blender_CPU, r) {