[RenderEngine] Add hybrid Log-Gamma support.

Previously we already added support for BT2020 PQ, this patch adds BT2020 HLG
transfer function to render engine.

BUG: 73825729
Test: Build and flash
Change-Id: I70d974b3f8fb43c8de4c527f328dbb2f8725e8ec
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index 5e79e7a..323bdb2 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -73,8 +73,4 @@
     mOutputTransferFunction = transferFunction;
 }
 
-void Description::enableToneMapping(bool enable) {
-    mToneMappingEnabled = enable;
-}
-
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index 75c1981..5854ba4 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -51,12 +51,11 @@
         LINEAR,
         SRGB,
         ST2084,
+        HLG,  // Hybrid Log-Gamma for HDR.
     };
     void setInputTransferFunction(TransferFunction transferFunction);
     void setOutputTransferFunction(TransferFunction transferFunction);
 
-    void enableToneMapping(bool enable);
-
 private:
     friend class Program;
     friend class ProgramCache;
@@ -84,9 +83,6 @@
     // transfer functions for the input/output
     TransferFunction mInputTransferFunction = TransferFunction::LINEAR;
     TransferFunction mOutputTransferFunction = TransferFunction::LINEAR;
-
-    // tone-map the color
-    bool mToneMappingEnabled = false;
 };
 
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 1efe0ac..6e0fa32 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -316,7 +316,12 @@
                 wideColorState.setColorMatrix(mState.getColorMatrix() * mBt2020ToDisplayP3);
                 wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084);
                 wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
-                wideColorState.enableToneMapping(true);
+                break;
+            case Dataspace::BT2020_HLG:
+            case Dataspace::BT2020_ITU_HLG:
+                wideColorState.setColorMatrix(mState.getColorMatrix() * mBt2020ToDisplayP3);
+                wideColorState.setInputTransferFunction(Description::TransferFunction::HLG);
+                wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
                 break;
             default:
                 // treat all other dataspaces as sRGB
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 6a34981..d1887ee 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -143,6 +143,9 @@
             case Description::TransferFunction::ST2084:
                 needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084);
                 break;
+            case Description::TransferFunction::HLG:
+                needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG);
+                break;
         }
 
         switch (description.mOutputTransferFunction) {
@@ -156,10 +159,10 @@
             case Description::TransferFunction::ST2084:
                 needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084);
                 break;
+            case Description::TransferFunction::HLG:
+                needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG);
+                break;
         }
-
-        needs.set(Key::TONE_MAPPING_MASK,
-                  description.mToneMappingEnabled ? Key::TONE_MAPPING_ON : Key::TONE_MAPPING_OFF);
     }
 
     return needs;
@@ -220,6 +223,8 @@
     if (needs.hasColorMatrix()) {
         fs << "uniform mat4 colorMatrix;";
 
+        // Generate EOTF that converts signal values to relative display light,
+        // both normalized to [0, 1].
         switch (needs.getInputTF()) {
             case Key::INPUT_TF_LINEAR:
             default:
@@ -259,8 +264,123 @@
                     }
                     )__SHADER__";
                 break;
+          case Key::INPUT_TF_HLG:
+              fs << R"__SHADER__(
+                  highp float EOTF_channel(const highp float channel) {
+                      const highp float a = 0.17883277;
+                      const highp float b = 0.28466892;
+                      const highp float c = 0.55991073;
+                      return channel <= 0.5 ? channel * channel / 3.0 :
+                              (exp((channel - c) / a) + b) / 12.0;
+                  }
+
+                  vec3 EOTF(const highp vec3 color) {
+                      return vec3(EOTF_channel(color.r), EOTF_channel(color.g),
+                              EOTF_channel(color.b));
+                  }
+                  )__SHADER__";
+              break;
         }
 
