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

Commit 8a186104 authored by Alec Mouri's avatar Alec Mouri
Browse files

Align HLG and PQ color management to 203 nits == SDR max by default

The public HLG and PQ definitions map 203 nits to SDR white by scaling
the respective transfer functions.

Bug: 278121691
Bug: 278122024
Test: HwAccelerationTest test gradients
Test: HDR test videos match DPU and GPU composition
Test: SilkFX test HLG and PQ images
Change-Id: Id830e2ac72f5bcf8566556053fcf3af6b945581b
parent 3e5965f3
Loading
Loading
Loading
Loading
+6 −3
Original line number Original line Diff line number Diff line
@@ -21,6 +21,8 @@ namespace renderengine {
namespace skia {
namespace skia {


// please keep in sync with hwui/utils/Color.cpp
// please keep in sync with hwui/utils/Color.cpp
// TODO: Scale by the dimming ratio here instead of in a generic 3x3 transform
// Otherwise there may be luminance shift for e.g., HLG.
sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
    skcms_Matrix3x3 gamut;
    skcms_Matrix3x3 gamut;
    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -61,13 +63,14 @@ sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
        case HAL_DATASPACE_TRANSFER_GAMMA2_8:
        case HAL_DATASPACE_TRANSFER_GAMMA2_8:
            return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
            return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
        case HAL_DATASPACE_TRANSFER_ST2084:
        case HAL_DATASPACE_TRANSFER_ST2084:
            return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
            return SkColorSpace::MakeRGB({-2.f, -1.55522297832f, 1.86045365631f, 32 / 2523.0f,
                                          2413 / 128.0f, -2392 / 128.0f, 8192 / 1305.0f},
                                         gamut);
        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
            return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
            return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
        case HAL_DATASPACE_TRANSFER_HLG:
        case HAL_DATASPACE_TRANSFER_HLG:
            // return HLG transfer but scale by 1/12
            skcms_TransferFunction hlgFn;
            skcms_TransferFunction hlgFn;
            if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 1.f / 12.f, 2.f, 2.f,
            if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 0.314509843, 2.f, 2.f,
                                                        1.f / 0.17883277f, 0.28466892f,
                                                        1.f / 0.17883277f, 0.28466892f,
                                                        0.55991073f)) {
                                                        0.55991073f)) {
                return SkColorSpace::MakeRGB(hlgFn, gamut);
                return SkColorSpace::MakeRGB(hlgFn, gamut);
+4 −6
Original line number Original line Diff line number Diff line
@@ -913,12 +913,10 @@ void SkiaRenderEngine::drawLayersInternal(
            continue;
            continue;
        }
        }


        // If we need to map to linear space or color management is disabled, then mark the source
        // If color management is disabled, then mark the source image with the same colorspace as
        // image with the same colorspace as the destination surface so that Skia's color
        // the destination surface so that Skia's color management is a no-op.
        // management is a no-op.
        const ui::Dataspace layerDataspace =
        const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
                !mUseColorManagement ? display.outputDataspace : layer.sourceDataspace;
                ? display.outputDataspace
                : layer.sourceDataspace;


        SkPaint paint;
        SkPaint paint;
        if (layer.source.buffer.buffer) {
        if (layer.source.buffer.buffer) {
+10 −17
Original line number Original line Diff line number Diff line
@@ -51,23 +51,20 @@ struct LinearEffect {
    // Input dataspace of the source colors.
    // Input dataspace of the source colors.
    const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
    const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;


    // Working dataspace for the output surface, for conversion from linear space.
    // Working dataspace for the output surface.
    const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
    const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;


    // Sets whether alpha premultiplication must be undone.
    // Sets whether alpha premultiplication must be undone.
    // This is required if the source colors use premultiplied alpha and is not opaque.
    // This is required if the source colors use premultiplied alpha and is not opaque.
    const bool undoPremultipliedAlpha = false;
    const bool undoPremultipliedAlpha = false;


    // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear
    // "Fake" dataspace of the destination colors. This is used for applying an OETF to compute
    // RGB. This is used when Skia is expected to color manage the input image based on the
    // non-linear RGB. This is used when Skia is expected to color manage the input image based on
    // dataspace of the provided source image and destination surface. SkRuntimeEffects use the
    // the dataspace of the provided source image and destination surface. Some use-cases in
    // destination color space as the working color space. RenderEngine deliberately sets the color
    // RenderEngine expect to apply a different OETF than what is expected by Skia. As in,
    // space for input images and destination surfaces to be the same whenever LinearEffects are
    // RenderEngine will color manage to a custom destination and "cast" the result to Skia's
    // expected to be used so that color-management is controlled by RenderEngine, but other users
    // working space.
    // of a LinearEffect may not be able to control the color space of the images and surfaces. So
    ui::Dataspace fakeOutputDataspace = ui::Dataspace::UNKNOWN;
    // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
    // dataspace for correct conversion to linear colors.
    ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;


    enum SkSLType { Shader, ColorFilter };
    enum SkSLType { Shader, ColorFilter };
    SkSLType type = Shader;
    SkSLType type = Shader;
@@ -76,7 +73,7 @@ struct LinearEffect {
static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
    return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
    return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
            lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha &&
            lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha &&
            lhs.fakeInputDataspace == rhs.fakeInputDataspace;
            lhs.fakeOutputDataspace == rhs.fakeOutputDataspace;
}
}


struct LinearEffectHasher {
struct LinearEffectHasher {
@@ -89,7 +86,7 @@ struct LinearEffectHasher {
        size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
        size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
        result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
        result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
        result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
        result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
        return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace));
        return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeOutputDataspace));
    }
    }
};
};


