[graphite] New Combination API

Bug: skia:13430
Change-Id: I816c57d0ed8694d21911c449d67c035580dd63d1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/548776
Reviewed-by: Herb Derby <herb@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gn/core.gni b/gn/core.gni
index 3ba41e0..94a6ab2 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -212,6 +212,8 @@
   "$_src/core/SkEnumerate.h",
   "$_src/core/SkExecutor.cpp",
   "$_src/core/SkFDot6.h",
+  "$_src/core/SkFactoryFunctions.cpp",
+  "$_src/core/SkFactoryFunctions.h",
   "$_src/core/SkFlattenable.cpp",
   "$_src/core/SkFont.cpp",
   "$_src/core/SkFontDescriptor.cpp",
@@ -317,6 +319,7 @@
   "$_src/core/SkPixmap.cpp",
   "$_src/core/SkPoint.cpp",
   "$_src/core/SkPoint3.cpp",
+  "$_src/core/SkPrecompile.h",
   "$_src/core/SkPromiseImageTexture.cpp",
   "$_src/core/SkPtrRecorder.cpp",
   "$_src/core/SkPtrRecorder.h",
diff --git a/public.bzl b/public.bzl
index 8cc4ee3..d2fb611 100644
--- a/public.bzl
+++ b/public.bzl
@@ -457,6 +457,8 @@
     "src/core/SkEnumerate.h",
     "src/core/SkExecutor.cpp",
     "src/core/SkFDot6.h",
+    "src/core/SkFactoryFunctions.h",
+    "src/core/SkFactoryFunctions.cpp",
     "src/core/SkFlattenable.cpp",
     "src/core/SkFont.cpp",
     "src/core/SkFontDescriptor.cpp",
@@ -589,6 +591,7 @@
     "src/core/SkPoint.cpp",
     "src/core/SkPoint3.cpp",
     "src/core/SkPointPriv.h",
+    "src/core/SkPrecompile.h",
     "src/core/SkPromiseImageTexture.cpp",
     "src/core/SkPtrRecorder.cpp",
     "src/core/SkPtrRecorder.h",
