Update MakeChildFP to allow processor hierarchies to be created.

Previously, MakeChildFP avoided infinite recursion by rejecting any FP
that took inputs. MakeChildFP now generates random inputs up to a
user-supplied tree depth.

The ProcessorOptimizationValidationTest test has been updated to
test up to a tree depth of 3. The ProcessorCloneTest has been left at
a tree depth of 1 due to a bug that only appears on Galaxy S20/Mali G77.
The Mali bug doesn't appear to be related to FP cloning, but probably
deserves further analysis. (It appears that on this device, these
processors hooked together in sequence render a tiny bit differently
each time: DitherEffect -> RectBlurEffect -> ImprovedPerlinNoise. By
visual inspection it looks like the dither varies on each draw.)

Change-Id: Ib8f619eb7a8a9c9254080303504c20065ff35453
Bug: skia:10384, skia:10595
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308556
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/src/gpu/GrProcessorUnitTest.cpp b/src/gpu/GrProcessorUnitTest.cpp
index 0f47ba5..013b57a 100644
--- a/src/gpu/GrProcessorUnitTest.cpp
+++ b/src/gpu/GrProcessorUnitTest.cpp
@@ -12,19 +12,24 @@
 #include "include/gpu/GrRecordingContext.h"
 #include "src/gpu/GrFragmentProcessor.h"
 #include "src/gpu/GrRecordingContextPriv.h"
+#include "src/gpu/effects/generated/GrConstColorProcessor.h"
 
 #if GR_TEST_UTILS
 
 class GrGeometryProcessor;
 
 GrProcessorTestData::GrProcessorTestData(SkRandom* random, GrRecordingContext* context,
-                                         int numViews, const ViewInfo views[])
-        : GrProcessorTestData(random, context, numViews, views, /*inputFP=*/nullptr) {}
+                                         int maxTreeDepth, int numViews, const ViewInfo views[])
+        : GrProcessorTestData(random, context, maxTreeDepth, numViews, views,
+                              /*inputFP=*/nullptr) {}
 
 GrProcessorTestData::GrProcessorTestData(SkRandom* random, GrRecordingContext* context,
-                                         int numViews, const ViewInfo views[],
+                                         int maxTreeDepth, int numViews, const ViewInfo views[],
                                          std::unique_ptr<GrFragmentProcessor> inputFP)
-        : fRandom(random), fContext(context), fInputFP(std::move(inputFP)) {
+        : fRandom(random)
+        , fMaxTreeDepth(maxTreeDepth)
+        , fContext(context)
+        , fInputFP(std::move(inputFP)) {
     fViews.reset(views, numViews);
     fArena = std::make_unique<SkArenaAlloc>(1000);
 }
@@ -35,7 +40,15 @@
 
 const GrCaps* GrProcessorTestData::caps() { return fContext->priv().caps(); }
 
-std::unique_ptr<GrFragmentProcessor> GrProcessorTestData::inputFP() { return std::move(fInputFP); }
+std::unique_ptr<GrFragmentProcessor> GrProcessorTestData::inputFP() {
+    if (fCurrentTreeDepth == 0) {
+        // At the top level of the tree, provide the input FP from the test data.
+        return fInputFP ? fInputFP->clone() : nullptr;
+    } else {
+        // At deeper levels of recursion, synthesize a random input.
+        return GrProcessorUnitTest::MakeChildFP(this);
+    }
+}
 
 GrProcessorTestData::ViewInfo GrProcessorTestData::randomView() {
     SkASSERT(!fViews.empty());
@@ -164,13 +177,34 @@
 
 std::unique_ptr<GrFragmentProcessor> GrProcessorUnitTest::MakeChildFP(GrProcessorTestData* data) {
     std::unique_ptr<GrFragmentProcessor> fp;
-    do {
-        fp = GrFragmentProcessorTestFactory::Make(data);
-        SkASSERT(fp);
-    } while (fp->numNonNullChildProcessors() != 0);
+
+    ++data->fCurrentTreeDepth;
+    if (data->fCurrentTreeDepth > data->fMaxTreeDepth) {
+        // We've gone too deep, but we can't necessarily return null without risking an assertion.
+        // Instead, return a known-simple zero-child FP. This limits the recursion, and the
+        // generated FP will be rejected by the numNonNullChildProcessors check below.
+        fp = GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT);
+    } else {
+        for (;;) {
+            fp = GrFragmentProcessorTestFactory::Make(data);
+            SkASSERT(fp);
+            // If our tree has already reached its max depth, we must reject FPs that have children.
+            if (data->fCurrentTreeDepth < data->fMaxTreeDepth ||
+                fp->numNonNullChildProcessors() == 0) {
+                break;
+            }
+        }
+    }
+
+    --data->fCurrentTreeDepth;
     return fp;
 }
 
+std::unique_ptr<GrFragmentProcessor> GrProcessorUnitTest::MakeOptionalChildFP(
+        GrProcessorTestData* data) {
+    return data->fRandom->nextBool() ? MakeChildFP(data) : nullptr;
+}
+
 template class GrProcessorTestFactory<GrGeometryProcessor*>;
 template class GrProcessorTestFactory<std::unique_ptr<GrFragmentProcessor>>;
 
diff --git a/src/gpu/GrProcessorUnitTest.h b/src/gpu/GrProcessorUnitTest.h
index 5225c1f..7c2cbde 100644
--- a/src/gpu/GrProcessorUnitTest.h
+++ b/src/gpu/GrProcessorUnitTest.h
@@ -37,25 +37,27 @@
 };
 
 /** This allows parent FPs to implement a test create with known leaf children in order to avoid
-creating an unbounded FP tree which may overflow various shader limits. */
+ *  creating an unbounded FP tree which may overflow various shader limits.
+ *  MakeOptionalChildFP is the same as MakeChildFP, but can return null.
+ */
 std::unique_ptr<GrFragmentProcessor> MakeChildFP(GrProcessorTestData*);
