Remove all config conversion modes except round-to-nearest

On all GPUs where we can perfectly round-trip, this mode does so.
This mode fails on Mali 400 and Tegra 3, but nothing works there.

Bug: skia:
Change-Id: Ifb045fc772a5b1c03b51b5cb2ae039fe792d17bb
Reviewed-on: https://skia-review.googlesource.com/13271
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 08198be..158699b 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -313,8 +313,8 @@
 
     bool                                    fDisableGpuYUVConversion;
     bool                                    fDidTestPMConversions;
-    int                                     fPMToUPMConversion;
-    int                                     fUPMToPMConversion;
+    // true if the PM/UPM conversion succeeded; false otherwise
+    bool                                    fPMUPMConversionsRoundTrip;
 
     // In debug builds we guard against improper thread handling
     // This guard is passed to the GrDrawingManager and, from there to all the
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 0edfb29..02ffe3c 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -878,16 +878,6 @@
     return fDrawingManager->wasAbandoned();
 }
 
-namespace {
-void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) {
-    GrConfigConversionEffect::PMConversion pmToUPM;
-    GrConfigConversionEffect::PMConversion upmToPM;
-    GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM);
-    *pmToUPMValue = pmToUPM;
-    *upmToPMValue = upmToPM;
-}
-}
-
 sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrFragmentProcessor> fp,
                                                           bool useConfigConversionEffect) {
     ASSERT_SINGLE_OWNER
@@ -898,9 +888,8 @@
         // ...and it should have succeeded
         SkASSERT(this->validPMUPMConversionExists());
 
-        GrConfigConversionEffect::PMConversion pmToUPM =
-            static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
-        return GrConfigConversionEffect::Make(std::move(fp), pmToUPM);
+        return GrConfigConversionEffect::Make(std::move(fp),
+                                              GrConfigConversionEffect::kToUnpremul_PMConversion);
     } else {
         // For everything else (sRGB, half-float, etc...), it doesn't make sense to try and
         // explicitly round the results. Just do the obvious, naive thing in the shader.
@@ -918,9 +907,8 @@
         // ...and it should have succeeded
         SkASSERT(this->validPMUPMConversionExists());
 
-        GrConfigConversionEffect::PMConversion upmToPM =
-            static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
-        return GrConfigConversionEffect::Make(std::move(fp), upmToPM);
+        return GrConfigConversionEffect::Make(std::move(fp),
+                                              GrConfigConversionEffect::kToPremul_PMConversion);
     } else {
         // For everything else (sRGB, half-float, etc...), it doesn't make sense to try and
         // explicitly round the results. Just do the obvious, naive thing in the shader.
@@ -931,12 +919,12 @@
 bool GrContext::validPMUPMConversionExists() {
     ASSERT_SINGLE_OWNER
     if (!fDidTestPMConversions) {
-        test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
+        fPMUPMConversionsRoundTrip = GrConfigConversionEffect::TestForPreservingPMConversions(this);
         fDidTestPMConversions = true;
     }
 
     // The PM<->UPM tests fail or succeed together so we only need to check one.
-    return GrConfigConversionEffect::kPMConversionCnt != fPMToUPMConversion;
+    return fPMUPMConversionsRoundTrip;
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 566a1af..2e0886f 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -30,36 +30,15 @@
 
         // 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("vec4 color = floor(%s * 255.0 + 0.5) / 255.0;",
-                                args.fInputColor);
+        fragBuilder->codeAppendf("vec4 color = floor(%s * 255.0 + 0.5) / 255.0;", args.fInputColor);
 
         switch (cce.pmConversion()) {
-            case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
-                fragBuilder->codeAppend(
-                    "color.rgb = ceil(color.rgb * color.a * 255.0) / 255.0;");
-                break;
-            case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
-                // Add a compensation(0.001) here to avoid the side effect of the floor operation.
-                // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0
-                // is less than the integer value converted from  %s.r by 1 when the %s.r is
-                // converted from the integer value 2^n, such as 1, 2, 4, 8, etc.
-                fragBuilder->codeAppend(
-                    "color.rgb = floor(color.rgb * color.a * 255.0 + 0.001) / 255.0;");
-                break;
-            case GrConfigConversionEffect::kMulByAlpha_RoundNearest_PMConversion:
+            case GrConfigConversionEffect::kToPremul_PMConversion:
                 fragBuilder->codeAppend(
                     "color.rgb = floor(color.rgb * color.a * 255.0 + 0.5) / 255.0;");
                 break;
 
-            case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
-                fragBuilder->codeAppend(
-                    "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : ceil(color.rgb / color.a * 255.0) / 255.0;");
-                break;
-            case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
-                fragBuilder->codeAppend(
-                    "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0) / 255.0;");
-                break;
-            case GrConfigConversionEffect::kDivByAlpha_RoundNearest_PMConversion:
+            case GrConfigConversionEffect::kToUnpremul_PMConversion:
                 fragBuilder->codeAppend(
                     "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0 + 0.5) / 255.0;");
                 break;
@@ -119,12 +98,7 @@
 }
 
 
-
-void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
-                                                              PMConversion* pmToUPMRule,
-                                                              PMConversion* upmToPMRule) {
-    *pmToUPMRule = kPMConversionCnt;
-    *upmToPMRule = kPMConversionCnt;
+bool GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context) {
     static constexpr int kSize = 256;
     static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
     SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
@@ -153,8 +127,8 @@
     sk_sp<GrRenderTargetContext> tempRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
                                                                           kSize, kSize,
                                                                           kConfig, nullptr));
