Various fixes for wide color gamut rendering

This CL addresses multiple issues:
- A logic issue where the wide gamut color mode was not set at the right time
- The presence of scRGB surfaces was not detected
- The sRGB to Display P3 matrix was transposed
- The color matrix was applied in gamma space instead of linear space*
- The GPU code path was doing a division by w for each pixel when a
  color transform is set, which shouldn't be necessary (the code now
  checks that the matrix has the expected form)
- Incorrect comment
- Dead code in Description.cpp (mDirtyUniforms was never used)
- Screenshots were taken using the last render engine configuration,
  the configuration is now properly set every time

* This can in theory cause a discrepancy when we switch to/from hardware
  composer mode. I was not able to create a visible discrepancy in practice
  using Night Light, color blindness compensation modes and color inversion.
  More importantly, how/where the hardware composer applies the color
  transform is not specified: it could be gamma or linear space, before or
  after the hardware color LUT. We'll address this in a future CL if needed.
  In addition, this code assumes that fp16 surfaces are encoded in non-linear
  space (scRGB-nl instead of scRGB) but we do not have EGL/Vulkan extensions
  to specify this behavior. We need to address this as well

This CL also fixes potential divides by 0 in the GPU code path.

Bug: 29940137
Test: CtsUiRenderingTestsCases, CtsGraphicsTestCases

Change-Id: I9ae15850f8b9d48c39ebc2724ca3da202be9b008
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index ba11259..06b2252 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -129,7 +129,9 @@
     .set(Key::OPACITY_MASK,
             description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
     .set(Key::COLOR_MATRIX_MASK,
-            description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON :  Key::COLOR_MATRIX_OFF);
+            description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON :  Key::COLOR_MATRIX_OFF)
+    .set(Key::WIDE_GAMUT_MASK,
+            description.mIsWideGamut ? Key::WIDE_GAMUT_ON : Key::WIDE_GAMUT_OFF);
     return needs;
 }
 
@@ -175,6 +177,50 @@
     if (needs.hasColorMatrix()) {
         fs << "uniform mat4 colorMatrix;";
     }
+    if (needs.hasColorMatrix()) {
+        // When in wide gamut mode, the color matrix will contain a color space
+        // conversion matrix that needs to be applied in linear space
+        // When not in wide gamut, we can simply no-op the transfer functions
+        // and let the shader compiler get rid of them
+        if (needs.isWideGamut()) {
+            fs << R"__SHADER__(
+                  float OETF_sRGB(const float linear) {
+                      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));
+                  }
+
+                  vec3 OETF_scRGB(const vec3 linear) {
+                      return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                  }
+
+                  float EOTF_sRGB(float srgb) {
+                      return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+                  }
+
+                  vec3 EOTF_sRGB(const vec3 srgb) {
+                      return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                  }
+
+                  vec3 EOTF_scRGB(const vec3 srgb) {
+                      return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                  }
+            )__SHADER__";
+        } else {
+            fs << R"__SHADER__(
+                  vec3 OETF_scRGB(const vec3 linear) {
+                      return linear;
+                  }
+
+                  vec3 EOTF_scRGB(const vec3 srgb) {
+                      return srgb;
+                  }
+            )__SHADER__";
+        }
+    }
     fs << "void main(void) {" << indent;
     if (needs.isTexturing()) {
         fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
@@ -197,13 +243,15 @@
     if (needs.hasColorMatrix()) {
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // un-premultiply if needed before linearization
-            fs << "gl_FragColor.rgb = gl_FragColor.rgb/gl_FragColor.a;";
+            // avoid divide by 0 by adding 0.5/256 to the alpha channel
+            fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
         }
-        fs << "vec4 transformed = colorMatrix * vec4(gl_FragColor.rgb, 1);";
-        fs << "gl_FragColor.rgb = transformed.rgb/transformed.a;";
+        fs << "vec4 transformed = colorMatrix * vec4(EOTF_scRGB(gl_FragColor.rgb), 1);";
+        // We assume the last row is always {0,0,0,1} and we skip the division by w
+        fs << "gl_FragColor.rgb = OETF_scRGB(transformed.rgb);";
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // and re-premultiply if needed after gamma correction
-            fs << "gl_FragColor.rgb = gl_FragColor.rgb*gl_FragColor.a;";
+            fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
         }
     }