+std::unique_ptr<GrFragmentProcessor> MakeOptionalChildFP(GrProcessorTestData*);
 
 }  // namespace GrProcessorUnitTest
 
-/*
- * GrProcessorTestData is an argument struct to TestCreate functions
- * fTextures are valid textures that can optionally be used to construct
- * TextureSampler. The first texture has a RGBA8 format and the second has Alpha8 format for the
- * specific backend API. TestCreate functions are also free to create additional textures using
- * the GrContext.
+/** GrProcessorTestData is an argument struct to TestCreate functions
+ *  fTextures are valid textures that can optionally be used to construct
+ *  TextureSampler. The first texture has a RGBA8 format and the second has Alpha8 format for the
+ *  specific backend API. TestCreate functions are also free to create additional textures using
+ *  the GrContext.
  */
 class GrProcessorTestData {
 public:
     using ViewInfo = std::tuple<GrSurfaceProxyView, GrColorType, SkAlphaType>;
 
-    GrProcessorTestData(SkRandom* random, GrRecordingContext* context,
+    GrProcessorTestData(SkRandom* random, GrRecordingContext* context, int maxTreeDepth,
                         int numViews, const ViewInfo views[]);
-    GrProcessorTestData(SkRandom* random, GrRecordingContext* context,
+    GrProcessorTestData(SkRandom* random, GrRecordingContext* context, int maxTreeDepth,
                         int numViews, const ViewInfo views[],
                         std::unique_ptr<GrFragmentProcessor> inputFP);
     GrProcessorTestData(const GrProcessorTestData&) = delete;
@@ -71,6 +73,8 @@
     ViewInfo randomAlphaOnlyView();
 
     SkRandom* fRandom;
+    int fCurrentTreeDepth = 0;
+    int fMaxTreeDepth = 1;
 
 private:
     GrRecordingContext* fContext;
diff --git a/src/gpu/effects/GrBlendFragmentProcessor.cpp b/src/gpu/effects/GrBlendFragmentProcessor.cpp
index 83d701a..e1a7dea 100644
--- a/src/gpu/effects/GrBlendFragmentProcessor.cpp
+++ b/src/gpu/effects/GrBlendFragmentProcessor.cpp
@@ -239,19 +239,22 @@
 
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> BlendFragmentProcessor::TestCreate(GrProcessorTestData* d) {
-    // Create two random frag procs.
-    std::unique_ptr<GrFragmentProcessor> fpA(GrProcessorUnitTest::MakeChildFP(d));
-    std::unique_ptr<GrFragmentProcessor> fpB(GrProcessorUnitTest::MakeChildFP(d));
+    // Create one or two random fragment processors.
+    std::unique_ptr<GrFragmentProcessor> src(GrProcessorUnitTest::MakeOptionalChildFP(d));
+    std::unique_ptr<GrFragmentProcessor> dst(GrProcessorUnitTest::MakeChildFP(d));
+    if (d->fRandom->nextBool()) {
+        std::swap(src, dst);
+    }
 
     SkBlendMode mode;
     BlendBehavior behavior;
     do {
         mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
         behavior = static_cast<BlendBehavior>(
-                       d->fRandom->nextRangeU(0, (int)BlendBehavior::kLastBlendBehavior));
+                d->fRandom->nextRangeU(0, (int)BlendBehavior::kLastBlendBehavior));
     } while (SkBlendMode::kClear == mode || SkBlendMode::kSrc == mode || SkBlendMode::kDst == mode);
     return std::unique_ptr<GrFragmentProcessor>(
-            new BlendFragmentProcessor(std::move(fpA), std::move(fpB), mode, behavior));
+            new BlendFragmentProcessor(std::move(src), std::move(dst), mode, behavior));
 }
 #endif