@@ -99,10 +96,6 @@ struct LinearEffectHasher {
// 2. Apply color transform matrices in linear space
// 2. Apply color transform matrices in linear space
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);


// Generates a shader string that applies color transforms in linear space.
// This is intended to be plugged into an SkColorFilter
std::string buildLinearEffectSkSLForColorFilter(const LinearEffect& linearEffect);

// Generates a list of uniforms to set on the LinearEffect shader above.
// Generates a list of uniforms to set on the LinearEffect shader above.
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
        const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
        const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
+127 −354

File changed.

Preview size limit exceeded, changes collapsed.

+20 −22
Original line number Original line Diff line number Diff line
@@ -35,6 +35,10 @@ MATCHER_P2(UniformEq, name, value, "") {
    return arg.name == name && arg.value == value;
    return arg.name == name && arg.value == value;
}
}


MATCHER_P(UniformNameEq, name, "") {
    return arg.name == name;
}

template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
std::vector<uint8_t> buildUniformValue(T value) {
std::vector<uint8_t> buildUniformValue(T value) {
    std::vector<uint8_t> result;
    std::vector<uint8_t> result;
@@ -49,50 +53,44 @@ TEST_F(ShadersTest, buildLinearEffectUniforms_selectsNoOpGamutMatrices) {
    shaders::LinearEffect effect =
    shaders::LinearEffect effect =
            shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
                                  .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
                                  .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
                                  .fakeInputDataspace = ui::Dataspace::UNKNOWN};
                                  .fakeOutputDataspace = ui::Dataspace::UNKNOWN};


    mat4 colorTransform = mat4::scale(vec4(.9, .9, .9, 1.));
    mat4 colorTransform = mat4::scale(vec4(.9, .9, .9, 1.));
    auto uniforms =
    auto uniforms =
            shaders::buildLinearEffectUniforms(effect, colorTransform, 1.f, 1.f, 1.f, nullptr,
            shaders::buildLinearEffectUniforms(effect, colorTransform, 1.f, 1.f, 1.f, nullptr,
                                               aidl::android::hardware::graphics::composer3::
                                               aidl::android::hardware::graphics::composer3::
                                                       RenderIntent::COLORIMETRIC);
                                                       RenderIntent::COLORIMETRIC);
    EXPECT_THAT(uniforms, Contains(UniformEq("in_rgbToXyz", buildUniformValue<mat4>(mat4()))));
    EXPECT_THAT(uniforms,
    EXPECT_THAT(uniforms,
                Contains(UniformEq("in_xyzToRgb", buildUniformValue<mat4>(colorTransform))));
                Contains(UniformEq("in_rgbToXyz",
                                   buildUniformValue<mat3>(
                                           ColorSpace::linearExtendedSRGB().getRGBtoXYZ()))));
    EXPECT_THAT(uniforms,
                Contains(UniformEq("in_xyzToSrcRgb",
                                   buildUniformValue<mat3>(
                                           ColorSpace::linearSRGB().getXYZtoRGB()))));
    // color transforms are already tested in renderengine's tests
    EXPECT_THAT(uniforms, Contains(UniformNameEq("in_colorTransform")));
}
}