+        fs << R"__SHADER__(
+            highp float CalculateY(const highp vec3 color) {
+                // BT2020 standard uses the unadjusted KR = 0.2627,
+                // KB = 0.0593 luminance interpretation for RGB conversion.
+                return color.r * 0.262700 + color.g * 0.677998 +
+                        color.b * 0.059302;
+            }
+        )__SHADER__";
+
+        // Generate OOTF that modifies the relative display light.
+        switch(needs.getInputTF()) {
+            case Key::INPUT_TF_ST2084:
+                fs << R"__SHADER__(
+                    highp vec3 OOTF(const highp vec3 color) {
+                        const float maxLumi = 10000.0;
+                        const float maxMasteringLumi = 1000.0;
+                        const float maxContentLumi = 1000.0;
+                        const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+                        const float maxOutLumi = 500.0;
+
+                        // Calculate Y value in XYZ color space.
+                        float colorY = CalculateY(color);
+
+                        // convert to nits first
+                        float nits = colorY * maxLumi;
+
+                        // clamp to max input luminance
+                        nits = clamp(nits, 0.0, maxInLumi);
+
+                        // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+                        if (maxInLumi <= maxOutLumi) {
+                            nits *= maxOutLumi / maxInLumi;
+                        } else {
+                            // three control points
+                            const float x0 = 10.0;
+                            const float y0 = 17.0;
+                            const float x1 = maxOutLumi * 0.75;
+                            const float y1 = x1;
+                            const float x2 = x1 + (maxInLumi - x1) / 2.0;
+                            const float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+                            // horizontal distances between the last three control points
+                            const float h12 = x2 - x1;
+                            const float h23 = maxInLumi - x2;
+                            // tangents at the last three control points
+                            const float m1 = (y2 - y1) / h12;
+                            const float m3 = (maxOutLumi - y2) / h23;
+                            const float m2 = (m1 + m3) / 2.0;
+
+                            if (nits < x0) {
+                                // scale [0.0, x0] to [0.0, y0] linearly
+                                const float slope = y0 / x0;
+                                nits *= slope;
+                            } else if (nits < x1) {
+                                // scale [x0, x1] to [y0, y1] linearly
+                                const float slope = (y1 - y0) / (x1 - x0);
+                                nits = y0 + (nits - x0) * slope;
+                            } else if (nits < x2) {
+                                // scale [x1, x2] to [y1, y2] using Hermite interp
+                                float t = (nits - x1) / h12;
+                                nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+                                       (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+                            } else {
+                                // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+                                float t = (nits - x2) / h23;
+                                nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+                                       (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+                            }
+                        }
+
+                        // convert back to [0.0, 1.0]
+                        float targetY = nits / maxOutLumi;
+                        return color * (targetY / max(1e-6, colorY));
+                    }
+                )__SHADER__";
+                break;
+            case Key::INPUT_TF_HLG:
+                fs << R"__SHADER__(
+                    highp vec3 OOTF(const highp vec3 color) {
+                        const float maxOutLumi = 500.0;
+                        const float gamma = 1.2 + 0.42 * log(maxOutLumi / 1000.0) / log(10.0);
+                        // The formula is:
+                        // alpha * pow(Y, gamma - 1.0) * color + beta;
+                        // where alpha is 1.0, beta is 0.0 as recommended in
+                        // Rec. ITU-R BT.2100-1 TABLE 5.
+                        return pow(CalculateY(color), gamma - 1.0) * color;
+                    }
+                )__SHADER__";
+                break;
+            default:
+                fs << R"__SHADER__(
+                    highp vec3 OOTF(const highp vec3 color) {
+                        return color;
+                    }
+                )__SHADER__";
+        }
+
+        // Generate OETF that converts relative display light to signal values,
+        // both normalized to [0, 1]
         switch (needs.getOutputTF()) {
             case Key::OUTPUT_TF_LINEAR:
             default:
@@ -301,84 +421,22 @@
                     }
                 )__SHADER__";
                 break;
