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

Commit 667f11ab authored by Valerie Hau's avatar Valerie Hau
Browse files

Hardware composer VTS test harness

Bug: 111563608
Test: VtsHalGraphicsComposerV2_2TargetTest
Change-Id: I965efe8811e45a114978a9c8dd7a440c759787ec
parent 4b5acbaf
Loading
Loading
Loading
Loading
+31 −1
Original line number Diff line number Diff line
@@ -26,23 +26,53 @@ namespace V2_1 {
namespace vts {

void TestCommandReader::parse() {
    mCompositionChanges.clear();
    while (!isEmpty()) {
        IComposerClient::Command command;
        uint16_t length;
        ASSERT_TRUE(beginCommand(&command, &length));

        switch (command) {
            case IComposerClient::Command::SELECT_DISPLAY:
                ASSERT_EQ(2, length);
                read64();  // display
                break;
            case IComposerClient::Command::SET_ERROR: {
                ASSERT_EQ(2, length);
                auto loc = read();
                auto err = readSigned();
                GTEST_FAIL() << "unexpected error " << err << " at location " << loc;
            } break;
            case IComposerClient::Command::SELECT_DISPLAY:
            case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
                ASSERT_EQ(0, length % 3);
                for (uint16_t count = 0; count < length / 3; ++count) {
                    uint64_t layerId = read64();
                    uint32_t composition = read();

                    std::pair<uint64_t, uint32_t> compositionChange(layerId, composition);
                    mCompositionChanges.push_back(compositionChange);
                }
                break;
            case IComposerClient::Command::SET_DISPLAY_REQUESTS:
                ASSERT_EQ(1, length % 3);
                read();  // displayRequests, ignored for now
                for (uint16_t count = 0; count < (length - 1) / 3; ++count) {
                    read64();  // layer
                    // silently eat requests to clear the client target, since we won't be testing
                    // client composition anyway
                    ASSERT_EQ(1u, read());
                }
                break;
            case IComposerClient::Command::SET_PRESENT_FENCE:
                ASSERT_EQ(1, length);
                close(readFence());
                break;
            case IComposerClient::Command::SET_RELEASE_FENCES:
                ASSERT_EQ(0, length % 3);
                for (uint16_t count = 0; count < length / 3; ++count) {
                    read64();
                    close(readFence());
                }
                break;
            default:
                GTEST_FAIL() << "unexpected return command " << std::hex
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,8 @@ class TestCommandReader : public CommandReaderBase {
    // Parse all commands in the return command queue.  Call GTEST_FAIL() for
    // unexpected errors or commands.
    void parse();

    std::vector<std::pair<uint64_t, uint32_t>> mCompositionChanges;
};

}  // namespace vts
+2 −3
Original line number Diff line number Diff line
@@ -138,12 +138,11 @@ void ComposerClient::getReadbackBufferAttributes(Display display, PixelFormat* o
}

void ComposerClient::getReadbackBufferFence(Display display, int32_t* outFence) {
    hidl_handle handle;
    mClient->getReadbackBufferFence(display, [&](const auto& tmpError, const auto& tmpHandle) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to get readback fence";
        handle = tmpHandle;
        const native_handle_t* nativeFenceHandle = tmpHandle.getNativeHandle();
        *outFence = dup(nativeFenceHandle->data[0]);
    });
    *outFence = 0;
}

std::vector<ColorMode> ComposerClient::getColorModes(Display display) {
+5 −2
Original line number Diff line number Diff line
@@ -17,7 +17,10 @@
cc_test {
    name: "VtsHalGraphicsComposerV2_2TargetTest",
    defaults: ["VtsHalTargetTestDefaults"],
    srcs: ["VtsHalGraphicsComposerV2_2TargetTest.cpp"],
    srcs: [
        "VtsHalGraphicsComposerV2_2ReadbackTest.cpp",
        "VtsHalGraphicsComposerV2_2TargetTest.cpp",
    ],

    // TODO(b/64437680): Assume these libs are always available on the device.
    shared_libs: [
+312 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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.
 */

#define LOG_TAG "graphics_composer_hidl_hal_readback_tests@2.2"

#include <VtsHalHidlTargetTestBase.h>
#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/unique_fd.h>
#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <composer-vts/2.1/GraphicsComposerCallback.h>
#include <composer-vts/2.1/TestCommandReader.h>
#include <composer-vts/2.2/ComposerVts.h>
#include <mapper-vts/2.1/MapperVts.h>

namespace android {
namespace hardware {
namespace graphics {
namespace composer {
namespace V2_2 {
namespace vts {
namespace {

using android::hardware::hidl_handle;
using common::V1_1::BufferUsage;
using common::V1_1::Dataspace;
using common::V1_1::PixelFormat;
using mapper::V2_1::IMapper;
using mapper::V2_1::vts::Gralloc;
using V2_1::Display;
using V2_1::Layer;
using V2_1::vts::TestCommandReader;

// Test environment for graphics.composer
class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
   public:
    // get the test environment singleton
    static GraphicsComposerHidlEnvironment* Instance() {
        static GraphicsComposerHidlEnvironment* instance = new GraphicsComposerHidlEnvironment;
        return instance;
    }
    virtual void registerTestServices() override { registerTestService<IComposer>(); }

   private:
    GraphicsComposerHidlEnvironment() {}
    GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment);
};

class TestLayer {
   public:
    TestLayer(std::shared_ptr<ComposerClient> const client, Display display)
        : mLayer(client->createLayer(display, kBufferSlotCount)),
          mComposerClient(client),
          mDisplay(display) {}

    virtual ~TestLayer() { mComposerClient->destroyLayer(mDisplay, mLayer); }

    virtual void write(std::shared_ptr<CommandWriterBase> writer) {
        writer->selectLayer(mLayer);
        writer->setLayerDisplayFrame(mDisplayFrame);
        writer->setLayerZOrder(mZOrder);
    }

    void setDisplayFrame(IComposerClient::Rect frame) { mDisplayFrame = frame; }
    void setZOrder(uint32_t z) { mZOrder = z; }

   protected:
    Layer mLayer;
    IComposerClient::Rect mDisplayFrame = {0, 0, 0, 0};
    uint32_t mZOrder = 0;

   private:
    std::shared_ptr<ComposerClient> const mComposerClient;
    const Display mDisplay;
    static constexpr uint32_t kBufferSlotCount = 64;
};

class TestColorLayer : public TestLayer {
   public:
    TestColorLayer(std::shared_ptr<ComposerClient> const client, Display display)
        : TestLayer{client, display} {}

    void write(std::shared_ptr<CommandWriterBase> writer) override {
        TestLayer::write(writer);
        writer->setLayerCompositionType(IComposerClient::Composition::SOLID_COLOR);
        writer->setLayerColor(mColor);
    }

    void setColor(IComposerClient::Color color) { mColor = color; }

   private:
    IComposerClient::Color mColor = {0xff, 0xff, 0xff, 0xff};
};

class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase {
   protected:
    using PowerMode = V2_1::IComposerClient::PowerMode;
    void SetUp() override {
        ASSERT_NO_FATAL_FAILURE(
            mComposer = std::make_unique<Composer>(
                GraphicsComposerHidlEnvironment::Instance()->getServiceName<IComposer>()));
        ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient());
        mComposerCallback = new V2_1::vts::GraphicsComposerCallback;
        mComposerClient->registerCallback(mComposerCallback);

        // assume the first display is primary and is never removed
        mPrimaryDisplay = waitForFirstDisplay();
        Config activeConfig = mComposerClient->getActiveConfig(mPrimaryDisplay);
        width = mComposerClient->getDisplayAttribute(mPrimaryDisplay, activeConfig,
                                                     IComposerClient::Attribute::WIDTH);
        height = mComposerClient->getDisplayAttribute(mPrimaryDisplay, activeConfig,
                                                      IComposerClient::Attribute::HEIGHT);

        // explicitly disable vsync
        mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
        mComposerCallback->setVsyncAllowed(false);

        // set up command writer/reader and gralloc
        mWriter = std::make_shared<CommandWriterBase>(1024);
        mReader = std::make_unique<TestCommandReader>();
        mGralloc = std::make_unique<Gralloc>();
    }

    ~GraphicsComposerReadbackTest() override {
        if (mComposerCallback != nullptr) {
            EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
            EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
            EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount());
        }
    }

    void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }

    void render(const std::vector<std::shared_ptr<TestLayer>>& layers) {
        for (auto layer : layers) {
            layer->write(mWriter);
        }
        execute();
        mWriter->validateDisplay();
        mWriter->presentDisplay();
        execute();
    }

    int32_t GetBytesPerPixel(PixelFormat format) {
        switch (format) {
            case PixelFormat::RGBA_8888:
                return 4;
            case PixelFormat::RGB_888:
                return 3;
            default:
                return -1;
        }
    }

    bool readbackSupported(const PixelFormat& pixelFormat, const Dataspace& dataspace,
                           const Error& error) {
        if (error == Error::UNSUPPORTED) {
            return false;
        }
        // TODO: add support for RGBA_1010102
        if (pixelFormat != PixelFormat::RGB_888 && pixelFormat != PixelFormat::RGBA_8888) {
            return false;
        }
        if (dataspace != Dataspace::V0_SRGB) {
            return false;
        }
        return true;
    }

    void getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
                                     Dataspace* outDataspace, Error* outError) {
        mComposerClient->getRaw()->getReadbackBufferAttributes(
            display,
            [&](const auto& tmpError, const auto& tmpOutPixelFormat, const auto& tmpOutDataspace) {
                *outError = tmpError;
                *outPixelFormat = tmpOutPixelFormat;
                *outDataspace = tmpOutDataspace;
            });

        // Not all devices support readback.  Pass test if this is the case
        if (!readbackSupported(*outPixelFormat, *outDataspace, *outError)) {
            GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace";
        }
    }

    void checkReadbackBuffer(IMapper::BufferDescriptorInfo info, uint32_t stride, void* bufferData,
                             std::vector<IComposerClient::Color> expectedColors) {
        int32_t bytesPerPixel = GetBytesPerPixel(info.format);
        ASSERT_NE(-1, bytesPerPixel)
            << "unexpected pixel format " << static_cast<int32_t>(info.format)
            << "(expected RGBA_8888 or RGB_888)";
        for (int row = 0; row < height; row++) {
            for (int col = 0; col < width; col++) {
                int pixel = row * width + col;
                int offset = (row * stride + col) * bytesPerPixel;
                uint8_t* pixelColor = (uint8_t*)bufferData + offset;

                EXPECT_EQ(expectedColors[pixel].r, pixelColor[0]);
                EXPECT_EQ(expectedColors[pixel].g, pixelColor[1]);
                EXPECT_EQ(expectedColors[pixel].b, pixelColor[2]);
            }
        }
    }

    std::unique_ptr<Composer> mComposer;
    std::shared_ptr<ComposerClient> mComposerClient;

    sp<V2_1::vts::GraphicsComposerCallback> mComposerCallback;
    // the first display and is assumed never to be removed
    Display mPrimaryDisplay;
    int32_t width;
    int32_t height;
    std::shared_ptr<CommandWriterBase> mWriter;
    std::unique_ptr<TestCommandReader> mReader;
    std::unique_ptr<Gralloc> mGralloc;

   private:
    Display waitForFirstDisplay() {
        while (true) {
            std::vector<Display> displays = mComposerCallback->getDisplays();
            if (displays.empty()) {
                usleep(5 * 1000);
                continue;
            }
            return displays[0];
        }
    }
};

TEST_F(GraphicsComposerReadbackTest, SingleSolidColorLayer) {
    mWriter->selectDisplay(mPrimaryDisplay);
    mComposerClient->setPowerMode(mPrimaryDisplay, PowerMode::ON);
    mComposerClient->setColorMode(mPrimaryDisplay, ColorMode::SRGB, RenderIntent::COLORIMETRIC);

    auto layer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay);
    IComposerClient::Color color({0, 0, 0xff, 0xff});
    IComposerClient::Rect coloredSquare({100, 100, 500, 500});
    layer->setColor(color);
    layer->setDisplayFrame(coloredSquare);
    layer->setZOrder(10);

    std::vector<std::shared_ptr<TestLayer>> layers = {layer};

    // expected color for each pixel
    std::vector<IComposerClient::Color> expectedColors(width * height);
    for (int row = 0; row < height; row++) {
        for (int col = 0; col < width; col++) {
            int pixel = row * width + col;
            if (row >= coloredSquare.top && row < coloredSquare.bottom &&
                col >= coloredSquare.left && col < coloredSquare.right) {
                expectedColors[pixel] = color;
            } else {
                expectedColors[pixel] = {0, 0, 0, 0xff};
            }
        }
    }

    PixelFormat pixelFormat;
    Dataspace dataspace;
    Error error;
    getReadbackBufferAttributes(mPrimaryDisplay, &pixelFormat, &dataspace, &error);

    IMapper::BufferDescriptorInfo info;
    info.width = width;
    info.height = height;
    info.layerCount = 1;
    info.format = pixelFormat;
    info.usage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::GPU_TEXTURE);

    uint32_t stride;
    const native_handle_t* buffer = mGralloc->allocate(info, /*import*/ true, &stride);
    mComposerClient->setReadbackBuffer(mPrimaryDisplay, buffer, -1);

    render(layers);

    int32_t fenceHandle;
    mComposerClient->getReadbackBufferFence(mPrimaryDisplay, &fenceHandle);

    base::unique_fd fence(fenceHandle);

    // lock buffer
    // Create Rect accessRegion to specify reading the entire buffer
    IMapper::Rect accessRegion;
    accessRegion.left = 0;
    accessRegion.top = 0;
    accessRegion.width = info.width;
    accessRegion.height = info.height;

    void* bufData = mGralloc->lock(buffer, info.usage, accessRegion, fence);
    checkReadbackBuffer(info, stride, bufData, expectedColors);
}

}  // anonymous namespace
}  // namespace vts
}  // namespace V2_2
}  // namespace composer
}  // namespace graphics
}  // namespace hardware
}  // namespace android