TEST_F(ShadersTest, buildLinearEffectUniforms_selectsGamutTransformMatrices) {
TEST_F(ShadersTest, buildLinearEffectUniforms_selectsGamutTransformMatrices) {
    shaders::LinearEffect effect =
    shaders::LinearEffect effect =
            shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB,
            shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB,
                                  .outputDataspace = ui::Dataspace::DISPLAY_P3,
                                  .outputDataspace = ui::Dataspace::DISPLAY_P3,
                                  .fakeInputDataspace = ui::Dataspace::UNKNOWN};
                                  .fakeOutputDataspace = ui::Dataspace::UNKNOWN};


    ColorSpace inputColorSpace = ColorSpace::sRGB();
    ColorSpace inputColorSpace = ColorSpace::sRGB();
    ColorSpace outputColorSpace = ColorSpace::DisplayP3();
    auto uniforms =
    auto uniforms =
            shaders::buildLinearEffectUniforms(effect, mat4(), 1.f, 1.f, 1.f, nullptr,
            shaders::buildLinearEffectUniforms(effect, mat4(), 1.f, 1.f, 1.f, nullptr,
                                               aidl::android::hardware::graphics::composer3::
                                               aidl::android::hardware::graphics::composer3::
                                                       RenderIntent::COLORIMETRIC);
                                                       RenderIntent::COLORIMETRIC);
    EXPECT_THAT(uniforms,
    EXPECT_THAT(uniforms,
                Contains(UniformEq("in_rgbToXyz",
                Contains(UniformEq("in_rgbToXyz",
                                   buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ())))));
                                   buildUniformValue<mat3>(
                                           ColorSpace::linearExtendedSRGB().getRGBtoXYZ()))));
    EXPECT_THAT(uniforms,
    EXPECT_THAT(uniforms,
                Contains(UniformEq("in_xyzToRgb",
                Contains(UniformEq("in_xyzToSrcRgb",
                                   buildUniformValue<mat4>(mat4(outputColorSpace.getXYZtoRGB())))));
                                   buildUniformValue<mat3>(inputColorSpace.getXYZtoRGB()))));
}
    EXPECT_THAT(uniforms, Contains(UniformNameEq("in_colorTransform")));

TEST_F(ShadersTest, buildLinearEffectUniforms_respectsFakeInputDataspace) {
    shaders::LinearEffect effect =
            shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB,
                                  .outputDataspace = ui::Dataspace::DISPLAY_P3,
                                  .fakeInputDataspace = ui::Dataspace::DISPLAY_P3};

    auto uniforms =
            shaders::buildLinearEffectUniforms(effect, mat4(), 1.f, 1.f, 1.f, nullptr,
                                               aidl::android::hardware::graphics::composer3::
                                                       RenderIntent::COLORIMETRIC);
    EXPECT_THAT(uniforms, Contains(UniformEq("in_rgbToXyz", buildUniformValue<mat4>(mat4()))));
    EXPECT_THAT(uniforms, Contains(UniformEq("in_xyzToRgb", buildUniformValue<mat4>(mat4()))));
}
}


} // namespace android
} // namespace android