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

Commit da2a3aa2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add libtonemap library"

parents 3704e31f 465b2967
Loading
Loading
Loading
Loading
+5 −1
Original line number Original line Diff line number Diff line
@@ -40,6 +40,10 @@ cc_defaults {
        "libui",
        "libui",
        "libutils",
        "libutils",
    ],
    ],

    static_libs: [
        "libtonemap",
    ],
    local_include_dirs: ["include"],
    local_include_dirs: ["include"],
    export_include_dirs: ["include"],
    export_include_dirs: ["include"],
}
}
@@ -97,7 +101,7 @@ filegroup {
        "skia/filters/GaussianBlurFilter.cpp",
        "skia/filters/GaussianBlurFilter.cpp",
        "skia/filters/KawaseBlurFilter.cpp",
        "skia/filters/KawaseBlurFilter.cpp",
        "skia/filters/LinearEffect.cpp",
        "skia/filters/LinearEffect.cpp",
        "skia/filters/StretchShaderFactory.cpp"
        "skia/filters/StretchShaderFactory.cpp",
    ],
    ],
}
}


+6 −2
Original line number Original line Diff line number Diff line
@@ -23,7 +23,10 @@ package {


cc_benchmark {
cc_benchmark {
    name: "librenderengine_bench",
    name: "librenderengine_bench",
    defaults: ["skia_deps", "surfaceflinger_defaults"],
    defaults: [
        "skia_deps",
        "surfaceflinger_defaults",
    ],
    srcs: [
    srcs: [
        "main.cpp",
        "main.cpp",
        "Codec.cpp",
        "Codec.cpp",
@@ -32,6 +35,7 @@ cc_benchmark {
    ],
    ],
    static_libs: [
    static_libs: [
        "librenderengine",
        "librenderengine",
        "libtonemap",
    ],
    ],
    cflags: [
    cflags: [
        "-DLOG_TAG=\"RenderEngineBench\"",
        "-DLOG_TAG=\"RenderEngineBench\"",
+36 −162
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#define ATRACE_TAG ATRACE_TAG_GRAPHICS


#include <SkString.h>
#include <SkString.h>
#include <tonemap/tonemap.h>
#include <utils/Trace.h>
#include <utils/Trace.h>


#include <optional>
#include <optional>
@@ -32,6 +33,11 @@ namespace android {
namespace renderengine {
namespace renderengine {
namespace skia {
namespace skia {


static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(
        ui::Dataspace dataspace) {
    return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
}

static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
        case HAL_DATASPACE_TRANSFER_ST2084:
        case HAL_DATASPACE_TRANSFER_ST2084:
@@ -127,156 +133,10 @@ static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkStrin
        default:
        default:
            shader.append(R"(
            shader.append(R"(
                    float3 ScaleLuminance(float3 xyz) {
                    float3 ScaleLuminance(float3 xyz) {
                        return xyz * in_inputMaxLuminance;
                        return xyz * in_libtonemap_inputMaxLuminance;
                    }
                )");
            break;
    }
}

static void generateToneMapInterpolation(ui::Dataspace inputDataspace,
                                         ui::Dataspace outputDataspace, SkString& shader) {
    switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
        case HAL_DATASPACE_TRANSFER_ST2084:
        case HAL_DATASPACE_TRANSFER_HLG:
            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
                case HAL_DATASPACE_TRANSFER_ST2084:
                    shader.append(R"(
                            float3 ToneMap(float3 xyz) {
                                return xyz;
                            }
                        )");
                    break;
                case HAL_DATASPACE_TRANSFER_HLG:
                    // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
                    // we'll clamp the luminance range in case we're mapping from PQ input to HLG
                    // output.
                    shader.append(R"(
                            float3 ToneMap(float3 xyz) {
                                return clamp(xyz, 0.0, 1000.0);
                            }
                        )");
                    break;
                default:
                    // Here we're mapping from HDR to SDR content, so interpolate using a Hermitian
                    // polynomial onto the smaller luminance range.
                    shader.append(R"(
                            float3 ToneMap(float3 xyz) {
                                float maxInLumi = in_inputMaxLuminance;
                                float maxOutLumi = in_displayMaxLuminance;

                                float nits = xyz.y;

                                // if the max input luminance is less than what we can output then
                                // no tone mapping is needed as all color values will be in range.
                                if (maxInLumi <= maxOutLumi) {
                                    return xyz;
                                } else {

                                    // three control points
                                    const float x0 = 10.0;
                                    const float y0 = 17.0;
                                    float x1 = maxOutLumi * 0.75;
                                    float y1 = x1;
                                    float x2 = x1 + (maxInLumi - x1) / 2.0;
                                    float y2 = y1 + (maxOutLumi - y1) * 0.75;

                                    // horizontal distances between the last three control points
                                    float h12 = x2 - x1;
                                    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;

                                    if (nits < x0) {
                                        // scale [0.0, x0] to [0.0, y0] linearly
                                        float slope = y0 / x0;
                                        return xyz * slope;
                                    } else if (nits < x1) {
                                        // scale [x0, x1] to [y0, y1] linearly
                                        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;
                                    }
                                }

                                // color.y is greater than x0 and is thus non-zero
                                return xyz * (nits / xyz.y);
                            }
                        )");
                    break;
            }
            break;
        default:
            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
                case HAL_DATASPACE_TRANSFER_ST2084:
                case HAL_DATASPACE_TRANSFER_HLG:
                    // Map from SDR onto an HDR output buffer
                    // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
                    // [0, maxOutLumi] which is hard-coded to be 3000 nits.
                    shader.append(R"(
                            float3 ToneMap(float3 xyz) {
                                const float maxOutLumi = 3000.0;

                                const float x0 = 5.0;
                                const float y0 = 2.5;
                                float x1 = in_displayMaxLuminance * 0.7;
                                float y1 = maxOutLumi * 0.15;
                                float x2 = in_displayMaxLuminance * 0.9;
                                float y2 = maxOutLumi * 0.45;
                                float x3 = in_displayMaxLuminance;
                                float y3 = maxOutLumi;

                                float c1 = y1 / 3.0;
                                float c2 = y2 / 2.0;
                                float c3 = y3 / 1.5;

                                float nits = xyz.y;

                                if (nits <= x0) {
                                    // scale [0.0, x0] to [0.0, y0] linearly
                                    float slope = y0 / x0;
                                    return xyz * slope;
                                } else if (nits <= x1) {
                                    // scale [x0, x1] to [y0, y1] using a curve
                                    float t = (nits - x0) / (x1 - x0);
                                    nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
                                } else if (nits <= x2) {
                                    // scale [x1, x2] to [y1, y2] using a curve
                                    float t = (nits - x1) / (x2 - x1);
                                    nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
                                } else {
                                    // scale [x2, x3] to [y2, y3] using a curve
                                    float t = (nits - x2) / (x3 - x2);
                                    nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
                                }

                                // xyz.y is greater than x0 and is thus non-zero
                                return xyz * (nits / xyz.y);
                    }
                    }
                )");
                )");
            break;
            break;
                default:
                    // For completeness, this is tone-mapping from SDR to SDR, where this is just a
                    // no-op.
                    shader.append(R"(
                            float3 ToneMap(float3 xyz) {
                                return xyz;
                            }
                        )");
                    break;
            }
            break;
    }
    }
}
}


@@ -300,7 +160,7 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
        default:
        default:
            shader.append(R"(
            shader.append(R"(
                    float3 NormalizeLuminance(float3 xyz) {
                    float3 NormalizeLuminance(float3 xyz) {
                        return xyz / in_displayMaxLuminance;
                        return xyz / in_libtonemap_displayMaxLuminance;
                    }
                    }
                )");
                )");
            break;
            break;
@@ -309,19 +169,22 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,


static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
                         SkString& shader) {
                         SkString& shader) {
    // Input uniforms
    shader.append(tonemap::getToneMapper()
    shader.append(R"(
                          ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
            uniform float in_displayMaxLuminance;
                                                          toAidlDataspace(outputDataspace))
            uniform float in_inputMaxLuminance;
                          .c_str());
        )");


    generateLuminanceScalesForOOTF(inputDataspace, shader);
    generateLuminanceScalesForOOTF(inputDataspace, shader);
    generateToneMapInterpolation(inputDataspace, outputDataspace, shader);
    generateLuminanceNormalizationForOOTF(outputDataspace, shader);
    generateLuminanceNormalizationForOOTF(outputDataspace, shader);


    shader.append(R"(
    shader.append(R"(
            float3 OOTF(float3 xyz) {
            float3 OOTF(float3 linearRGB, float3 xyz) {
                return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
                float3 scaledLinearRGB = ScaleLuminance(linearRGB);
                float3 scaledXYZ = ScaleLuminance(xyz);

                float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);

                return NormalizeLuminance(scaledXYZ * gain);
            }
            }
        )");
        )");
}
}
@@ -399,7 +262,9 @@ static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader)
        )");
        )");
    }
    }
    shader.append(R"(
    shader.append(R"(
        c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
        float3 linearRGB = EOTF(c.rgb);
        float3 xyz = ToXYZ(linearRGB);
        c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
    )");
    )");
    if (undoPremultipliedAlpha) {
    if (undoPremultipliedAlpha) {
        shader.append(R"(
        shader.append(R"(
@@ -465,11 +330,20 @@ sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEff
                colorTransform * mat4(outputColorSpace.getXYZtoRGB());
                colorTransform * mat4(outputColorSpace.getXYZtoRGB());
    }
    }


    effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
    tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
    // If the input luminance is unknown, use display luminance (aka, no-op any luminance changes)
                               // If the input luminance is unknown, use display luminance (aka,
    // This will be the case for eg screenshots in addition to uncalibrated displays
                               // no-op any luminance changes)
    effectBuilder.uniform("in_inputMaxLuminance") =
                               // This will be the case for eg screenshots in addition to
            maxLuminance > 0 ? maxLuminance : maxDisplayLuminance;
                               // uncalibrated displays
                               .contentMaxLuminance =
                                       maxLuminance > 0 ? maxLuminance : maxDisplayLuminance};

    const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata);

    for (const auto& uniform : uniforms) {
        effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
    }

    return effectBuilder.makeShader(nullptr, false);
    return effectBuilder.makeShader(nullptr, false);
}
}


