surfaceflinger: generalize Description::mIsWideGamut

Generalize the mIsWideGamut bool into input and output transfer
functions.  In most cases, the transfer functions are LINEAR,
meaning EOTF() and OETF() are no-op.

Test: manual
Change-Id: I18bfa97ffa950190b553a2ebb18f68b72ab296dd
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index 1b5a466..d70083f 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -61,8 +61,12 @@
     return mColorMatrix;
 }
 
-void Description::setWideGamut(bool wideGamut) {
-    mIsWideGamut = wideGamut;
+void Description::setInputTransferFunction(TransferFunction transferFunction) {
+    mInputTransferFunction = transferFunction;
+}
+
+void Description::setOutputTransferFunction(TransferFunction transferFunction) {
+    mOutputTransferFunction = transferFunction;
 }
 
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index 1811952..b0b08fe 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -32,6 +32,27 @@
  * Program and ProgramCache are friends and access the state directly
  */
 class Description {
+public:
+    Description() = default;
+    ~Description() = default;
+
+    void setPremultipliedAlpha(bool premultipliedAlpha);
+    void setOpaque(bool opaque);
+    void setTexture(const Texture& texture);
+    void disableTexture();
+    void setColor(const half4& color);
+    void setProjectionMatrix(const mat4& mtx);
+    void setColorMatrix(const mat4& mtx);
+    const mat4& getColorMatrix() const;
+
+    enum class TransferFunction : int {
+        LINEAR,
+        SRGB,
+    };
+    void setInputTransferFunction(TransferFunction transferFunction);
+    void setOutputTransferFunction(TransferFunction transferFunction);
+
+private:
     friend class Program;
     friend class ProgramCache;
 
@@ -52,21 +73,9 @@
     bool mColorMatrixEnabled = false;
     mat4 mColorMatrix;
 
-    bool mIsWideGamut = false;
-
-public:
-    Description() = default;
-    ~Description() = default;
-
-    void setPremultipliedAlpha(bool premultipliedAlpha);
-    void setOpaque(bool opaque);
-    void setTexture(const Texture& texture);
-    void disableTexture();
-    void setColor(const half4& color);
-    void setProjectionMatrix(const mat4& mtx);
-    void setColorMatrix(const mat4& mtx);
-    const mat4& getColorMatrix() const;
-    void setWideGamut(bool wideGamut);
+    // transfer functions for the input/output
+    TransferFunction mInputTransferFunction = TransferFunction::LINEAR;
+    TransferFunction mOutputTransferFunction = TransferFunction::LINEAR;
 };
 
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index d1ee6f8..745929d 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -130,14 +130,8 @@
         // Compute sRGB to DisplayP3 color transform
         // NOTE: For now, we are limiting wide-color support to
         // Display-P3 only.
-        mat3 srgbToP3 =
-                ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::DisplayP3()).getTransform();
-
-        // color transform needs to be expanded to 4x4 to be what the shader wants
-        // mat has an initializer that expands mat3 to mat4, but
-        // not an assignment operator
-        mat4 gamutTransform(srgbToP3);
-        mSrgbToDisplayP3 = gamutTransform;
+        mSrgbToDisplayP3 = mat4(
+                ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::DisplayP3()).getTransform());
     }
 }
 
@@ -324,10 +318,16 @@
 
     if (usesWideColor()) {
         Description wideColorState = mState;
-        if (mDataSpace != HAL_DATASPACE_DISPLAY_P3) {
-            wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
-            wideColorState.setWideGamut(true);
-            ALOGV("drawMesh: gamut transform applied");
+        switch (mDataSpace) {
+            case HAL_DATASPACE_DISPLAY_P3:
+                // input matches output
+                break;
+            default:
+                wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
+                wideColorState.setInputTransferFunction(Description::TransferFunction::SRGB);
+                wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
+                ALOGV("drawMesh: gamut transform applied");
+                break;
         }
         ProgramCache::getInstance().useProgram(wideColorState);
 
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 3b8ac0e..30d2369 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -126,9 +126,30 @@
             .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)
-            .set(Key::WIDE_GAMUT_MASK,
-                 description.mIsWideGamut ? Key::WIDE_GAMUT_ON : Key::WIDE_GAMUT_OFF);
+                 description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON : Key::COLOR_MATRIX_OFF);
+
+    if (needs.hasColorMatrix()) {
+        switch (description.mInputTransferFunction) {
+            case Description::TransferFunction::LINEAR:
+            default:
+                needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
+                break;
+            case Description::TransferFunction::SRGB:
+                needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB);
+                break;
+        }
+
+        switch (description.mOutputTransferFunction) {
+            case Description::TransferFunction::LINEAR:
+            default:
+                needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
+                break;
+            case Description::TransferFunction::SRGB:
+                needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB);
+                break;
+        }
+    }
+
     return needs;
 }
 
@@ -172,51 +193,61 @@
 
     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));
