converted GrConfigConversionEffect to SkSL

Bug: skia:
Change-Id: If17cf0fc8b857d22f33a462a39a02bcddd15deda
Reviewed-on: https://skia-review.googlesource.com/55741
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 65913e3..55e8bd2 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -29,6 +29,7 @@
   "$_src/effects/GrCircleBlurFragmentProcessor.fp",
   "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.fp",
   "$_src/gpu/effects/GrCircleEffect.fp",
+  "$_src/gpu/effects/GrConfigConversionEffect.fp",
   "$_src/gpu/effects/GrDitherEffect.fp",
   "$_src/gpu/effects/GrEllipseEffect.fp",
   "$_src/gpu/effects/GrSimpleTextureEffect.fp",
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index e1ecc7f..2c12c7e 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -1,212 +1,71 @@
 /*
- * Copyright 2012 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
+/*
+ * This file was autogenerated from GrConfigConversionEffect.fp; do not modify.
+ */
 #include "GrConfigConversionEffect.h"
-#include "../private/GrGLSL.h"
-#include "GrClip.h"
-#include "GrContext.h"
-#include "GrRenderTargetContext.h"
-#include "SkMatrix.h"
+#if SK_SUPPORT_GPU
+#include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
-
-class GrGLConfigConversionEffect : public GrGLSLFragmentProcessor {
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "SkSLCPP.h"
+#include "SkSLUtil.h"
+class GrGLSLConfigConversionEffect : public GrGLSLFragmentProcessor {
 public:
+    GrGLSLConfigConversionEffect() {}
     void emitCode(EmitArgs& args) override {
-        const GrConfigConversionEffect& cce = args.fFp.cast<GrConfigConversionEffect>();
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+        const GrConfigConversionEffect& _outer = args.fFp.cast<GrConfigConversionEffect>();
+        (void)_outer;
 
-        // Use highp throughout the shader to avoid some precision issues on specific GPUs.
         fragBuilder->forceHighPrecision();
-
-        if (nullptr == args.fInputColor) {
-            // could optimize this case, but we aren't for now.
-            args.fInputColor = "half4(1)";
-        }
-
-        // Aggressively round to the nearest exact (N / 255) floating point value. This lets us
-        // find a round-trip preserving pair on some GPUs that do odd byte to float conversion.
-        fragBuilder->codeAppendf("half4 color = floor(%s * 255.0 + 0.5) / 255.0;", args.fInputColor);
-
-        switch (cce.pmConversion()) {
-            case GrConfigConversionEffect::kToPremul_PMConversion:
-                fragBuilder->codeAppend(
-                    "color.rgb = floor(color.rgb * color.a * 255.0 + 0.5) / 255.0;");
-                break;
-
-            case GrConfigConversionEffect::kToUnpremul_PMConversion:
-                fragBuilder->codeAppend(
-                    "color.rgb = color.a <= 0.0 ? half3(0,0,0) : floor(color.rgb / color.a * 255.0 + 0.5) / 255.0;");
-                break;
-
-            default:
-                SK_ABORT("Unknown conversion op.");
-                break;
-        }
-        fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
-    }
-
-    static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
-                              GrProcessorKeyBuilder* b) {
-        const GrConfigConversionEffect& cce = processor.cast<GrConfigConversionEffect>();
-        uint32_t key = cce.pmConversion();
-        b->add32(key);
+        fragBuilder->codeAppendf(
+                "%s = half4(floor(float4(float4(%s * 255.0) + 0.5)) / 255.0);\n@switch (%d) {\n    "
+                "case 0:\n        %s.xyz = half3(floor(float3(float3((%s.xyz * %s.w) * 255.0) + "
+                "0.5)) / 255.0);\n        break;\n    case 1:\n        %s.xyz = float(%s.w) <= 0.0 "
+                "? half3(0.0) : half3(floor(float3(float3((%s.xyz / %s.w) * 255.0) + 0.5)) / "
+                "255.0);\n        break;\n}\n",
+                args.fOutputColor, args.fInputColor ? args.fInputColor : "half4(1)",
+                _outer.pmConversion(), args.fOutputColor, args.fOutputColor, args.fOutputColor,
+                args.fOutputColor, args.fOutputColor, args.fOutputColor, args.fOutputColor);
     }
 
 private:
-    typedef GrGLSLFragmentProcessor INHERITED;
-
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& _proc) override {}
 };
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrConfigConversionEffect::GrConfigConversionEffect(PMConversion pmConversion)
-        : INHERITED(kNone_OptimizationFlags)
-        , fPMConversion(pmConversion) {
+GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const {
+    return new GrGLSLConfigConversionEffect();
+}
+void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                     GrProcessorKeyBuilder* b) const {
+    b->add32(fPmConversion);
+}
+bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrConfigConversionEffect& that = other.cast<GrConfigConversionEffect>();
+    (void)that;
+    if (fPmConversion != that.fPmConversion) return false;
+    return true;
+}
+GrConfigConversionEffect::GrConfigConversionEffect(const GrConfigConversionEffect& src)
+        : INHERITED(src.optimizationFlags()), fPmConversion(src.fPmConversion) {
     this->initClassID<GrConfigConversionEffect>();
 }
-
 std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::clone() const {
-    return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(fPMConversion));
+    return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(*this));
 }
-
-bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const {
-    const GrConfigConversionEffect& other = s.cast<GrConfigConversionEffect>();
-    return other.fPMConversion == fPMConversion;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect);
-
 #if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::TestCreate(GrProcessorTestData* d) {
-    PMConversion pmConv = static_cast<PMConversion>(d->fRandom->nextULessThan(kPMConversionCnt));
+std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::TestCreate(
+        GrProcessorTestData* data) {
+    PMConversion pmConv = static_cast<PMConversion>(data->fRandom->nextULessThan(kPMConversionCnt));
     return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
 }
 #endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                     GrProcessorKeyBuilder* b) const {
-    GrGLConfigConversionEffect::GenKey(*this, caps, b);
-}
-
-GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const {
-    return new GrGLConfigConversionEffect();
-}
-
-
-bool GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context) {
-    static constexpr int kSize = 256;
-    static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
-    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] = SkTMin(x, y);
-            color[1] = SkTMin(x, y);
-            color[0] = SkTMin(x, y);
-        }
-    }
-
-    const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
-                                             kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-
-    sk_sp<GrRenderTargetContext> readRTC(context->makeDeferredRenderTargetContext(
-                                                                          SkBackingFit::kExact,
-                                                                          kSize, kSize,
-                                                                          kConfig, nullptr));
-    sk_sp<GrRenderTargetContext> tempRTC(context->makeDeferredRenderTargetContext(
-                                                                          SkBackingFit::kExact,
-                                                                          kSize, kSize,
-                                                                          kConfig, nullptr));
-    if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
-        return false;
-    }
-    GrSurfaceDesc desc;
-    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-    desc.fWidth = kSize;
-    desc.fHeight = kSize;
-    desc.fConfig = kConfig;
-
-    sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
-                                                                   desc,
-                                                                   SkBudgeted::kYes, data, 0);
-    if (!dataProxy) {
-        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;
-    GrPaint paint2;
-    GrPaint paint3;
-    std::unique_ptr<GrFragmentProcessor> pmToUPM(
-            new GrConfigConversionEffect(kToUnpremul_PMConversion));
-    std::unique_ptr<GrFragmentProcessor> upmToPM(
-            new GrConfigConversionEffect(kToPremul_PMConversion));
-
-    paint1.addColorTextureProcessor(dataProxy, nullptr, SkMatrix::I());
-    paint1.addColorFragmentProcessor(pmToUPM->clone());
-    paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
-
-    readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, kRect);
-    if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
-        return false;
-    }
-
-    paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), nullptr,
-                                    SkMatrix::I());
-    paint2.addColorFragmentProcessor(std::move(upmToPM));
-    paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
-
-    tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, kRect);
-
-    paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), nullptr,
-                                    SkMatrix::I());
-    paint3.addColorFragmentProcessor(std::move(pmToUPM));
-    paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
-
-    readRTC->fillRectToRect(GrNoClip(), 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;
-}
-
-std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::Make(
-        std::unique_ptr<GrFragmentProcessor> fp, PMConversion pmConversion) {
-    if (!fp) {
-        return nullptr;
-    }
-    std::unique_ptr<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion));
-    std::unique_ptr<GrFragmentProcessor> fpPipeline[] = { std::move(fp), std::move(ccFP) };
-    return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
-}
+#endif
diff --git a/src/gpu/effects/GrConfigConversionEffect.fp b/src/gpu/effects/GrConfigConversionEffect.fp
new file mode 100644
index 0000000..9c697b4
--- /dev/null
+++ b/src/gpu/effects/GrConfigConversionEffect.fp
@@ -0,0 +1,157 @@
+@header {
+    #include "GrClip.h"
+    #include "GrContext.h"
+    #include "GrRenderTargetContext.h"
+}
+
+@class {
+    enum PMConversion {
+        kToPremul_PMConversion   = 0,
+        kToUnpremul_PMConversion = 1,
+        kPMConversionCnt         = 2
+    };
+
+    static bool TestForPreservingPMConversions(GrContext* context) {
+        static constexpr int kSize = 256;
+        static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
+        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] = SkTMin(x, y);
+                color[1] = SkTMin(x, y);
+                color[0] = SkTMin(x, y);
+            }
+        }
+
+        const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
+                                                 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+        sk_sp<GrRenderTargetContext> readRTC(context->makeDeferredRenderTargetContext(
+                                                                              SkBackingFit::kExact,
+                                                                              kSize, kSize,
+                                                                              kConfig, nullptr));
+        sk_sp<GrRenderTargetContext> tempRTC(context->makeDeferredRenderTargetContext(
+                                                                              SkBackingFit::kExact,
+                                                                              kSize, kSize,
+                                                                              kConfig, nullptr));
+        if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
+            return false;
+        }
+        GrSurfaceDesc desc;
+        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+        desc.fWidth = kSize;
+        desc.fHeight = kSize;
+        desc.fConfig = kConfig;
+
+        sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                       desc,
+                                                                       SkBudgeted::kYes, data, 0);
+        if (!dataProxy) {
+            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;
+        GrPaint paint2;
+        GrPaint paint3;
+        std::unique_ptr<GrFragmentProcessor> pmToUPM(
+                new GrConfigConversionEffect(kToUnpremul_PMConversion));
+        std::unique_ptr<GrFragmentProcessor> upmToPM(
+                new GrConfigConversionEffect(kToPremul_PMConversion));
+
+        paint1.addColorTextureProcessor(dataProxy, nullptr, SkMatrix::I());
+        paint1.addColorFragmentProcessor(pmToUPM->clone());
+        paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+        readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect,
+                                kRect);
+        if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
+            return false;
+        }
+
+        paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), nullptr,
+                                        SkMatrix::I());
+        paint2.addColorFragmentProcessor(std::move(upmToPM));
+        paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+        tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect,
+                                kRect);
+
+        paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), nullptr,
+                                        SkMatrix::I());
+        paint3.addColorFragmentProcessor(std::move(pmToUPM));
+        paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+        readRTC->fillRectToRect(GrNoClip(), 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;
+    }
+}
+
+@make {
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                     PMConversion pmConversion) {
+        if (!fp) {
+            return nullptr;
+        }
+        std::unique_ptr<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion));
+        std::unique_ptr<GrFragmentProcessor> fpPipeline[] = { std::move(fp), std::move(ccFP) };
+        return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
+    }
+}
+
+layout(key) in int pmConversion;
+
+@emitCode {
+    fragBuilder->forceHighPrecision();
+}
+
+void main() {
+    // Aggressively round to the nearest exact (N / 255) floating point value. This lets us find a
+    // round-trip preserving pair on some GPUs that do odd byte to float conversion.
+    sk_OutColor = floor(sk_InColor * 255 + 0.5) / 255;
+
+    @switch (pmConversion) {
+        case 0 /* kToPremul_PMConversion */:
+            sk_OutColor.rgb = floor(sk_OutColor.rgb * sk_OutColor.a * 255 + 0.5) / 255;
+            break;
+
+        case 1 /* kToUnpremul_PMConversion */:
+            sk_OutColor.rgb = sk_OutColor.a <= 0.0 ?
+                                          half3(0) :
+                                          floor(sk_OutColor.rgb / sk_OutColor.a * 255 + 0.5) / 255;
+            break;
+    }
+}
+
+@test(data) {
+    PMConversion pmConv = static_cast<PMConversion>(data->fRandom->nextULessThan(kPMConversionCnt));
+    return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
+}
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index 690fbd5..f17b32f 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -1,64 +1,153 @@
 /*
- * Copyright 2012 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
+/*
+ * This file was autogenerated from GrConfigConversionEffect.fp; do not modify.
+ */
 #ifndef GrConfigConversionEffect_DEFINED
 #define GrConfigConversionEffect_DEFINED
