Reland "Add SkRuntimeBlender class."

This is a reland of 6034941cc28a28a7c9fc79d6cb815fed188cdfb6

Original change's description:
> Add SkRuntimeBlender class.
>
> This class is returned by SkRuntimeEffect::makeBlender when a runtime
> blend is returned. SkRuntimeBlendBuilder is also added as a convenience
> class to simplify creation and uniform setup.
>
> Our ability to add tests is limited in this CL because the SkPaint does
> not contain an SkBlender yet. We do have one test which builds an
> SkBlender, but there's not much we can do with it. Testing will be
> bulked up in the next CL.
>
> Change-Id: Ib2d7d04186690ec0d2238fc990a19d9e6b786ce3
> Bug: skia:12080
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/417006
> Commit-Queue: John Stiles <johnstiles@google.com>
> Auto-Submit: John Stiles <johnstiles@google.com>
> Reviewed-by: Brian Osman <brianosman@google.com>

Bug: skia:12080
Change-Id: Ie41be141ba3787452f9d5c72946ebc748a5d6176
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419160
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index f965d19..750a5a4 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -8,6 +8,7 @@
 #ifndef SkRuntimeEffect_DEFINED
 #define SkRuntimeEffect_DEFINED
 
+#include "include/core/SkBlender.h"
 #include "include/core/SkColorFilter.h"
 #include "include/core/SkData.h"
 #include "include/core/SkImageInfo.h"
@@ -21,7 +22,6 @@
 #include <vector>
 
 class GrRecordingContext;
-class SkBlender;
 class SkFilterColorProgram;
 class SkImage;
 
@@ -249,6 +249,7 @@
 #endif
 
     friend class SkRTShader;            // fBaseProgram, fMain
+    friend class SkRuntimeBlender;      //
     friend class SkRuntimeColorFilter;  //
 
     friend class SkFilterColorProgram;
@@ -419,4 +420,21 @@
     using INHERITED = SkRuntimeEffectBuilder<sk_sp<SkShader>>;
 };
 
+/**
+ * SkRuntimeBlendBuilder is a utility to simplify creation and uniform setup of runtime blenders.
+ */
+class SK_API SkRuntimeBlendBuilder : public SkRuntimeEffectBuilder<sk_sp<SkBlender>> {
+public:
+    explicit SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect>);
+    ~SkRuntimeBlendBuilder();
+
+    SkRuntimeBlendBuilder(const SkRuntimeBlendBuilder&) = delete;
+    SkRuntimeBlendBuilder& operator=(const SkRuntimeBlendBuilder&) = delete;
+
+    sk_sp<SkBlender> makeBlender();
+
+private:
+    using INHERITED = SkRuntimeEffectBuilder<sk_sp<SkBlender>>;
+};
+
 #endif
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 7f35595..99c3914 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -1003,6 +1003,64 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+class SkRuntimeBlender : public SkBlenderBase {
+public:
+    SkRuntimeBlender(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms)
+            : fEffect(std::move(effect))
+            , fUniforms(std::move(uniforms)) {}
+
+    skvm::Color onProgram(skvm::Builder* p, skvm::Color src, skvm::Color dst,
+                          const SkColorInfo& colorInfo, skvm::Uniforms* uniforms,
+                          SkArenaAlloc* alloc) const override {
+        sk_sp<SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms,
+                                                    colorInfo.colorSpace());
+        SkASSERT(inputs);
+
+        const size_t uniformCount = fEffect->uniformSize() / 4;
+        std::vector<skvm::Val> uniform;
+        uniform.reserve(uniformCount);
+        for (size_t i = 0; i < uniformCount; i++) {
+            int bits;
+            memcpy(&bits, (const char*)inputs->data() + 4*i, 4);
+            uniform.push_back(p->uniform32(uniforms->push(bits)).id);
+        }
+
+        // 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);
+    }
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeString(fEffect->source().c_str());
+        buffer.writeDataAsByteArray(fUniforms.get());
+    }
+
+    SK_FLATTENABLE_HOOKS(SkRuntimeBlender)
+
+private:
+    using INHERITED = SkBlenderBase;
+
+    sk_sp<SkRuntimeEffect> fEffect;
+    sk_sp<SkData> fUniforms;
+};
+
+sk_sp<SkFlattenable> SkRuntimeBlender::CreateProc(SkReadBuffer& buffer) {
+    SkString sksl;
+    buffer.readString(&sksl);
+    sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
+
+    auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForBlender, std::move(sksl));
+    if (!buffer.validate(effect != nullptr)) {
+        return nullptr;
+    }
+
+    return effect->makeBlender(std::move(uniforms));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<SkData> uniforms,
                                             sk_sp<SkShader> childShaders[],
                                             size_t childCount,
