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

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

Merge "End-to-end plumbing for dimming SDR layers"

parents e4973438 cdf6cbc6
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -57,8 +57,10 @@ struct DisplaySettings {
    // orientation.
    uint32_t orientation = ui::Transform::ROT_0;

    // SDR white point, -1f if unknown
    float sdrWhitePointNits = -1.f;
    // Target luminance of the display. -1f if unknown.
    // All layers will be dimmed by (max(layer white points) / targetLuminanceNits).
    // If the target luminance is unknown, then no display-level dimming occurs.
    float targetLuminanceNits = -1.f;
};

static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
+6 −0
Original line number Diff line number Diff line
@@ -171,6 +171,12 @@ struct LayerSettings {

    // Name associated with the layer for debugging purposes.
    std::string name;

    // Luminance of the white point for this layer. Used for linear dimming.
    // Individual layers will be dimmed by (whitePointNits / maxWhitePoint).
    // If white point nits are unknown, then this layer is assumed to have the
    // same luminance as the brightest layer in the scene.
    float whitePointNits = -1.f;
};

// Keep in sync with custom comparison function in
+69 −33
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@
#include <cmath>
#include <cstdint>
#include <memory>
#include <numeric>

#include "../gl/GLExtensions.h"
#include "Cache.h"
@@ -612,33 +613,33 @@ private:
    AutoBackendTexture::CleanupManager& mMgr;
};

sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
                                                              const LayerSettings& layer,
                                                              const DisplaySettings& display,
                                                              bool undoPremultipliedAlpha,
                                                              bool requiresLinearEffect) {
    const auto stretchEffect = layer.stretchEffect;
sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
        const RuntimeEffectShaderParameters& parameters) {
    // The given surface will be stretched by HWUI via matrix transformation
    // which gets similar results for most surfaces
    // Determine later on if we need to leverage the stertch shader within
    // surface flinger
    const auto& stretchEffect = parameters.layer.stretchEffect;
    auto shader = parameters.shader;
    if (stretchEffect.hasEffect()) {
        const auto targetBuffer = layer.source.buffer.buffer;
        const auto targetBuffer = parameters.layer.source.buffer.buffer;
        const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
        if (graphicBuffer && shader) {
        if (graphicBuffer && parameters.shader) {
            shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
        }
    }

    if (requiresLinearEffect) {
        const ui::Dataspace inputDataspace =
                mUseColorManagement ? layer.sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
        const ui::Dataspace outputDataspace =
                mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
    if (parameters.requiresLinearEffect) {
        const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
                                                                 : ui::Dataspace::V0_SRGB_LINEAR;
        const ui::Dataspace outputDataspace = mUseColorManagement
                ? parameters.display.outputDataspace
                : ui::Dataspace::V0_SRGB_LINEAR;

        auto effect = shaders::LinearEffect{.inputDataspace = inputDataspace,
        auto effect =
                shaders::LinearEffect{.inputDataspace = inputDataspace,
                                      .outputDataspace = outputDataspace,
                                            .undoPremultipliedAlpha = undoPremultipliedAlpha};
                                      .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};

        auto effectIter = mRuntimeEffects.find(effect);
        sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -648,16 +649,16 @@ sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> sh
        } else {
            runtimeEffect = effectIter->second;
        }
        float maxLuminance = layer.source.buffer.maxLuminanceNits;
        // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
        // white point
        if (maxLuminance <= 0.f) {
            maxLuminance = display.sdrWhitePointNits;
        }
        return createLinearEffectShader(shader, effect, runtimeEffect, layer.colorTransform,
                                        display.maxLuminance, maxLuminance);
        mat4 colorTransform = parameters.layer.colorTransform;

        colorTransform *=
                mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
                                 parameters.layerDimmingRatio, 1.f));
        return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
                                        parameters.display.maxLuminance,
                                        parameters.layer.source.buffer.maxLuminanceNits);
    }
    return shader;
    return parameters.shader;
}

void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
@@ -730,6 +731,11 @@ static SkRRect getBlurRRect(const BlurRegion& region) {
    return roundedRect;
}

static bool equalsWithinMargin(float expected, float value, float margin) {
    LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
    return std::abs(expected - value) < margin;
}