+5 −1
Original line number Original line Diff line number Diff line
@@ -23,7 +23,10 @@ package {


cc_test {
cc_test {
    name: "librenderengine_test",
    name: "librenderengine_test",
    defaults: ["skia_deps", "surfaceflinger_defaults"],
    defaults: [
        "skia_deps",
        "surfaceflinger_defaults",
    ],
    test_suites: ["device-tests"],
    test_suites: ["device-tests"],
    srcs: [
    srcs: [
        "RenderEngineTest.cpp",
        "RenderEngineTest.cpp",
@@ -36,6 +39,7 @@ cc_test {
        "libgmock",
        "libgmock",
        "librenderengine",
        "librenderengine",
        "librenderengine_mocks",
        "librenderengine_mocks",
        "libtonemap",
    ],
    ],


    shared_libs: [
    shared_libs: [
+37 −0
Original line number Original line Diff line number Diff line
// Copyright 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package {
    // See: http://go/android-license-faq
    // A large-scale-change added 'default_applicable_licenses' to import
    // all of the 'license_kinds' from "frameworks_native_license"
    // to get the below license kinds:
    //   SPDX-license-identifier-Apache-2.0
    default_applicable_licenses: ["frameworks_native_license"],
}

cc_library_static {
    name: "libtonemap",
    vendor_available: true,

    export_include_dirs: ["include"],
    local_include_dirs: ["include"],

    shared_libs: [
        "android.hardware.graphics.common-V3-ndk",
    ],
    srcs: [
        "tonemap.cpp",
    ],
}
Loading