-    if (!readRTC || !tempRTC) {
-        return;
+    if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
+        return false;
     }
     GrSurfaceDesc desc;
     desc.fWidth = kSize;
@@ -165,90 +139,57 @@
     sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
                                                                    SkBudgeted::kYes, data, 0);
     if (!dataProxy) {
-        return;
+        return false;
     }
 
-    static const PMConversion kConversionRules[][2] = {
-        {kDivByAlpha_RoundNearest_PMConversion, kMulByAlpha_RoundNearest_PMConversion},
-        {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
-        {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
-    };
+    static const SkRect kRect = SkRect::MakeIWH(kSize, kSize);
 
-    uint32_t bestFailCount = 0xFFFFFFFF;
-    size_t bestRule = 0;
+    // 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.
 
-    for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && bestFailCount; ++i) {
-        *pmToUPMRule = kConversionRules[i][0];
-        *upmToPMRule = kConversionRules[i][1];
+    GrPaint paint1;
+    GrPaint paint2;
+    GrPaint paint3;
+    sk_sp<GrFragmentProcessor> pmToUPM(new GrConfigConversionEffect(kToUnpremul_PMConversion));
+    sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(kToPremul_PMConversion));
 
-        static const SkRect kDstRect = SkRect::MakeIWH(kSize, kSize);
-        static const SkRect kSrcRect = 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.
+    paint1.addColorTextureProcessor(resourceProvider, dataProxy, nullptr, SkMatrix::I());
+    paint1.addColorFragmentProcessor(pmToUPM);
+    paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-        if (!readRTC->asTextureProxy()) {
-            continue;
-        }
-        GrPaint paint1;
-        GrPaint paint2;
-        GrPaint paint3;
-        sk_sp<GrFragmentProcessor> pmToUPM(new GrConfigConversionEffect(*pmToUPMRule));
-        sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(*upmToPMRule));
+    readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+    if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
+        return false;
+    }
 
-        paint1.addColorTextureProcessor(resourceProvider, dataProxy, nullptr, SkMatrix::I());
-        paint1.addColorFragmentProcessor(pmToUPM);
-        paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint2.addColorTextureProcessor(resourceProvider, readRTC->asTextureProxyRef(), nullptr,
+                                    SkMatrix::I());
+    paint2.addColorFragmentProcessor(std::move(upmToPM));
+    paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-        readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kDstRect,
-                                kSrcRect);
+    tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, kRect);
 
-        if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
-            continue;
-        }
+    paint3.addColorTextureProcessor(resourceProvider, tempRTC->asTextureProxyRef(), nullptr,
+                                    SkMatrix::I());
+    paint3.addColorFragmentProcessor(std::move(pmToUPM));
+    paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-        paint2.addColorTextureProcessor(resourceProvider, readRTC->asTextureProxyRef(), nullptr,
-                                        SkMatrix::I());
-        paint2.addColorFragmentProcessor(std::move(upmToPM));
-        paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, kRect);
 
-        tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kDstRect,
-                                kSrcRect);
+    if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
+        return false;
+    }
 
-        paint3.addColorTextureProcessor(resourceProvider, tempRTC->asTextureProxyRef(), nullptr,
-                                        SkMatrix::I());
-        paint3.addColorFragmentProcessor(std::move(pmToUPM));
-        paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
-
-        readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kDstRect,
-                                kSrcRect);
-
-        if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
-            continue;
-        }
-
-        uint32_t failCount = 0;
-        for (int y = 0; y < kSize; ++y) {
-            for (int x = 0; x <= y; ++x) {
-                if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
-                    if (++failCount >= bestFailCount) {
-                        break;
-                    }
-                }
+    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;
             }
         }
-        if (failCount < bestFailCount) {
-            bestFailCount = failCount;
-            bestRule = i;
-        }
     }
-    if (bestFailCount > 0) {
-        *pmToUPMRule = kPMConversionCnt;
-        *upmToPMRule = kPMConversionCnt;
-    } else {
-        *pmToUPMRule = kConversionRules[bestRule][0];
-        *upmToPMRule = kConversionRules[bestRule][1];
-    }
+
+    return true;
 }
 
 sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(sk_sp<GrFragmentProcessor> fp,
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index 4cc2aeb..4b0c02d 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -20,14 +20,8 @@
      * The PM->UPM or UPM->PM conversions to apply.
      */
     enum PMConversion {
-        kMulByAlpha_RoundUp_PMConversion = 0,
-        kMulByAlpha_RoundDown_PMConversion,
-        kMulByAlpha_RoundNearest_PMConversion,
-
-        kDivByAlpha_RoundUp_PMConversion,
-        kDivByAlpha_RoundDown_PMConversion,
-        kDivByAlpha_RoundNearest_PMConversion,
-
+        kToPremul_PMConversion = 0,
+        kToUnpremul_PMConversion,
         kPMConversionCnt
     };
 
@@ -46,9 +40,8 @@
     // 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 void TestForPreservingPMConversions(GrContext* context,
-                                               PMConversion* PMToUPMRule,
-                                               PMConversion* UPMToPMRule);
+    static bool TestForPreservingPMConversions(GrContext* context);
+
 private:
     GrConfigConversionEffect(PMConversion);