Update GrConfigConversionEffect to use an input FP.

In a strange twist, this effect already exposed a required input FP to
external callers, but implemented its behavior using RunInSeries. This
doesn't appear to have imparted any benefit, compared to sampling from
a child FP in the usual way.

Additionally, moved a 100-line test function `GrConfigConversionEffect::
TestForPreservingPMConversions` out of the header and into the CPP.

Change-Id: Ice8c1f1e2cb76177a8a2bf6cfc3e3d163854df64
Bug: skia:10217
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300908
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/effects/generated/GrConfigConversionEffect.cpp b/src/gpu/effects/generated/GrConfigConversionEffect.cpp
index 0ec66fd..9f3422a 100644
--- a/src/gpu/effects/generated/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/generated/GrConfigConversionEffect.cpp
@@ -27,6 +27,8 @@
         (void)pmConversion;
 
         fragBuilder->forceHighPrecision();
+        SkString _sample5730;
+        _sample5730 = this->invokeChild(_outer.inputFP_index, args);
         fragBuilder->codeAppendf(
                 R"SkSL(%s = floor(%s * 255.0 + 0.5) / 255.0;
 @switch (%d) {
@@ -38,7 +40,7 @@
         break;
 }
 )SkSL",
-                args.fOutputColor, args.fInputColor, (int)_outer.pmConversion, args.fOutputColor,
+                args.fOutputColor, _sample5730.c_str(), (int)_outer.pmConversion, args.fOutputColor,
                 args.fOutputColor, args.fOutputColor, args.fOutputColor, args.fOutputColor,
                 args.fOutputColor, args.fOutputColor);
     }
@@ -62,7 +64,9 @@
 }
 GrConfigConversionEffect::GrConfigConversionEffect(const GrConfigConversionEffect& src)
         : INHERITED(kGrConfigConversionEffect_ClassID, src.optimizationFlags())
-        , pmConversion(src.pmConversion) {}
+        , pmConversion(src.pmConversion) {
+    { inputFP_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.inputFP_index)); }
+}
 std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::clone() const {
     return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(*this));
 }
@@ -72,6 +76,109 @@
         GrProcessorTestData* data) {
     PMConversion pmConv = static_cast<PMConversion>(
             data->fRandom->nextULessThan((int)PMConversion::kPMConversionCnt));
-    return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
+    return std::unique_ptr<GrFragmentProcessor>(
+            new GrConfigConversionEffect(GrProcessorUnitTest::MakeChildFP(data), pmConv));
 }
 #endif
+
+bool GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context) {
+    static constexpr int kSize = 256;
+    static constexpr GrColorType kColorType = GrColorType::kRGBA_8888;
+    SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
+    uint32_t* srcData = data.get();
+    uint32_t* firstRead = data.get() + kSize * kSize;
+    uint32_t* secondRead = data.get() + 2 * kSize * kSize;
+
+    // Fill with every possible premultiplied A, color channel value. There will be 256-y
+    // duplicate values in row y. We set r, g, and b to the same value since they are handled
+    // identically.
+    for (int y = 0; y < kSize; ++y) {
+        for (int x = 0; x < kSize; ++x) {
+            uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize * y + x]);
+            color[3] = y;
+            color[2] = std::min(x, y);
+            color[1] = std::min(x, y);
+            color[0] = std::min(x, y);
+        }
+    }
+    memset(firstRead, 0, kSize * kSize * sizeof(uint32_t));
+    memset(secondRead, 0, kSize * kSize * sizeof(uint32_t));
+
+    const SkImageInfo ii =
+            SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+    auto readRTC = GrRenderTargetContext::Make(context, kColorType, nullptr, SkBackingFit::kExact,
+                                               {kSize, kSize});
+    auto tempRTC = GrRenderTargetContext::Make(context, kColorType, nullptr, SkBackingFit::kExact,
+                                               {kSize, kSize});
+    if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
+        return false;
+    }
+    // Adding discard to appease vulkan validation warning about loading uninitialized data on
+    // draw
+    readRTC->discard();
+
+    // This function is only ever called if we are in a GrContext that has a GrGpu since we are
+    // calling read pixels here. Thus the pixel data will be uploaded immediately and we don't
+    // need to keep the pixel data alive in the proxy. Therefore the ReleaseProc is nullptr.
+    SkBitmap bitmap;
+    bitmap.installPixels(ii, srcData, 4 * kSize);
+    bitmap.setImmutable();
+
+    GrBitmapTextureMaker maker(context, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
+    auto dataView = maker.view(GrMipMapped::kNo);
+    if (!dataView) {
+        return false;
+    }
+
+    static const SkRect kRect = SkRect::MakeIWH(kSize, kSize);
+
+    // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
+    // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
+    // We then verify that two reads produced the same values.
+
+    GrPaint paint1;
+    paint1.addColorFragmentProcessor(GrConfigConversionEffect::Make(
+            GrTextureEffect::Make(std::move(dataView), kPremul_SkAlphaType),
+            PMConversion::kToUnpremul));
+    paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+    readRTC->fillRectToRect(nullptr, std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+    if (!readRTC->readPixels(ii, firstRead, 0, {0, 0})) {
+        return false;
+    }
+
+    // Adding discard to appease vulkan validation warning about loading uninitialized data on
+    // draw
+    tempRTC->discard();
+
+    GrPaint paint2;
+    paint2.addColorFragmentProcessor(GrConfigConversionEffect::Make(
+            GrTextureEffect::Make(readRTC->readSurfaceView(), kUnpremul_SkAlphaType),
+            PMConversion::kToPremul));
+    paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+    tempRTC->fillRectToRect(nullptr, std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+
+    GrPaint paint3;
+    paint3.addColorFragmentProcessor(GrConfigConversionEffect::Make(
+            GrTextureEffect::Make(tempRTC->readSurfaceView(), kPremul_SkAlphaType),
+            PMConversion::kToUnpremul));
+    paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+    readRTC->fillRectToRect(nullptr, std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+
+    if (!readRTC->readPixels(ii, secondRead, 0, {0, 0})) {
+        return false;
+    }
+
+    for (int y = 0; y < kSize; ++y) {
+        for (int x = 0; x <= y; ++x) {
+            if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}