[graphite] Add first version of Blend Shader support

This CL adds children to the SkPaintParamsKey and ShaderInfo objects and then uses them to implement the blend shader glue code.

Bug: skia:12701
Change-Id: I6750fe375b20a5c1cda315336bfcb3feda8cab28
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/505297
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/core/SkKeyHelpers.cpp b/src/core/SkKeyHelpers.cpp
index efb957e..440133b 100644
--- a/src/core/SkKeyHelpers.cpp
+++ b/src/core/SkKeyHelpers.cpp
@@ -547,7 +547,7 @@
     if (backend == SkBackend::kGraphite) {
         int headerOffset = key->beginBlock(SkBuiltInCodeSnippetID::kBlendShader);
 
-        add_blendmode_to_key(key, blendData.fBM);
+        // Child blocks always go right after the parent block's header
         int start = key->sizeInBytes();
         as_SB(blendData.fDst)->addToKey(dict, backend, key, uniformBlock);
         int firstShaderSize = key->sizeInBytes() - start;
@@ -556,6 +556,8 @@
         as_SB(blendData.fSrc)->addToKey(dict, backend, key, uniformBlock);
         int secondShaderSize = key->sizeInBytes() - start;
 
+        add_blendmode_to_key(key, blendData.fBM);
+
         key->endBlock(headerOffset, SkBuiltInCodeSnippetID::kBlendShader);
 
         int expectedBlockSize = 1 + firstShaderSize + secondShaderSize;
@@ -581,18 +583,19 @@
 
     int runningOffset = headerOffset + SkPaintParamsKey::kBlockHeaderSizeInBytes;
 
-    uint8_t data = key.byte(runningOffset);
-    SkBlendMode bm = to_blendmode(data);
-
-    SkDebugf("BlendMode: %s\n", SkBlendMode_Name(bm));
-    runningOffset += 1; // 1 byte for blendmode
-
     SkDebugf("\nDst:  ");
     int firstBlockSize = SkPaintParamsKey::DumpBlock(key, runningOffset);
     runningOffset += firstBlockSize;
 
     SkDebugf("Src: ");
     int secondBlockSize = SkPaintParamsKey::DumpBlock(key, runningOffset);
+    runningOffset += secondBlockSize;
+
+    uint8_t data = key.byte(runningOffset);
+    SkBlendMode bm = to_blendmode(data);
+
+    SkDebugf("BlendMode: %s\n", SkBlendMode_Name(bm));
+    runningOffset += 1; // 1 byte for blendmode
 
     int calculatedBlockSize = SkPaintParamsKey::kBlockHeaderSizeInBytes +
                               firstBlockSize + secondBlockSize + 1;
diff --git a/src/core/SkPaintParamsKey.cpp b/src/core/SkPaintParamsKey.cpp
index e2b4dd8..6458b01 100644
--- a/src/core/SkPaintParamsKey.cpp
+++ b/src/core/SkPaintParamsKey.cpp
@@ -84,6 +84,16 @@
 
         result->add(*entry);
 
+        // The child blocks appear right after the parent block's header in the key and go
+        // right after the parent's SnippetEntry in the shader info
+        int childOffset = headerOffset + kBlockHeaderSizeInBytes;
+        for (int i = 0; i < entry->fNumChildren; ++i) {
+            SkASSERT(childOffset < headerOffset + blockSize);
+
+            int childBlockSize = AddBlockToShaderInfo(dict, key, childOffset, result);
+            childOffset += childBlockSize;
+        }
+
         if (codeSnippetID != SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw) {
             result->setWritesColor();
         }
diff --git a/src/core/SkPaintParamsKey.h b/src/core/SkPaintParamsKey.h
index 5fd9bf6..e315ecf 100644
--- a/src/core/SkPaintParamsKey.h
+++ b/src/core/SkPaintParamsKey.h
@@ -23,8 +23,12 @@
 
 // This class is a compact representation of the shader needed to implement a given
 // PaintParams. Its structure is a series of blocks where each block has a
-// header that consists of 2-bytes - a 1-byte code-snippet ID and a 1-byte number-of-bytes-in-the-
-// block field. The rest of the data in the block is dependent on the individual code snippet.
+// header that consists of 2-bytes:
+//   a 1-byte code-snippet ID
+//   a 1-byte number-of-bytes-in-the-block field (incl. the space for the header)
+// The rest of the data in the block is dependent on the individual code snippet.
+// If a given block has child blocks, they appear in the key right after their
+// parent block's header.
 class SkPaintParamsKey {
 public:
     static const int kBlockHeaderSizeInBytes = 2;
diff --git a/src/core/SkShaderCodeDictionary.cpp b/src/core/SkShaderCodeDictionary.cpp
index c225651..38bf1d6 100644
--- a/src/core/SkShaderCodeDictionary.cpp
+++ b/src/core/SkShaderCodeDictionary.cpp
@@ -36,7 +36,12 @@
 //
 //     half4 outColor%d;
 //     {
-//         /* emitted snippet sksl assigns to outColor%d */
+//         half4 child-outColor%d;  // for each child
+//         {
+//             /* emitted snippet sksl assigns to child-outColor%d */
+//         }
+//
+//         /* emitted snippet sksl assigns to outColor%d - taking a vector of child var names */
 //     }
 // Where the %d is filled in with 'entryIndex'.
 std::string SkShaderInfo::emitGlueCodeForEntry(int* entryIndex,
@@ -52,7 +57,17 @@
     add_indent(result, indent);
     *result += "{\n";
 
-    *result += (entry.fGlueCodeGenerator)(scopeOutputVar, curEntryIndex, entry, indent+1);
+    // Although the children appear after the parent in the shader info they are emitted
+    // before the parent
+    std::vector<std::string> childNames;
+    for (int j = 0; j < entry.fNumChildren; ++j) {
+        *entryIndex += 1;
+        std::string childOutputVar = this->emitGlueCodeForEntry(entryIndex, result, indent+1);
+        childNames.push_back(childOutputVar);
+    }
+
+    *result += (entry.fGlueCodeGenerator)(scopeOutputVar, curEntryIndex,
+                                          entry, childNames, indent+1);
     add_indent(result, indent);
     *result += "}\n";
 
@@ -160,7 +175,10 @@
 std::string GenerateDefaultGlueCode(const std::string& resultName,
                                     int entryIndex,
                                     const SkShaderInfo::SnippetEntry& entry,
+                                    const std::vector<std::string>& childNames,
                                     int indent) {
+    SkASSERT(childNames.empty());
+
     std::string result;
 
     add_indent(&result, indent);
@@ -247,6 +265,99 @@
         "}\n";
 
 //--------------------------------------------------------------------------------------------------
+static constexpr int kNumBlendShaderUniforms = 0;
+static constexpr int kNumBlendShaderChildren = 2;
+
+// Note: we're counting on the compiler to inline this code and trim it down to just the used
+// branch(es).
+static const char* kBlendShaderName = "blend_shader";
+static const char* kBlendShaderSkSL =
+        "const int kClear      = 0;\n"
+        "const int kSrc        = 1;\n"
+        "const int kDst        = 2;\n"
+        "const int kSrcOver    = 3;\n"
+        "const int kDstOver    = 4;\n"
+        "const int kSrcIn      = 5;\n"
+        "const int kDstIn      = 6;\n"
+        "const int kSrcOut     = 7;\n"
+        "const int kDstOut     = 8;\n"
+        "const int kSrcATop    = 9;\n"
+        "const int kDstATop    = 10;\n"
+        "const int kXor        = 11;\n"
+        "const int kPlus       = 12;\n"
+        "const int kModulate   = 13;\n"
+        "const int kScreen     = 14;\n"
+        "const int kOverlay    = 15;\n"
+        "const int kDarken     = 16;\n"
+        "const int kLighten    = 17;\n"
+        "const int kColorDodge = 18;\n"
+        "const int kColorBurn  = 19;\n"
+        "const int kHardLight  = 20;\n"
+        "const int kSoftLight  = 21;\n"
+        "const int kDifference = 22;\n"
+        "const int kExclusion  = 23;\n"
+        "const int kMultiply   = 24;\n"
+        "const int kHue        = 25;\n"
+        "const int kSaturation = 26;\n"
+        "const int kColor      = 27;\n"
+        "const int kLuminosity = 28;\n"
+        "\n"
+        "half4 blend(int mode, half4 src, half4 dst) {\n"
+        "    switch (mode) {\n"
+        "        case kClear:      { return blend_clear(src, dst); }\n"
+        "        case kSrc:        { return blend_src(src, dst); }\n"
+        "        case kDst:        { return blend_dst(src, dst); }\n"
+        "        case kSrcOver:    { return blend_src_over(src, dst); }\n"
+        "        case kDstOver:    { return blend_dst_over(src, dst); }\n"
+        "        case kSrcIn:      { return blend_src_in(src, dst); }\n"
+        "        case kDstIn:      { return blend_dst_in(src, dst); }\n"
+        "        case kSrcOut:     { return blend_src_out(src, dst); }\n"
+        "        case kDstOut:     { return blend_dst_out(src, dst); }\n"
+        "        case kSrcATop:    { return blend_src_atop(src, dst); }\n"
+        "        case kDstATop:    { return blend_dst_atop(src, dst); }\n"
+        "        case kXor:        { return blend_xor(src, dst); }\n"
+        "        case kPlus:       { return blend_plus(src, dst); }\n"
+        "        case kModulate:   { return blend_modulate(src, dst); }\n"
+        "        case kScreen:     { return blend_screen(src, dst); }\n"
+        "        case kOverlay:    { return blend_overlay(src, dst); }\n"
+        "        case kDarken:     { return blend_darken(src, dst); }\n"
+        "        case kLighten:    { return blend_lighten(src, dst); }\n"
+        "        case kColorDodge: { return blend_color_dodge(src, dst); }\n"
+        "        case kColorBurn:  { return blend_color_burn(src, dst); }\n"
+        "        case kHardLight:  { return blend_hard_light(src, dst); }\n"
+        "        case kSoftLight:  { return blend_soft_light(src, dst); }\n"
+        "        case kDifference: { return blend_difference(src, dst); }\n"
+        "        case kExclusion:  { return blend_exclusion(src, dst); }\n"
+        "        case kMultiply:   { return blend_multiply(src, dst); }\n"
+        "        case kHue:        { return blend_hue(src, dst); }\n"
+        "        case kSaturation: { return blend_saturation(src, dst); }\n"
+        "        case kColor:      { return blend_color(src, dst); }\n"
+        "        case kLuminosity: { return blend_luminosity(src, dst); }\n"
+        "        default: return half4(0);  // Avoids 'blend can exit without returning a value' error\n"
+        "    }\n"
+        "}\n";
+
+std::string GenerateBlendShaderGlueCode(const std::string& resultName,
+                                        int entryIndex,
+                                        const SkShaderInfo::SnippetEntry& entry,
+                                        const std::vector<std::string>& childNames,
+                                        int indent) {
+    SkASSERT(childNames.size() == kNumBlendShaderChildren);
+
+    std::string result;
+
+    add_indent(&result, indent);
+    // TODO: actually feed in the blend mode either through a uniform or, somehow, from the
+    // SkPaintParamsKey
+    SkSL::String::appendf(&result, "%s = blend(kModulate, %s, %s);\n",
+                          resultName.c_str(),
+                          childNames[1].c_str(),
+                          childNames[0].c_str());
+
+    return result;
+}
+
+//--------------------------------------------------------------------------------------------------
 static constexpr int kNumErrorUniforms = 0;
 static const char* kErrorName = "error";
 static const char* kErrorSkSL =
@@ -256,6 +367,8 @@
 
 } // anonymous namespace
 
+static constexpr int kNoChildren = 0;
+
 SkShaderCodeDictionary::SkShaderCodeDictionary() {
     // The 0th index is reserved as invalid
     fEntryVector.push_back(nullptr);
@@ -264,45 +377,54 @@
             { nullptr, kNumErrorUniforms },
             kErrorName, kErrorSkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kSolidColorShader] = {
             SkMakeSpan(kSolidShaderUniforms, kNumSolidShaderUniforms),
             kSolidShaderName, kSolidShaderSkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kLinearGradientShader] = {
             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
             kLinearGradient4Name, kLinearGradient4SkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kRadialGradientShader] = {
             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
             kLinearGradient4Name, kLinearGradient4SkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kSweepGradientShader] = {
             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
             kLinearGradient4Name, kLinearGradient4SkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kConicalGradientShader] = {
             SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
             kLinearGradient4Name, kLinearGradient4SkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kImageShader] = {
             { nullptr, kNumImageShaderUniforms },
             kImageShaderName, kImageShaderSkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kBlendShader] = {
-            { nullptr, kNumErrorUniforms },
-            kErrorName, kErrorSkSL,
-            GenerateDefaultGlueCode,
+            { nullptr, kNumBlendShaderUniforms },
+            kBlendShaderName, kBlendShaderSkSL,
+            GenerateBlendShaderGlueCode,
+            kNumBlendShaderChildren
     };
     fCodeSnippets[(int) SkBuiltInCodeSnippetID::kSimpleBlendMode] = {
             { nullptr, kNumErrorUniforms },
             kErrorName, kErrorSkSL,
             GenerateDefaultGlueCode,
+            kNoChildren
     };
 }
diff --git a/src/core/SkShaderCodeDictionary.h b/src/core/SkShaderCodeDictionary.h
index 0dcaf03..666299e 100644
--- a/src/core/SkShaderCodeDictionary.h
+++ b/src/core/SkShaderCodeDictionary.h
@@ -24,6 +24,7 @@
     using GenerateGlueCodeForEntry = std::string (*)(const std::string& resultName,
                                                      int entryIndex, // for uniform name mangling
                                                      const SnippetEntry&,
+                                                     const std::vector<std::string>& childNames,
                                                      int indent);
 
     struct SnippetEntry {
@@ -31,6 +32,7 @@
         const char* fStaticFunctionName;
         const char* fStaticSkSL;
         GenerateGlueCodeForEntry fGlueCodeGenerator;
+        int fNumChildren;
     };
 
     void add(const SnippetEntry& entry) {