@@ -1179,9 +1237,7 @@
     if (uniforms->size() != this->uniformSize() || !fChildren.empty()) {
         return nullptr;
     }
-    // TODO(skia:12080): create a runtime blend class
-    SkDEBUGFAIL("not yet implemented");
-    return nullptr;
+    return sk_sp<SkBlender>(new SkRuntimeBlender(sk_ref_sp(this), std::move(uniforms)));
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1216,3 +1272,12 @@
                                       localMatrix,
                                       isOpaque);
 }
+
+SkRuntimeBlendBuilder::SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect> effect)
+        : INHERITED(std::move(effect)) {}
+
+SkRuntimeBlendBuilder::~SkRuntimeBlendBuilder() = default;
+
+sk_sp<SkBlender> SkRuntimeBlendBuilder::makeBlender() {
+    return this->effect()->makeBlender(this->uniforms());
+}
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index c2e24ff..69d4579 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "include/core/SkBitmap.h"
+#include "include/core/SkBlender.h"
 #include "include/core/SkCanvas.h"
 #include "include/core/SkColorFilter.h"
 #include "include/core/SkData.h"
@@ -329,7 +330,7 @@
 
     void test(GrColor TL, GrColor TR, GrColor BL, GrColor BR,
               PreTestFn preTestCallback = nullptr) {
-        auto shader = fBuilder->makeShader(nullptr, false);
+        auto shader = fBuilder->makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false);
         if (!shader) {
             REPORT_FAILURE(fReporter, "shader", SkString("Effect didn't produce a shader"));
             return;
@@ -499,10 +500,27 @@
     // Test passes if this sequence doesn't assert.  skbug.com/10667
     SkRuntimeShaderBuilder b(std::move(effect));
     b.uniform("x") = 0.0f;
-    auto shader_0 = b.makeShader(nullptr, false);
+    auto shader_0 = b.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false);
 
     b.uniform("x") = 1.0f;
-    auto shader_1 = b.makeShader(nullptr, true);
+    auto shader_1 = b.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/true);
+}
+
+DEF_TEST(SkRuntimeBlendBuilderReuse, r) {
+    const char* kSource = R"(
+        uniform half x;
+        half4 main(half4 s, half4 d) { return half4(x); }
+    )";
+
+    sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::MakeForBlender(SkString(kSource)).effect;
+    REPORTER_ASSERT(r, effect);
+
+    // We should be able to construct multiple SkBlenders in a row without asserting.
+    SkRuntimeBlendBuilder b(std::move(effect));
+    for (float x = 0.0f; x <= 2.0f; x += 2.0f) {
+        b.uniform("x") = x;
+        sk_sp<SkBlender> blender = b.makeBlender();
+    }
 }
 
 DEF_TEST(SkRuntimeShaderBuilderSetUniforms, r) {
@@ -530,8 +548,7 @@
     REPORTER_ASSERT(r, !b.uniform("offset").set<float>(origin, 3));
 #endif
 
-
-    auto shader = b.makeShader(nullptr, false);
+    auto shader = b.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false);
 }
 
 DEF_TEST(SkRuntimeEffectThreaded, r) {