void SkiaGLRenderEngine::drawLayersInternal(
        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
@@ -791,6 +797,18 @@ void SkiaGLRenderEngine::drawLayersInternal(
    const bool ctModifiesAlpha =
            displayColorTransform && !displayColorTransform->isAlphaUnchanged();

    // Find the max layer white point to determine the max luminance of the scene...
    const float maxLayerWhitePoint = std::transform_reduce(
            layers.cbegin(), layers.cend(), 0.f,
            [](float left, float right) { return std::max(left, right); },
            [&](const auto& l) { return l.whitePointNits; });

    // ...and compute the dimming ratio if dimming is requested
    const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
                    maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
            ? maxLayerWhitePoint / display.targetLuminanceNits
            : 1.f;

    // Find if any layers have requested blur, we'll use that info to decide when to render to an
    // offscreen buffer and when to render to the native buffer.
    sk_sp<SkSurface> activeSurface(dstSurface);
@@ -964,11 +982,14 @@ void SkiaGLRenderEngine::drawLayersInternal(
            drawShadow(canvas, rrect, layer.shadow);
        }

        const float layerDimmingRatio = layer.whitePointNits <= 0.f
                ? displayDimmingRatio
                : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;

        const bool requiresLinearEffect = layer.colorTransform != mat4() ||
                (mUseColorManagement &&
                 needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
                (display.sdrWhitePointNits > 0.f &&
                 display.sdrWhitePointNits != display.maxLuminance);
                !equalsWithinMargin(1.f, layerDimmingRatio, 0.001f);

        // quick abort from drawing the remaining portion of the layer
        if (layer.skipContentDraw ||
@@ -1067,9 +1088,20 @@ void SkiaGLRenderEngine::drawLayersInternal(
                                                           toSkColorSpace(layerDataspace)));
            }

            paint.setShader(createRuntimeEffectShader(shader, layer, display,
                                                      !item.isOpaque && item.usePremultipliedAlpha,
                                                      requiresLinearEffect));
            paint.setShader(createRuntimeEffectShader(
                    RuntimeEffectShaderParameters{.shader = shader,
                                                  .layer = layer,
                                                  .display = display,
                                                  .undoPremultipliedAlpha = !item.isOpaque &&
                                                          item.usePremultipliedAlpha,
                                                  .requiresLinearEffect = requiresLinearEffect,
                                                  .layerDimmingRatio = layerDimmingRatio}));

            // Turn on dithering when dimming beyond this threshold.
            static constexpr float kDimmingThreshold = 0.2f;
            if (layerDimmingRatio <= kDimmingThreshold) {
                paint.setDither(true);
            }
            paint.setAlphaf(layer.alpha);
        } else {
            ATRACE_NAME("DrawColor");
@@ -1079,9 +1111,13 @@ void SkiaGLRenderEngine::drawLayersInternal(
                                                                .fB = color.b,
                                                                .fA = layer.alpha},
                                                      toSkColorSpace(layerDataspace));
            paint.setShader(createRuntimeEffectShader(shader, layer, display,
                                                      /* undoPremultipliedAlpha */ false,
                                                      requiresLinearEffect));
            paint.setShader(createRuntimeEffectShader(
                    RuntimeEffectShaderParameters{.shader = shader,
                                                  .layer = layer,
                                                  .display = display,
                                                  .undoPremultipliedAlpha = false,
                                                  .requiresLinearEffect = requiresLinearEffect,
                                                  .layerDimmingRatio = layerDimmingRatio}));
        }

        if (layer.disableBlending) {
+10 −4
Original line number Diff line number Diff line
@@ -106,12 +106,18 @@ private:
    void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
    void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                    const ShadowSettings& shadowSettings);

    // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
    // Otherwise it returns the input shader.
    sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings& layer,
                                              const DisplaySettings& display,
                                              bool undoPremultipliedAlpha,
                                              bool requiresLinearEffect);
    struct RuntimeEffectShaderParameters {
        sk_sp<SkShader> shader;
        const LayerSettings& layer;
        const DisplaySettings& display;
        bool undoPremultipliedAlpha;
        bool requiresLinearEffect;
        float layerDimmingRatio;
    };
    sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);

    EGLDisplay mEGLDisplay;
    EGLContext mEGLContext;
+142 −0
Original line number Diff line number Diff line
@@ -208,6 +208,21 @@ public:
                                                 renderengine::ExternalTexture::Usage::WRITEABLE);
    }

    std::shared_ptr<renderengine::ExternalTexture> allocateAndFillSourceBuffer(uint32_t width,
                                                                               uint32_t height,
                                                                               ubyte4 color) {
        const auto buffer = allocateSourceBuffer(width, height);
        uint8_t* pixels;
        buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                                  reinterpret_cast<void**>(&pixels));
        pixels[0] = color.r;
        pixels[1] = color.g;
        pixels[2] = color.b;
        pixels[3] = color.a;
        buffer->getBuffer()->unlock();
        return buffer;
    }

    RenderEngineTest() {
        const ::testing::TestInfo* const test_info =
                ::testing::UnitTest::GetInstance()->current_test_info();
@@ -2195,6 +2210,133 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) {
    expectBufferColor(rect, 0, 128, 0, 128);
}

TEST_P(RenderEngineTest, testDimming) {
    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
        return;
    }
    initializeRenderEngine();

    const auto displayRect = Rect(3, 1);
    const renderengine::DisplaySettings display{
            .physicalDisplay = displayRect,
            .clip = displayRect,
            .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            .targetLuminanceNits = 1000.f,
    };

    const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
    const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
    const auto redBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(255, 0, 0, 255));

    const renderengine::LayerSettings greenLayer{
            .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
            .source =
                    renderengine::PixelSource{
                            .buffer =
                                    renderengine::Buffer{
                                            .buffer = greenBuffer,
                                            .usePremultipliedAlpha = true,
                                    },
                    },
            .alpha = 1.0f,
            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            .whitePointNits = 200.f,
    };

    const renderengine::LayerSettings blueLayer{
            .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
            .source =
                    renderengine::PixelSource{
                            .buffer =
                                    renderengine::Buffer{
                                            .buffer = blueBuffer,
                                            .usePremultipliedAlpha = true,
                                    },
                    },
            .alpha = 1.0f,
            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            .whitePointNits = 1000.f / 51.f,
    };

    const renderengine::LayerSettings redLayer{
            .geometry.boundaries = FloatRect(2.f, 0.f, 3.f, 1.f),
            .source =
                    renderengine::PixelSource{
                            .buffer =
                                    renderengine::Buffer{
                                            .buffer = redBuffer,
                                            .usePremultipliedAlpha = true,
                                    },
                    },
            .alpha = 1.0f,
            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            // When the white point is not set for a layer, just ignore it and treat it as the same
            // as the max layer
            .whitePointNits = -1.f,
    };

    std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer, redLayer};
    invokeDraw(display, layers);

    expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
    expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 5, 255, 1);
    expectBufferColor(Rect(2, 0, 3, 1), 51, 0, 0, 255, 1);
}

TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
    initializeRenderEngine();
    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
        return;
    }

    const auto displayRect = Rect(2, 1);
    const renderengine::DisplaySettings display{
            .physicalDisplay = displayRect,
            .clip = displayRect,
            .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            .targetLuminanceNits = -1.f,
    };

    const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
    const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));

    const renderengine::LayerSettings greenLayer{
            .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
            .source =
                    renderengine::PixelSource{
                            .buffer =
                                    renderengine::Buffer{
                                            .buffer = greenBuffer,
                                            .usePremultipliedAlpha = true,
                                    },
                    },
            .alpha = 1.0f,
            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            .whitePointNits = 200.f,
    };

    const renderengine::LayerSettings blueLayer{
            .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
            .source =
                    renderengine::PixelSource{
                            .buffer =
                                    renderengine::Buffer{
                                            .buffer = blueBuffer,
                                            .usePremultipliedAlpha = true,
                                    },
                    },
            .alpha = 1.0f,
            .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
            .whitePointNits = 1000.f,
    };

    std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer};
    invokeDraw(display, layers);

    expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
    expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 255, 255);
}

TEST_P(RenderEngineTest, test_isOpaque) {
    initializeRenderEngine();

Loading