PM/UPM conversion improvements
authorBrian Osman <brianosman@google.com>
Mon, 10 Apr 2017 17:42:48 +0000 (13:42 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 10 Apr 2017 19:23:45 +0000 (19:23 +0000)
Snap input values to N / 255 before doing PM/UPM conversions, and add
a new round-to-nearest conversion pair. With the pre-snapping, that
pair is chosen by (almost?) every GPU we have. This now lets us round
trip perfectly on Mali GPUs (which were falling back to SW before).

Bug: skia:
Change-Id: I0cebf1382e1c829aedfef4ec1614d7fa980057b5
Reviewed-on: https://skia-review.googlesource.com/11520
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>

src/gpu/effects/GrConfigConversionEffect.cpp
src/gpu/effects/GrConfigConversionEffect.h

index 07225f1..566a1af 100644 (file)
@@ -27,7 +27,11 @@ public:
             // could optimize this case, but we aren't for now.
             args.fInputColor = "vec4(1)";
         }
-        fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
+
+        // 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);
 
         switch (cce.pmConversion()) {
             case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
@@ -42,6 +46,11 @@ public:
                 fragBuilder->codeAppend(
                     "color.rgb = floor(color.rgb * color.a * 255.0 + 0.001) / 255.0;");
                 break;
+            case GrConfigConversionEffect::kMulByAlpha_RoundNearest_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;");
@@ -50,6 +59,11 @@ public:
                 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:
+                fragBuilder->codeAppend(
+                    "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0 + 0.5) / 255.0;");
+                break;
+
             default:
                 SkFAIL("Unknown conversion op.");
                 break;
@@ -155,13 +169,15 @@ void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context
     }
 
     static const PMConversion kConversionRules[][2] = {
+        {kDivByAlpha_RoundNearest_PMConversion, kMulByAlpha_RoundNearest_PMConversion},
         {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
         {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
     };
 
-    bool failed = true;
+    uint32_t bestFailCount = 0xFFFFFFFF;
+    size_t bestRule = 0;
 
-    for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && failed; ++i) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && bestFailCount; ++i) {
         *pmToUPMRule = kConversionRules[i][0];
         *upmToPMRule = kConversionRules[i][1];
 
@@ -211,19 +227,27 @@ void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context
             continue;
         }
 
-        failed = false;
-        for (int y = 0; y < kSize && !failed; ++y) {
+        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]) {
-                    failed = true;
-                    break;
+                    if (++failCount >= bestFailCount) {
+                        break;
+                    }
                 }
             }
         }
+        if (failCount < bestFailCount) {
+            bestFailCount = failCount;
+            bestRule = i;
+        }
     }
-    if (failed) {
+    if (bestFailCount > 0) {
         *pmToUPMRule = kPMConversionCnt;
         *upmToPMRule = kPMConversionCnt;
+    } else {
+        *pmToUPMRule = kConversionRules[bestRule][0];
+        *upmToPMRule = kConversionRules[bestRule][1];
     }
 }
 
index e5fa10c..4cc2aeb 100644 (file)
@@ -22,8 +22,11 @@ public:
     enum PMConversion {
         kMulByAlpha_RoundUp_PMConversion = 0,
         kMulByAlpha_RoundDown_PMConversion,
+        kMulByAlpha_RoundNearest_PMConversion,
+
         kDivByAlpha_RoundUp_PMConversion,
         kDivByAlpha_RoundDown_PMConversion,
+        kDivByAlpha_RoundNearest_PMConversion,
 
         kPMConversionCnt
     };