+#include "SkTypes.h"
+#if SK_SUPPORT_GPU
 
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrRenderTargetContext.h"
 #include "GrFragmentProcessor.h"
-
-/**
- * This class is used to perform config conversions. Clients may want to read/write data that is
- * unpremultiplied.
- */
+#include "GrCoordTransform.h"
+#include "GrColorSpaceXform.h"
 class GrConfigConversionEffect : public GrFragmentProcessor {
 public:
-    /**
-     * The PM->UPM or UPM->PM conversions to apply.
-     */
     enum PMConversion {
         kToPremul_PMConversion = 0,
-        kToUnpremul_PMConversion,
-        kPMConversionCnt
+        kToUnpremul_PMConversion = 1,
+        kPMConversionCnt = 2
     };
 
-    /**
-     *  Returns a fragment processor that calls the passed in fragment processor, and then performs
-     *  the requested premul or unpremul conversion.
-     */
-    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
-                                                     PMConversion);
+    static bool TestForPreservingPMConversions(GrContext* context) {
+        static constexpr int kSize = 256;
+        static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
+        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;
 
-    const char* name() const override { return "Config Conversion"; }
+        // 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] = SkTMin(x, y);
+                color[1] = SkTMin(x, y);
+                color[0] = SkTMin(x, y);
+            }
+        }
 