-        }
-
-        if (needs.hasToneMapping()) {
-            fs << R"__SHADER__(
-                float CalculateY(const vec3 color) {
-                    // BT2020 standard uses the unadjusted KR = 0.2627,
-                    // KB = 0.0593 luminance interpretation for RGB conversion.
-                    return color.r * 0.262700 + color.g * 0.677998 +
-                            color.b * 0.059302;
-                }
-                vec3 ToneMap(const vec3 color) {
-                    const float maxLumi = 10000.0;
-                    const float maxMasteringLumi = 1000.0;
-                    const float maxContentLumi = 1000.0;
-                    const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
-                    const float maxOutLumi = 500.0;
-
-                    // Calculate Y value in XYZ color space.
-                    float colorY = CalculateY(color);
-
-                    // convert to nits first
-                    float nits = colorY * maxLumi;
-
-                    // clamp to max input luminance
-                    nits = clamp(nits, 0.0, maxInLumi);
-
-                    // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
-                    if (maxInLumi <= maxOutLumi) {
-                        nits *= maxOutLumi / maxInLumi;
-                    } else {
-                        // three control points
-                        const float x0 = 10.0;
-                        const float y0 = 17.0;
-                        const float x1 = maxOutLumi * 0.75;
-                        const float y1 = x1;
-                        const float x2 = x1 + (maxInLumi - x1) / 2.0;
-                        const float y2 = y1 + (maxOutLumi - y1) * 0.75;
-
-                        // horizontal distances between the last three control points
-                        const float h12 = x2 - x1;
-                        const float h23 = maxInLumi - x2;
-                        // tangents at the last three control points
-                        const float m1 = (y2 - y1) / h12;
-                        const float m3 = (maxOutLumi - y2) / h23;
-                        const float m2 = (m1 + m3) / 2.0;
-
-                        if (nits < x0) {
-                            // scale [0.0, x0] to [0.0, y0] linearly
-                            const float slope = y0 / x0;
-                            nits *= slope;
-                        } else if (nits < x1) {
-                            // scale [x0, x1] to [y0, y1] linearly
-                            const float slope = (y1 - y0) / (x1 - x0);
-                            nits = y0 + (nits - x0) * slope;
-                        } else if (nits < x2) {
-                            // scale [x1, x2] to [y1, y2] using Hermite interp
-                            float t = (nits - x1) / h12;
-                            nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
-                                   (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
-                        } else {
-                            // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
-                            float t = (nits - x2) / h23;
-                            nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
-                                   (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
-                        }
+            case Key::OUTPUT_TF_HLG:
+                fs << R"__SHADER__(
+                    highp float OETF_channel(const highp float channel) {
+                        const highp float a = 0.17883277;
+                        const highp float b = 0.28466892;
+                        const highp float c = 0.55991073;
+                        return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+                                a * log(12.0 * channel - b) + c;
                     }
 
-                    // convert back to [0.0, 1.0]
-                    float targetY = nits / maxOutLumi;
-                    return color * (targetY / max(1e-6, colorY));
-                }
-            )__SHADER__";
-        } else {
-            fs << R"__SHADER__(
-                vec3 ToneMap(const vec3 color) {
-                    return color;
-                }
-            )__SHADER__";
+                    vec3 OETF(const highp vec3 color) {
+                        return vec3(OETF_channel(color.r), OETF_channel(color.g),
+                                OETF_channel(color.b));
+                    }
+                )__SHADER__";
+                break;
         }
     }
 
@@ -411,7 +469,7 @@
             // 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(ToneMap(EOTF(gl_FragColor.rgb)), 1);";
+        fs << "vec4 transformed = colorMatrix * vec4(OOTF(EOTF(gl_FragColor.rgb)), 1);";
         // the transformation from a wider colorspace to a narrower one can
         // result in >1.0 or <0.0 pixel values
         fs << "transformed.rgb = clamp(transformed.rgb, 0.0, 1.0);";
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/services/surfaceflinger/RenderEngine/ProgramCache.h
index dcc8cc6..f67e132 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.h
@@ -81,19 +81,16 @@
             INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
             INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
             INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
+            INPUT_TF_HLG = 3 << 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,
             OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
+            OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
 
-            TONE_MAPPING_SHIFT = 10,
-            TONE_MAPPING_MASK = 1 << TONE_MAPPING_SHIFT,
-            TONE_MAPPING_OFF = 0 << TONE_MAPPING_SHIFT,
-            TONE_MAPPING_ON = 1 << TONE_MAPPING_SHIFT,
-
-            Y410_BT2020_SHIFT = 11,
+            Y410_BT2020_SHIFT = 10,
             Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
             Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
             Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
@@ -115,7 +112,6 @@
         inline bool hasColorMatrix() const { return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON; }
         inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
         inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
-        inline bool hasToneMapping() const { return (mKey & TONE_MAPPING_MASK) == TONE_MAPPING_ON; }
         inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
 
         // this is the definition of a friend function -- not a method of class Needs