Apply transfer function when rendering with linear textures

RGBA16F bitmaps are always encoded in linear space, which means we must
apply the opto-electronic transfer function before we can render them
in the framebuffer.

Since our linear bitmaps are assumed to be scRGB, values can be negative.
The OETF is a slightly modified sRGB OETF:

sign(x) * OETF_sRGB(abs(x))

This effectively mirrors the OETF over the negative domain.

This CL also removes the "optimized" shader generation path. With
current compilers, the optimized path doesn't do anything of value
and makes ProgramCache difficult to maintain. Shader compilers inline
everything and are really good at folding expressions and removing
unused code.

Bug: 32984164
Test: CtsUiRenderingTestCases
Change-Id: Ieb458ad53574e3a8959aa6bccbbd2d1fe203cbc5
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 42ef762..00de992 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -161,9 +161,35 @@
         "uniform vec4 roundRectInnerRectLTRB;\n"
         "uniform float roundRectRadius;\n";
 
+const char* gFS_OETF[2] = {
+         "\nvec4 OETF(const vec4 linear) {\n"
+         "    return linear;\n"
+         "}\n",
+          // We expect linear data to be scRGB so we mirror the gamma function
+         "\nvec4 OETF(const vec4 linear) {"
+          "    return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);\n"
+          "}\n",
+};
+
+const char* gFS_Transfer_Functions = R"__SHADER__(
+        float OETF_sRGB(const float linear) {
+            // IEC 61966-2-1:1999
+            return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+        }
+
+        vec3 OETF_sRGB(const vec3 linear) {
+            return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+        }
+
+        float EOTF_sRGB(float srgb) {
+            // IEC 61966-2-1:1999
+            return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+        }
+)__SHADER__";
+
 // Dithering must be done in the quantization space
 // When we are writing to an sRGB framebuffer, we must do the following:
-//     EOCF(OECF(color) + dither)
+//     EOTF(OETF(color) + dither)
 // The dithering pattern is generated with a triangle noise generator in the range [-0.0,1.0]
 // TODO: Handle linear fp16 render targets
 const char* gFS_Gradient_Functions = R"__SHADER__(
@@ -173,20 +199,6 @@
             highp float xy = p.x * p.y;
             return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
         }
-
-        float OECF_sRGB(const float linear) {
-            // IEC 61966-2-1:1999
-            return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
-        }
-
-        vec3 OECF_sRGB(const vec3 linear) {
-            return vec3(OECF_sRGB(linear.r), OECF_sRGB(linear.g), OECF_sRGB(linear.b));
-        }
-
-        float EOCF_sRGB(float srgb) {
-            // IEC 61966-2-1:1999
-            return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
-        }
 )__SHADER__";
 const char* gFS_Gradient_Preamble[2] = {
         // Linear framebuffer
@@ -195,8 +207,8 @@
         "}\n"
         "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
         "    vec4 c = mix(a, b, v);\n"
-        "    c.a = EOCF_sRGB(c.a);\n" // This is technically incorrect but preserves compatibility
-        "    return vec4(OECF_sRGB(c.rgb) * c.a, c.a);\n"
+        "    c.a = EOTF_sRGB(c.a);\n" // This is technically incorrect but preserves compatibility
+        "    return vec4(OETF_sRGB(c.rgb) * c.a, c.a);\n"
         "}\n",
         // sRGB framebuffer
         "\nvec4 dither(const vec4 color) {\n"
@@ -232,52 +244,6 @@
 const char* gFS_Main_AddDither =
         "    fragColor = dither(fragColor);\n";
 
-// Fast cases
-const char* gFS_Fast_SingleColor =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color;\n"
-        "}\n\n";
-const char* gFS_Fast_SingleTexture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleModulateTexture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color.a * texture2D(baseSampler, outTexCoords);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleA8Texture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleA8Texture_ApplyGamma =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, GAMMA));\n"
-        "}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n"
-        "}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleGradient[2] = {
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(texture2D(gradientSampler, linear));\n"
-        "}\n\n",
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
-        "}\n\n",
-};
-const char* gFS_Fast_SingleModulateGradient[2] = {
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(color.a * texture2D(gradientSampler, linear));\n"
-        "}\n\n",
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(color.a * gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
-        "}\n\n"
-};
-
 // General case
 const char* gFS_Main_FetchColor =
         "    fragColor = color;\n";
@@ -290,7 +256,7 @@
         "    fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
 const char* gFS_Main_FetchTexture[2] = {
         // Don't modulate
-        "    fragColor = texture2D(baseSampler, outTexCoords);\n",
+        "    fragColor = OETF(texture2D(baseSampler, outTexCoords));\n",
         // Modulate
         "    fragColor = color * texture2D(baseSampler, outTexCoords);\n"
 };
@@ -321,9 +287,9 @@
         "    vec4 gradientColor = gammaMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
 };
 const char* gFS_Main_FetchBitmap =
