Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0589adcd authored by Peiyong Lin's avatar Peiyong Lin
Browse files

[RenderEngine] Add PQ/HLG data space support.

This patch adds support for handling output data space that is PQ or HLG. When
SDR content and HDR content are presented together, the SDR content should be
adjusted through transcoding so that the brightness of SDR content remains the
same.

BUG: 73825729
Test: build, flash
Change-Id: I0b592886ebcfa4d1ad6903c222ec444ac159e024
parent 1fa21edf
Loading
Loading
Loading
Loading
+152 −78
Original line number Diff line number Diff line
@@ -233,31 +233,73 @@ void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) {
}

// Generate OOTF that modifies the relative scence light to relative display light.
void ProgramCache::generateOOTF(Formatter& fs, const Key& needs) {
void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) {
    // When HDR and non-HDR contents are mixed, or different types of HDR contents are
    // mixed, we will do a transcoding process to transcode the input content to output
    // content. Currently, the following conversions handled, they are:
    // * SDR -> HLG
    // * SDR -> PQ
    // * HLG -> PQ

    // Convert relative light to absolute light.
    switch (needs.getInputTF()) {
        case Key::INPUT_TF_ST2084:
            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;
                highp vec3 ScaleLuminance(color) {
                    return color * 10000.0;
                }
            )__SHADER__";
            break;
        case Key::INPUT_TF_HLG:
            fs << R"__SHADER__(
                highp vec3 ScaleLuminance(color) {
                    // The formula is:
                    // alpha * pow(Y, gamma - 1.0) * color + beta;
                    // where alpha is 1000.0, gamma is 1.2, beta is 0.0.
                    return color * 1000.0 * pow(color.y, 0.2);
                }
            )__SHADER__";
            break;
        default:
            fs << R"__SHADER__(
                highp vec3 ScaleLuminance(color) {
                    return color * displayMaxLuminance;
                }
            )__SHADER__";
            break;
    }

    // Generate OOTF that modifies the relative display light.
    // Tone map absolute light to display luminance range.
    switch (needs.getInputTF()) {
        case Key::INPUT_TF_ST2084:
        case Key::INPUT_TF_HLG:
            switch (needs.getOutputTF()) {
                case Key::OUTPUT_TF_HLG:
                    // Right now when mixed PQ and HLG contents are presented,
                    // HLG content will always be converted to PQ. However, for
                    // completeness, we simply clamp the value to [0.0, 1000.0].
                    fs << R"__SHADER__(
                highp vec3 OOTF(const highp vec3 color) {
                    const float maxLumi = 10000.0;
                        highp vec3 ToneMap(color) {
                            return clamp(color, 0.0, 1000.0);
                        }
                    )__SHADER__";
                    break;
                case Key::OUTPUT_TF_ST2084:
                    fs << R"__SHADER__(
                        highp vec3 ToneMap(color) {
                            return color;
                        }
                    )__SHADER__";
                    break;
                default:
                    fs << R"__SHADER__(
                        highp vec3 ToneMap(color) {
                            const float maxMasteringLumi = 1000.0;
                            const float maxContentLumi = 1000.0;
                            const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
                            float maxOutLumi = displayMaxLuminance;

                    // Calculate Y value in XYZ color space.
                    float colorY = CalculateY(color);

                    // convert to nits first
                    float nits = colorY * maxLumi;
                            float nits = color.y;

                            // clamp to max input luminance
                            nits = clamp(nits, 0.0, maxInLumi);
@@ -275,12 +317,12 @@ void ProgramCache::generateOOTF(Formatter& fs, const Key& needs) {
                                float y2 = y1 + (maxOutLumi - y1) * 0.75;

                                // horizontal distances between the last three control points
                        float h12 = x2 - x1;
                        float h23 = maxInLumi - x2;
                                const float h12 = x2 - x1;
                                const float h23 = maxInLumi - x2;
                                // tangents at the last three control points
                        float m1 = (y2 - y1) / h12;
                        float m3 = (maxOutLumi - y2) / h23;
                        float m2 = (m1 + m3) / 2.0;
                                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
@@ -288,7 +330,7 @@ void ProgramCache::generateOOTF(Formatter& fs, const Key& needs) {
                                    nits *= slope;
                                } else if (nits < x1) {
                                    // scale [x0, x1] to [y0, y1] linearly
                            float slope = (y1 - y0) / (x1 - x0);
                                    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
@@ -303,32 +345,64 @@ void ProgramCache::generateOOTF(Formatter& fs, const Key& needs) {
                                }
                            }

                    // convert back to [0.0, 1.0]
                    float targetY = nits / maxOutLumi;
                    return color * (targetY / max(1e-6, colorY));
                            return color * (nits / max(1e-6, color.y));
                        }
                    )__SHADER__";
                    break;
        case Key::INPUT_TF_HLG:
            }
            break;
        default:
            // TODO(73825729) We need to revert the tone mapping in
            // hardware composer properly.
            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;
                highp vec3 ToneMap(color) {
                    return color;
                }
            )__SHADER__";
            break;
    }

    // convert absolute light to relative light.
    switch (needs.getOutputTF()) {
        case Key::OUTPUT_TF_ST2084:
            fs << R"__SHADER__(
                highp vec3 NormalizeLuminance(color) {
                    return color / 10000.0;
                }
            )__SHADER__";
            break;
        case Key::OUTPUT_TF_HLG:
            fs << R"__SHADER__(
                highp vec3 NormalizeLuminance(color) {
                    return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2);
                }
            )__SHADER__";
            break;
        default:
            fs << R"__SHADER__(
                highp vec3 NormalizeLuminance(color) {
                    return color / displayMaxLuminance;
                }
            )__SHADER__";
            break;
    }

    if (needs.getInputTF() == needs.getOutputTF() ||
        (needs.getInputTF() == Key::INPUT_TF_LINEAR &&
         needs.getOutputTF() == Key::OUTPUT_TF_SRGB) ||
        (needs.getInputTF() == Key::INPUT_TF_SRGB &&
         needs.getOutputTF() == Key::OUTPUT_TF_LINEAR)) {
        fs << R"__SHADER__(
            highp vec3 OOTF(const highp vec3 color) {
                return color;
            }
        )__SHADER__";
            break;
    } else {
        fs << R"__SHADER__(
            highp vec3 OOTF(const highp vec3 color) {
                return NormalizeLuminance(ToneMap(ScaleLuminance(color)));
            }
        )__SHADER__";
    }
}