+        const SkImageInfo ii =
+                SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+        sk_sp<GrRenderTargetContext> readRTC(context->makeDeferredRenderTargetContext(
+                SkBackingFit::kExact, kSize, kSize, kConfig, nullptr));
+        sk_sp<GrRenderTargetContext> tempRTC(context->makeDeferredRenderTargetContext(
+                SkBackingFit::kExact, kSize, kSize, kConfig, nullptr));
+        if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
+            return false;
+        }
+        GrSurfaceDesc desc;
+        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+        desc.fWidth = kSize;
+        desc.fHeight = kSize;
+        desc.fConfig = kConfig;
+
+        sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(
+                context->resourceProvider(), desc, SkBudgeted::kYes, data, 0);
+        if (!dataProxy) {
+            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;
+        GrPaint paint2;
+        GrPaint paint3;
+        std::unique_ptr<GrFragmentProcessor> pmToUPM(
+                new GrConfigConversionEffect(kToUnpremul_PMConversion));
+        std::unique_ptr<GrFragmentProcessor> upmToPM(
+                new GrConfigConversionEffect(kToPremul_PMConversion));
+
+        paint1.addColorTextureProcessor(dataProxy, nullptr, SkMatrix::I());
+        paint1.addColorFragmentProcessor(pmToUPM->clone());
+        paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+        readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect,
+                                kRect);
+        if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
+            return false;
+        }
+
+        paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), nullptr, SkMatrix::I());
+        paint2.addColorFragmentProcessor(std::move(upmToPM));
+        paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+        tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect,
+                                kRect);
+
+        paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), nullptr, SkMatrix::I());
+        paint3.addColorFragmentProcessor(std::move(pmToUPM));
+        paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+        readRTC->fillRectToRect(GrNoClip(), 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;
+    }
+    int pmConversion() const { return fPmConversion; }
+
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                     PMConversion pmConversion) {
+        if (!fp) {
+            return nullptr;
+        }
+        std::unique_ptr<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion));
+        std::unique_ptr<GrFragmentProcessor> fpPipeline[] = {std::move(fp), std::move(ccFP)};
+        return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
+    }
+    GrConfigConversionEffect(const GrConfigConversionEffect& src);
     std::unique_ptr<GrFragmentProcessor> clone() const override;
-
-    PMConversion  pmConversion() const { return fPMConversion; }
-
-    // This function determines whether it is possible to choose PM->UPM and UPM->PM conversions
-    // for which in any PM->UPM->PM->UPM sequence the two UPM values are the same. This means that
-    // if pixels are read back to a UPM buffer, written back to PM to the GPU, and read back again
-    // both reads will produce the same result. This test is quite expensive and should not be run
-    // multiple times for a given context.
-    static bool TestForPreservingPMConversions(GrContext* context);
+    const char* name() const override { return "ConfigConversionEffect"; }
 
 private:
-    GrConfigConversionEffect(PMConversion);
-
+    GrConfigConversionEffect(int pmConversion)
+            : INHERITED(kNone_OptimizationFlags), fPmConversion(pmConversion) {
+        this->initClassID<GrConfigConversionEffect>();
+    }
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
     bool onIsEqual(const GrFragmentProcessor&) const override;
-
-    PMConversion    fPMConversion;
-
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
+    int fPmConversion;
     typedef GrFragmentProcessor INHERITED;
 };
-
+#endif
 #endif