-        "    vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
+        "    vec4 bitmapColor = OETF(texture2D(bitmapSampler, outBitmapTexCoords));\n";
 const char* gFS_Main_FetchBitmapNpot =
-        "    vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n";
+        "    vec4 bitmapColor = OETF(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
 const char* gFS_Main_BlendShadersBG =
         "    fragColor = blendShaders(gradientColor, bitmapColor)";
 const char* gFS_Main_BlendShadersGB =
@@ -649,71 +615,6 @@
         shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma);
     }
 
-    // Optimization for common cases
-    if (!description.hasVertexAlpha
-            && !blendFramebuffer
-            && !description.hasColors
-            && description.colorOp == ProgramDescription::ColorFilterMode::None
-            && !description.hasDebugHighlight
-            && !description.hasRoundRectClip) {
-        bool fast = false;
-
-        const bool noShader = !description.hasGradient && !description.hasBitmap;
-        const bool singleTexture = (description.hasTexture || description.hasExternalTexture) &&
-                !description.hasAlpha8Texture && noShader;
-        const bool singleA8Texture = description.hasTexture &&
-                description.hasAlpha8Texture && noShader;
-        const bool singleGradient = !description.hasTexture && !description.hasExternalTexture &&
-                description.hasGradient && !description.hasBitmap &&
-                description.gradientType == ProgramDescription::kGradientLinear;
-
-        if (singleColor) {
-            shader.append(gFS_Fast_SingleColor);
-            fast = true;
-        } else if (singleTexture) {
-            if (!description.modulate) {
-                shader.append(gFS_Fast_SingleTexture);
-            } else {
-                shader.append(gFS_Fast_SingleModulateTexture);
-            }
-            fast = true;
-        } else if (singleA8Texture) {
-            if (!description.modulate) {
-                if (description.hasGammaCorrection) {
-                    shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
-                } else {
-                    shader.append(gFS_Fast_SingleA8Texture);
-                }
-            } else {
-                if (description.hasGammaCorrection) {
-                    shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
-                } else {
-                    shader.append(gFS_Fast_SingleModulateA8Texture);
-                }
-            }
-            fast = true;
-        } else if (singleGradient) {
-            shader.append(gFS_Gradient_Functions);
-            shader.append(gFS_Gradient_Preamble[mHasSRGB]);
-            if (!description.modulate) {
-                shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
-            } else {
-                shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
-            }
-            fast = true;
-        }
-
-        if (fast) {
-#if DEBUG_PROGRAMS
-                PROGRAM_LOGD("*** Fast case:\n");
-                PROGRAM_LOGD("*** Generated fragment shader:\n\n");
-                printLongString(shader);
-#endif
-
-            return shader;
-        }
-    }
-
     if (description.hasBitmap) {
         if (description.isShaderBitmapExternal) {
             shader.append(gFS_Uniforms_BitmapExternalSampler);
@@ -736,6 +637,13 @@
     if (description.useShaderBasedWrap) {
         generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
     }
+    if (description.hasGradient || description.hasLinearTexture) {
+        shader.append(gFS_Transfer_Functions);
+    }
+    if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) &&
+            !description.hasAlpha8Texture)) {
+        shader.append(gFS_OETF[description.hasLinearTexture && !mHasSRGB]);
+    }
     if (description.hasGradient) {
         shader.append(gFS_Gradient_Functions);
         shader.append(gFS_Gradient_Preamble[mHasSRGB]);
@@ -827,10 +735,10 @@
     // End the shader
     shader.append(gFS_Footer);
 
-#if DEBUG_PROGRAMS
+//#if DEBUG_PROGRAMS
         PROGRAM_LOGD("*** Generated fragment shader:\n\n");
         printLongString(shader);
-#endif
+//#endif
 
     return shader;
 }