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

Commit b8485526 authored by Valerie Hau's avatar Valerie Hau Committed by Android (Google) Code Review
Browse files

Merge "Hardware composer VTS test harness"

parents b3f5bd11 667f11ab
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