diff --git a/src/core/BUILD.bazel b/src/core/BUILD.bazel
index d2b54f0..8c214a6 100644
--- a/src/core/BUILD.bazel
+++ b/src/core/BUILD.bazel
@@ -405,11 +405,14 @@
 # These files are only needed if SkSL is enabled (GPU backend or SkVM).
 SKSL_FILES = [
     "SkCombinationBuilder.cpp",
+    "SkFactoryFunctions.h",
+    "SkFactoryFunctions.cpp",
     "SkKeyContext.h",
     "SkKeyHelpers.cpp",
     "SkKeyHelpers.h",
     "SkPaintParamsKey.cpp",
     "SkPaintParamsKey.h",
+    "SkPrecompile.h",
     "SkRuntimeEffect.cpp",
     "SkSLTypeShared.h",
     "SkScopeExit.h",
diff --git a/src/core/SkFactoryFunctions.cpp b/src/core/SkFactoryFunctions.cpp
new file mode 100644
index 0000000..a57f23f
--- /dev/null
+++ b/src/core/SkFactoryFunctions.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/core/SkFactoryFunctions.h"
+
+#include "src/core/SkPrecompile.h"
+
+// TODO: wrap this in an  SK_PRECOMPILE check
+
+//--------------------------------------------------------------------------------------------------
+class SkPrecompileBlendModeBlender : public SkPrecompileBlender {
+public:
+    SkPrecompileBlendModeBlender(SkBlendMode blendMode) : fBlendMode(blendMode) {}
+
+    std::optional<SkBlendMode> asBlendMode() const final { return fBlendMode; }
+
+private:
+    SkBlendMode fBlendMode;
+};
+
+sk_sp<SkPrecompileBlender> SkPrecompileBlender::Mode(SkBlendMode blendMode) {
+    return sk_make_sp<SkPrecompileBlendModeBlender>(blendMode);
+}
+
+//--------------------------------------------------------------------------------------------------
+class SkBlendPrecompileShader : public SkPrecompileShader {
+public:
+    SkBlendPrecompileShader(SkSpan<const sk_sp<SkPrecompileBlender>> blenders,
+                            SkSpan<const sk_sp<SkPrecompileShader>> dsts,
+                            SkSpan<const sk_sp<SkPrecompileShader>> srcs)
+            : fBlenders(blenders.begin(), blenders.end())
+            , fDsts(dsts.begin(), dsts.end())
+            , fSrcs(srcs.begin(), srcs.end()) {
+    }
+
+private:
+    std::vector<sk_sp<SkPrecompileBlender>> fBlenders;
+    std::vector<sk_sp<SkPrecompileShader>> fDsts;
+    std::vector<sk_sp<SkPrecompileShader>> fSrcs;
+};
+
+//--------------------------------------------------------------------------------------------------
+sk_sp<SkPrecompileShader> SkPrecompileShaders::Color() {
+    return sk_make_sp<SkPrecompileShader>();
+}
+
+sk_sp<SkPrecompileShader> SkPrecompileShaders::Blend(
+        SkSpan<const sk_sp<SkPrecompileBlender>> blenders,
+        SkSpan<const sk_sp<SkPrecompileShader>> dsts,
+        SkSpan<const sk_sp<SkPrecompileShader>> srcs) {
+    return sk_make_sp<SkBlendPrecompileShader>(std::move(blenders),
+                                               std::move(dsts), std::move(srcs));
+}
+
+sk_sp<SkPrecompileShader> SkPrecompileShaders::Blend(
+        SkSpan<SkBlendMode> blendModes,
+        SkSpan<const sk_sp<SkPrecompileShader>> dsts,
+        SkSpan<const sk_sp<SkPrecompileShader>> srcs) {
+    std::vector<sk_sp<SkPrecompileBlender>> tmp;
+    tmp.reserve(blendModes.size());
+    for (SkBlendMode bm : blendModes) {
+        tmp.emplace_back(SkPrecompileBlender::Mode(bm));
+    }
+
+    return sk_make_sp<SkBlendPrecompileShader>(tmp, std::move(dsts), std::move(srcs));
+}
+
+sk_sp<SkPrecompileShader> SkPrecompileShaders::Image() {
+    return sk_make_sp<SkPrecompileShader>();
+}
+
+sk_sp<SkPrecompileShader> SkPrecompileShaders::LinearGradient() {
+    return sk_make_sp<SkPrecompileShader>();
+}
+
+sk_sp<SkPrecompileShader> SkPrecompileShaders::RadialGradient() {
+    return sk_make_sp<SkPrecompileShader>();
+}
+
+sk_sp<SkPrecompileShader> SkPrecompileShaders::TwoPointConicalGradient() {
+    return sk_make_sp<SkPrecompileShader>();
+}
+
+sk_sp<SkPrecompileShader> SkPrecompileShaders::SweepGradient() {
+    return sk_make_sp<SkPrecompileShader>();
+}
+
+//--------------------------------------------------------------------------------------------------
+sk_sp<SkPrecompileMaskFilter> SkPrecompileMaskFilters::Blur() {
+    return sk_make_sp<SkPrecompileMaskFilter>();
+}
+
+//--------------------------------------------------------------------------------------------------
+sk_sp<SkPrecompileColorFilter> SkPrecompileColorFilters::Matrix() {
+    return sk_make_sp<SkPrecompileColorFilter>();
+}
+
+//--------------------------------------------------------------------------------------------------
+sk_sp<SkPrecompileImageFilter> SkPrecompileImageFilters::Blur() {
+    return sk_make_sp<SkPrecompileImageFilter>();
+}
+
+sk_sp<SkPrecompileImageFilter> SkPrecompileImageFilters::Image() {
+    return sk_make_sp<SkPrecompileImageFilter>();
+}
+
+//--------------------------------------------------------------------------------------------------
diff --git a/src/core/SkFactoryFunctions.h b/src/core/SkFactoryFunctions.h
new file mode 100644
index 0000000..ea40e04
--- /dev/null
+++ b/src/core/SkFactoryFunctions.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFactoryFunctions_DEFINED
+#define SkFactoryFunctions_DEFINED
+
+#include "include/core/SkBlendMode.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSpan.h"
+
+class SkPrecompileBlender;
+class SkPrecompileColorFilter;
+class SkPrecompileImageFilter;
+class SkPrecompileMaskFilter;
+class SkPrecompileShader;
+class SkRuntimeEffect;
+
+// All of these factory functions will be moved elsewhere once the pre-compile API becomes public
+
+// TODO: wrap this in an  SK_PRECOMPILE check
+
+//--------------------------------------------------------------------------------------------------
+// This will move to be beside SkShaders in include/core/SkShader.h
+class SkPrecompileShaders {
+public:
+    //TODO: Add Empty? - see skbug.com/12165
+    static sk_sp<SkPrecompileShader> Color();
+    static sk_sp<SkPrecompileShader> Blend(SkSpan<const sk_sp<SkPrecompileBlender>> blenders,
+                                           SkSpan<const sk_sp<SkPrecompileShader>> dsts,
+                                           SkSpan<const sk_sp<SkPrecompileShader>> srcs);
+    static sk_sp<SkPrecompileShader> Blend(SkSpan<SkBlendMode> blendModes,
+                                           SkSpan<const sk_sp<SkPrecompileShader>> dsts,
+                                           SkSpan<const sk_sp<SkPrecompileShader>> srcs);
+    // TODO: add an SkShaders::Image to match this and SkImageFilters (skbug.com/13440)
+    static sk_sp<SkPrecompileShader> Image();
+
+    // TODO: make SkGradientShader match this convention (skbug.com/13438)
+    static sk_sp<SkPrecompileShader> LinearGradient();
+    static sk_sp<SkPrecompileShader> RadialGradient();
+    static sk_sp<SkPrecompileShader> TwoPointConicalGradient();
+    static sk_sp<SkPrecompileShader> SweepGradient();
+
+private:
+    SkPrecompileShaders() = delete;
+};
+
+//--------------------------------------------------------------------------------------------------
+// Initially this will go next to SkMaskFilter in include/core/SkMaskFilter.h but the
+// SkMaskFilter::MakeBlur factory should be split out or removed. This namespace will follow
+// where ever that factory goes.
+class SkPrecompileMaskFilters {
+public:
+    // TODO: change SkMaskFilter::MakeBlur to match this and SkImageFilters::Blur (skbug.com/13441)
+    static sk_sp<SkPrecompileMaskFilter> Blur();
+
+private:
+    SkPrecompileMaskFilters() = delete;
+};
+
+//--------------------------------------------------------------------------------------------------
+// This will move to be beside SkColorFilters in include/core/SkColorFilter.h
+class SkPrecompileColorFilters {
+public:
+    static sk_sp<SkPrecompileColorFilter> Matrix();
+    // TODO: Compose, Blend, HSLAMatrix, LinearToSRGBGamma, SRGBToLinearGamma, Lerp
+
+private:
+    SkPrecompileColorFilters() = delete;
+};
+
+//--------------------------------------------------------------------------------------------------
+// This will move to be beside SkImageFilters in include/effects/SkImageFilters.h
+class SkPrecompileImageFilters {
+public:
+    static sk_sp<SkPrecompileImageFilter> Blur();
+    static sk_sp<SkPrecompileImageFilter> Image();
+    // TODO: AlphaThreshold, Arithmetic, Blend (2 kinds), ColorFilter, Compose, DisplacementMap,
+    // DropShadow, DropShadowOnly, Magnifier, MatrixConvolution, MatrixTransform, Merge, Offset,
+    // Picture, Runtime, Shader, Tile, Dilate, Erode, DistantLitDiffuse, PointLitDiffuse,
+    // SpotLitDiffuse, DistantLitSpecular, PointLitSpecular, SpotLitSpecular
+
+private:
+    SkPrecompileImageFilters() = delete;
+};
+
+#endif // SkFactoryFunctions_DEFINED
diff --git a/src/core/SkPrecompile.h b/src/core/SkPrecompile.h
new file mode 100644
index 0000000..26a4af7
--- /dev/null
+++ b/src/core/SkPrecompile.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPrecompile_DEFINED
+#define SkPrecompile_DEFINED
+
+#include "include/core/SkBlendMode.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSpan.h"
+
+#include <optional>
+#include <vector>
+
+class SkRuntimeEffect;
+
+// TODO: wrap this in an  SK_PRECOMPILE check
+
+//--------------------------------------------------------------------------------------------------
+class SkPrecompileShader : public SkRefCnt {
+public:
+    // TODO: add a type enum to each of these base classes
+};
+
+class SkPrecompileMaskFilter : public SkRefCnt {
+public:
+
+};
+
+class SkPrecompileColorFilter : public SkRefCnt {
+public:
+
+};
+
+class SkPrecompileImageFilter : public SkRefCnt {
+public:
+
+};
+
+class SkPrecompileBlender : public SkRefCnt {
+public:
+    virtual std::optional<SkBlendMode> asBlendMode() const { return {}; }
+
+    static sk_sp<SkPrecompileBlender> Mode(SkBlendMode blendMode);
+
+};
+
+//--------------------------------------------------------------------------------------------------
+class SkPaintOptions {
+public:
+    void setShaders(SkSpan<const sk_sp<SkPrecompileShader>> shaders) {
+        fShaders.assign(shaders.begin(), shaders.end());
+    }
+
+    void setMaskFilters(SkSpan<const sk_sp<SkPrecompileMaskFilter>> maskFilters) {
+        fMaskFilters.assign(maskFilters.begin(), maskFilters.end());
+    }
+
+    void setColorFilters(SkSpan<const sk_sp<SkPrecompileColorFilter>> colorFilters) {
+        fColorFilters.assign(colorFilters.begin(), colorFilters.end());
+    }
+
+    void setImageFilters(SkSpan<const sk_sp<SkPrecompileImageFilter>> imageFilters) {
+        fImageFilters.assign(imageFilters.begin(), imageFilters.end());
+    }
+
+    void setBlendModes(SkSpan<SkBlendMode> blendModes) {
+        fBlenders.reserve(blendModes.size());
+        for (SkBlendMode bm : blendModes) {
+            fBlenders.emplace_back(SkPrecompileBlender::Mode(bm));
+        }
+    }
+    void setBlenders(SkSpan<const sk_sp<SkPrecompileBlender>> blenders) {
+        fBlenders.assign(blenders.begin(), blenders.end());
+    }
+
+private:
+    std::vector<sk_sp<SkPrecompileShader>> fShaders;
+    std::vector<sk_sp<SkPrecompileMaskFilter>> fMaskFilters;
+    std::vector<sk_sp<SkPrecompileColorFilter>> fColorFilters;
+    std::vector<sk_sp<SkPrecompileImageFilter>> fImageFilters;
+    std::vector<sk_sp<SkPrecompileBlender>> fBlenders;
+};
+
+#endif // SkPrecompile_DEFINED
diff --git a/tests/graphite/CombinationBuilderTest.cpp b/tests/graphite/CombinationBuilderTest.cpp
index 9ce2280..a091611 100644
--- a/tests/graphite/CombinationBuilderTest.cpp
+++ b/tests/graphite/CombinationBuilderTest.cpp
@@ -8,16 +8,19 @@
 #include "tests/Test.h"
 
 #include "include/core/SkCombinationBuilder.h"
-
+#include "src/core/SkFactoryFunctions.h"
+#include "src/core/SkPrecompile.h"
 #include "tests/graphite/CombinationBuilderTestAccess.h"
 
+#include <array>
+
 using namespace::skgpu::graphite;
 
 namespace {
 
 // For an entirely empty combination builder, both solid color shader and kSrcOver options
 // will be added
-void empty_test(Context *context, skiatest::Reporter* reporter) {
+void empty_test(Context* context, skiatest::Reporter* reporter) {
     SkCombinationBuilder builder(context);
 
     REPORTER_ASSERT(reporter, CombinationBuilderTestAccess::NumCombinations(&builder) == 1);
@@ -25,7 +28,7 @@
 
 // It is expected that the builder will supply a default solid color shader if no other shader
 // option is provided
-void no_shader_option_test(Context *context, skiatest::Reporter* reporter) {
+void no_shader_option_test(Context* context, skiatest::Reporter* reporter) {
     SkCombinationBuilder builder(context);
 
     builder.addOption(SkBlendMode::kSrcOver);
@@ -35,7 +38,7 @@
 
 // It is expected that the builder will supply a default kSrcOver blend mode if no other
 // options are added
-void no_blend_mode_option_test(Context *context, skiatest::Reporter* reporter) {
+void no_blend_mode_option_test(Context* context, skiatest::Reporter* reporter) {
     SkCombinationBuilder builder(context);
 
     builder.addOption(SkShaderType::kSolidColor);
@@ -43,7 +46,62 @@
     REPORTER_ASSERT(reporter, CombinationBuilderTestAccess::NumCombinations(&builder) == 1);
 }
 
-void big_test(Context *context, skiatest::Reporter* reporter) {
+void big_test_new(Context* context, skiatest::Reporter* reporter) {
+
+    // paintOptions
+    //  |- sweepGrad_0 | blendShader_0
+    //  |                     0: linearGrad_0 | solid_0
+    //  |                     1: linearGrad_1 | blendShader_1
+    //  |                                            0: radGrad_0 | solid_1
+    //  |                                            1: imageShader_0
+    //  |
+    //  |- 4-built-in-blend-modes
+
+    SkPaintOptions paintOptions;
+
+    // first, shaders. First top-level option (sweepGrad_0)
+    sk_sp<SkPrecompileShader> sweepGrad_0 = SkPrecompileShaders::SweepGradient();
+
+    std::array<SkBlendMode, 1> blendModes{ SkBlendMode::kSrc };
+
+    std::vector<SkBlendMode> moreBlendModes{ SkBlendMode::kDst };
+
+    // Second top-level option (blendShader_0)
+    auto blendShader_0 = SkPrecompileShaders::Blend(
+                                SkSpan<SkBlendMode>(blendModes),                // std::array
+                                {                                               // initializer_list
+                                    SkPrecompileShaders::LinearGradient(),
+                                    SkPrecompileShaders::Color()
+                                },
+                                {
+                                    SkPrecompileShaders::LinearGradient(),
+                                    SkPrecompileShaders::Blend(
+                                            SkSpan<SkBlendMode>(moreBlendModes),// std::vector
+                                            {
+                                                SkPrecompileShaders::RadialGradient(),
+                                                 SkPrecompileShaders::Color()
+                                            },
+                                            {
+                                                  SkPrecompileShaders::Image()
+                                            })
+                                });
+
+    paintOptions.setShaders({ sweepGrad_0, blendShader_0 });
+
+    SkBlendMode evenMoreBlendModes[] = {
+        SkBlendMode::kSrcOver,
+        SkBlendMode::kSrc,
+        SkBlendMode::kDstOver,
+        SkBlendMode::kDst
+    };
+
+    // now, blend modes
+    paintOptions.setBlendModes(evenMoreBlendModes);                             // c array
+
+//    context->precompile({paintOptions});
+}
+
+void big_test(Context* context, skiatest::Reporter* reporter) {
     SkCombinationBuilder builder(context);
 
     static constexpr int kMinNumStops = 4;
@@ -128,7 +186,7 @@
 }
 
 #ifdef SK_DEBUG
-void epoch_test(Context *context, skiatest::Reporter* reporter) {
+void epoch_test(Context* context, skiatest::Reporter* reporter) {
     SkCombinationBuilder builder(context);
 
     // Check that epochs are updated upon builder reset
@@ -148,6 +206,8 @@
 } // anonymous namespace
 
 DEF_GRAPHITE_TEST_FOR_CONTEXTS(CombinationBuilderTest, reporter, context) {
+    big_test_new(context, reporter);
+
     empty_test(context, reporter);
     no_shader_option_test(context, reporter);
     no_blend_mode_option_test(context, reporter);