-                  }
+        switch (needs.getInputTF()) {
+            case Key::INPUT_TF_LINEAR:
+            default:
+                fs << R"__SHADER__(
+                    vec3 EOTF(const vec3 linear) {
+                        return linear;
+                    }
+                )__SHADER__";
+                break;
+            case Key::INPUT_TF_SRGB:
+                fs << R"__SHADER__(
+                    float EOTF_sRGB(float srgb) {
+                        return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+                    }
 
-                  vec3 OETF_scRGB(const vec3 linear) {
-                      return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
-                  }
+                    vec3 EOTF_sRGB(const vec3 srgb) {
+                        return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                    }
 
-                  float EOTF_sRGB(float srgb) {
-                      return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
-                  }
+                    vec3 EOTF(const vec3 srgb) {
+                        return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                    }
+                )__SHADER__";
+                break;
+        }
 
-                  vec3 EOTF_sRGB(const vec3 srgb) {
-                      return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
-                  }
+        switch (needs.getOutputTF()) {
+            case Key::OUTPUT_TF_LINEAR:
+            default:
+                fs << R"__SHADER__(
+                    vec3 OETF(const vec3 linear) {
+                        return linear;
+                    }
+                )__SHADER__";
+                break;
+            case Key::OUTPUT_TF_SRGB:
+                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 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 OETF_sRGB(const vec3 linear) {
+                        return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                    }
 
-                  vec3 EOTF_scRGB(const vec3 srgb) {
-                      return srgb;
-                  }
-            )__SHADER__";
+                    vec3 OETF(const vec3 linear) {
+                        return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                    }
+                )__SHADER__";
+                break;
         }
     }
+
     fs << "void main(void) {" << indent;
     if (needs.isTexturing()) {
         fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
@@ -243,9 +274,9 @@
             // 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(EOTF_scRGB(gl_FragColor.rgb), 1);";
+        fs << "vec4 transformed = colorMatrix * vec4(EOTF(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);";
+        fs << "gl_FragColor.rgb = OETF(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 + 0.0019);";
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/services/surfaceflinger/RenderEngine/ProgramCache.h
index 54d3722..491fad3 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.h
@@ -50,30 +50,41 @@
 
     public:
         enum {
-            BLEND_PREMULT = 0x00000001,
-            BLEND_NORMAL = 0x00000000,
-            BLEND_MASK = 0x00000001,
+            BLEND_SHIFT = 0,
+            BLEND_MASK = 1 << BLEND_SHIFT,
+            BLEND_PREMULT = 1 << BLEND_SHIFT,
+            BLEND_NORMAL = 0 << BLEND_SHIFT,
 
-            OPACITY_OPAQUE = 0x00000002,
-            OPACITY_TRANSLUCENT = 0x00000000,
-            OPACITY_MASK = 0x00000002,
+            OPACITY_SHIFT = 1,
+            OPACITY_MASK = 1 << OPACITY_SHIFT,
+            OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
+            OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
 
-            ALPHA_LT_ONE = 0x00000004,
-            ALPHA_EQ_ONE = 0x00000000,
-            ALPHA_MASK = 0x00000004,
+            ALPHA_SHIFT = 2,
+            ALPHA_MASK = 1 << ALPHA_SHIFT,
+            ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
+            ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
 
-            TEXTURE_OFF = 0x00000000,
-            TEXTURE_EXT = 0x00000008,
-            TEXTURE_2D = 0x00000010,
-            TEXTURE_MASK = 0x00000018,
+            TEXTURE_SHIFT = 3,
+            TEXTURE_MASK = 3 << TEXTURE_SHIFT,
+            TEXTURE_OFF = 0 << TEXTURE_SHIFT,
+            TEXTURE_EXT = 1 << TEXTURE_SHIFT,
+            TEXTURE_2D = 2 << TEXTURE_SHIFT,
 
-            COLOR_MATRIX_OFF = 0x00000000,
-            COLOR_MATRIX_ON = 0x00000020,
-            COLOR_MATRIX_MASK = 0x00000020,
+            COLOR_MATRIX_SHIFT = 5,
+            COLOR_MATRIX_MASK = 1 << COLOR_MATRIX_SHIFT,
+            COLOR_MATRIX_OFF = 0 << COLOR_MATRIX_SHIFT,
+            COLOR_MATRIX_ON = 1 << COLOR_MATRIX_SHIFT,
 
-            WIDE_GAMUT_OFF = 0x00000000,
-            WIDE_GAMUT_ON = 0x00000040,
-            WIDE_GAMUT_MASK = 0x00000040,
+            INPUT_TF_SHIFT = 6,
+            INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
+            INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
+            INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
+
+            OUTPUT_TF_SHIFT = 8,
+            OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
+            OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
+            OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
         };
 
         inline Key() : mKey(0) {}
@@ -90,7 +101,8 @@
         inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
         inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
         inline bool hasColorMatrix() const { return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON; }
-        inline bool isWideGamut() const { return (mKey & WIDE_GAMUT_MASK) == WIDE_GAMUT_ON; }
+        inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
+        inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
 
         // this is the definition of a friend function -- not a method of class Needs
         friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {