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

Commit 51067019 authored by Alec Mouri's avatar Alec Mouri
Browse files

Readback test support for per-layer dimming

Dimming is only verified if setting display brightness is supported. The
test then does:
* Sets the display brightness to max, which is the same nit level that
is found in the display xml file
* Dims a layer by 10%

Bug: 213493262
Test: builds
Change-Id: I3b552682276be26fefd7ae5586ba42af0e574e31
parent 95e490bf
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ cc_test {
        "libgui",
        "libhidlbase",
        "libprocessgroup",
        "libtinyxml2",
        "android.hardware.graphics.mapper@2.0",
        "android.hardware.graphics.mapper@2.1",
        "android.hardware.graphics.mapper@3.0",
+162 −0
Original line number Diff line number Diff line
@@ -24,10 +24,18 @@
#include <composer-vts/include/ReadbackVts.h>
#include <composer-vts/include/RenderEngineVts.h>
#include <gtest/gtest.h>
#include <ui/DisplayId.h>
#include <ui/DisplayIdentification.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>

// tinyxml2 does implicit conversions >:(
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#include <tinyxml2.h>
#pragma clang diagnostic pop
#include "composer-vts/include/GraphicsComposerCallback.h"

namespace aidl::android::hardware::graphics::composer3::vts {
@@ -124,6 +132,76 @@ class GraphicsCompositionTestBase : public ::testing::Test {
                /*layerCount*/ 1u, usage, "VtsHalGraphicsComposer3_ReadbackTest");
    }

    uint64_t getStableDisplayId(int64_t display) {
        DisplayIdentification identification;
        const auto error = mComposerClient->getDisplayIdentificationData(display, &identification);
        EXPECT_TRUE(error.isOk());

        if (const auto info = ::android::parseDisplayIdentificationData(
                    static_cast<uint8_t>(identification.port), identification.data)) {
            return info->id.value;
        }

        return ::android::PhysicalDisplayId::fromPort(static_cast<uint8_t>(identification.port))
                .value;
    }

    // Gets the per-display XML config
    std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXml(int64_t display) {
        std::stringstream pathBuilder;
        pathBuilder << "/vendor/etc/displayconfig/display_id_" << getStableDisplayId(display)
                    << ".xml";
        const std::string path = pathBuilder.str();
        auto document = std::make_unique<tinyxml2::XMLDocument>();
        const tinyxml2::XMLError error = document->LoadFile(path.c_str());
        if (error == tinyxml2::XML_SUCCESS) {
            return document;
        } else {
            return nullptr;
        }
    }

    // Gets the max display brightness for this display.
    // If the display config xml does not exist, then assume that the display is not well-configured
    // enough to provide a display brightness, so return nullopt.
    std::optional<float> getMaxDisplayBrightnessNits(int64_t display) {
        const auto document = getDisplayConfigXml(display);
        if (!document) {
            // Assume the device doesn't support display brightness
            return std::nullopt;
        }

        const auto root = document->RootElement();
        if (!root) {
            // If there's somehow no root element, then this isn't a valid config
            return std::nullopt;
        }

        const auto screenBrightnessMap = root->FirstChildElement("screenBrightnessMap");
        if (!screenBrightnessMap) {
            // A valid display config must have a screen brightness map
            return std::nullopt;
        }

        auto point = screenBrightnessMap->FirstChildElement("point");
        float maxNits = -1.f;
        while (point != nullptr) {
            const auto nits = point->FirstChildElement("nits");
            if (nits) {
                maxNits = std::max(maxNits, nits->FloatText(-1.f));
            }
            point = point->NextSiblingElement("point");
        }

        if (maxNits < 0.f) {
            // If we got here, then there were no point elements containing a nit value, so this
            // config isn't valid
            return std::nullopt;
        }

        return maxNits;
    }

    void writeLayers(const std::vector<std::shared_ptr<TestLayer>>& layers) {
        for (auto layer : layers) {
            layer->write(mWriter);
@@ -876,6 +954,90 @@ TEST_P(GraphicsCompositionTest, SetLayerZOrder) {
    }
}

TEST_P(GraphicsCompositionTest, SetLayerWhitePointDims) {
    std::vector<DisplayCapability> capabilities;
    const auto error = mComposerClient->getDisplayCapabilities(mPrimaryDisplay, &capabilities);
    ASSERT_TRUE(error.isOk());

    const bool brightnessSupport = std::find(capabilities.begin(), capabilities.end(),
                                             DisplayCapability::BRIGHTNESS) != capabilities.end();

    if (!brightnessSupport) {
        GTEST_SUCCEED() << "Cannot verify dimming behavior without brightness support";
        return;
    }

    const std::optional<float> maxBrightnessNitsOptional =
            getMaxDisplayBrightnessNits(mPrimaryDisplay);

    ASSERT_TRUE(maxBrightnessNitsOptional.has_value());

    const float maxBrightnessNits = *maxBrightnessNitsOptional;

    // Preconditions to successfully run are knowing the max brightness and successfully applying
    // the max brightness
    ASSERT_GT(maxBrightnessNits, 0.f);
    mWriter.setDisplayBrightness(mPrimaryDisplay, 1.f);
    execute();
    ASSERT_TRUE(mReader.takeErrors().empty());

    for (ColorMode mode : mTestColorModes) {
        ASSERT_NO_FATAL_FAILURE(
                mComposerClient->setColorMode(mPrimaryDisplay, mode, RenderIntent::COLORIMETRIC));

        if (!getHasReadbackBuffer()) {
            GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace for "
                               "color mode: "
                            << toString(mode);
            continue;
        }
        const common::Rect redRect = {0, 0, mDisplayWidth, mDisplayHeight / 2};
        const common::Rect dimmerRedRect = {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight};
        const auto redLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay);
        redLayer->setColor(RED);
        redLayer->setDisplayFrame(redRect);
        redLayer->setWhitePointNits(maxBrightnessNits);

        const auto dimmerRedLayer =
                std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay);
        dimmerRedLayer->setColor(RED);
        dimmerRedLayer->setDisplayFrame(dimmerRedRect);
        // Intentionally use a small dimming ratio as some implementations may be more likely to
        // kick into GPU composition to apply dithering when the dimming ratio is high.
        static constexpr float kDimmingRatio = 0.9f;
        dimmerRedLayer->setWhitePointNits(maxBrightnessNits * kDimmingRatio);

        const std::vector<std::shared_ptr<TestLayer>> layers = {redLayer, dimmerRedLayer};
        std::vector<Color> expectedColors(static_cast<size_t>(mDisplayWidth * mDisplayHeight));

        ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, redRect, RED);
        ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, dimmerRedRect, DIM_RED);

        ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGraphicBuffer,
                                      mDisplayWidth, mDisplayHeight, mPixelFormat, mDataspace);
        ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());

        writeLayers(layers);
        ASSERT_TRUE(mReader.takeErrors().empty());
        mWriter.validateDisplay(mPrimaryDisplay, ComposerClientWriter::kNoTimestamp);
        execute();
        if (!mReader.takeChangedCompositionTypes(mPrimaryDisplay).empty()) {
            GTEST_SUCCEED()
                    << "Readback verification not supported for GPU composition for color mode: "
                    << toString(mode);
            continue;
        }
        mWriter.presentDisplay(mPrimaryDisplay);
        execute();
        ASSERT_TRUE(mReader.takeErrors().empty());

        ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
        mTestRenderEngine->setRenderLayers(layers);
        ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
        ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
    }
}

class GraphicsBlendModeCompositionTest
    : public GraphicsCompositionTestBase,
      public testing::WithParamInterface<std::tuple<std::string, std::string>> {
+2 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ void TestLayer::write(ComposerClientWriter& writer) {
    writer.setLayerTransform(mDisplay, mLayer, mTransform);
    writer.setLayerPlaneAlpha(mDisplay, mLayer, mAlpha);
    writer.setLayerBlendMode(mDisplay, mLayer, mBlendMode);
    writer.setLayerWhitePointNits(mDisplay, mLayer, mWhitePointNits);
}

std::string ReadbackHelper::getColorModeString(ColorMode mode) {
@@ -102,6 +103,7 @@ LayerSettings TestLayer::toRenderEngineLayerSettings() {
            1.0f, 1.0f));

    layerSettings.geometry.positionTransform = scale * translation;
    layerSettings.whitePointNits = mWhitePointNits;

    return layerSettings;
}
+8 −0
Original line number Diff line number Diff line
@@ -43,6 +43,10 @@ using IMapper2_1 = ::android::hardware::graphics::mapper::V2_1::IMapper;

static const Color BLACK = {0.0f, 0.0f, 0.0f, 1.0f};
static const Color RED = {1.0f, 0.0f, 0.0f, 1.0f};
// DIM_RED is 90% dimmed from RED in linear space
// hard-code as value 243 in 8-bit space here, as calculating it requires
// oetf(eotf(value) * .9), which is a complex non-linear transformation
static const Color DIM_RED = {243.f / 255.f, 0.0f, 0.0f, 1.0f};
static const Color TRANSLUCENT_RED = {1.0f, 0.0f, 0.0f, 0.3f};
static const Color GREEN = {0.0f, 1.0f, 0.0f, 1.0f};
static const Color BLUE = {0.0f, 0.0f, 1.0f, 1.0f};
@@ -67,6 +71,7 @@ class TestLayer {
    void setDisplayFrame(Rect frame) { mDisplayFrame = frame; }
    void setSourceCrop(FRect crop) { mSourceCrop = crop; }
    void setZOrder(uint32_t z) { mZOrder = z; }
    void setWhitePointNits(float whitePointNits) { mWhitePointNits = whitePointNits; }

    void setSurfaceDamage(std::vector<Rect> surfaceDamage) {
        mSurfaceDamage = std::move(surfaceDamage);
@@ -84,10 +89,13 @@ class TestLayer {

    int64_t getLayer() const { return mLayer; }

    float getWhitePointNits() const { return mWhitePointNits; }

  protected:
    int64_t mDisplay;
    int64_t mLayer;
    Rect mDisplayFrame = {0, 0, 0, 0};
    float mWhitePointNits = -1.f;
    std::vector<Rect> mSurfaceDamage;
    Transform mTransform = static_cast<Transform>(0);
    FRect mSourceCrop = {0, 0, 0, 0};