diff --git a/graphics/composer/2.1/utils/vts/TestCommandReader.cpp b/graphics/composer/2.1/utils/vts/TestCommandReader.cpp index 6f8f1ad480..e1dc5b6ebe 100644 --- a/graphics/composer/2.1/utils/vts/TestCommandReader.cpp +++ b/graphics/composer/2.1/utils/vts/TestCommandReader.cpp @@ -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 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 diff --git a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h index 3888eebebd..b9a4a5c103 100644 --- a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h +++ b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h @@ -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> mCompositionChanges; }; } // namespace vts diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp index f2596a462a..459e0fe2c1 100644 --- a/graphics/composer/2.2/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.2/utils/vts/ComposerVts.cpp @@ -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 ComposerClient::getColorModes(Display display) { diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp index 669fbaea35..acc9245952 100644 --- a/graphics/composer/2.2/vts/functional/Android.bp +++ b/graphics/composer/2.2/vts/functional/Android.bp @@ -17,12 +17,15 @@ 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: [ "libfmq", - "libhidltransport", + "libhidltransport", "libsync", ], static_libs: [ diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp new file mode 100644 index 0000000000..fc32951c00 --- /dev/null +++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp @@ -0,0 +1,312 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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(); } + + private: + GraphicsComposerHidlEnvironment() {} + GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment); +}; + +class TestLayer { + public: + TestLayer(std::shared_ptr 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 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 const mComposerClient; + const Display mDisplay; + static constexpr uint32_t kBufferSlotCount = 64; +}; + +class TestColorLayer : public TestLayer { + public: + TestColorLayer(std::shared_ptr const client, Display display) + : TestLayer{client, display} {} + + void write(std::shared_ptr 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( + GraphicsComposerHidlEnvironment::Instance()->getServiceName())); + 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(1024); + mReader = std::make_unique(); + mGralloc = std::make_unique(); + } + + ~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>& 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 expectedColors) { + int32_t bytesPerPixel = GetBytesPerPixel(info.format); + ASSERT_NE(-1, bytesPerPixel) + << "unexpected pixel format " << static_cast(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 mComposer; + std::shared_ptr mComposerClient; + + sp mComposerCallback; + // the first display and is assumed never to be removed + Display mPrimaryDisplay; + int32_t width; + int32_t height; + std::shared_ptr mWriter; + std::unique_ptr mReader; + std::unique_ptr mGralloc; + + private: + Display waitForFirstDisplay() { + while (true) { + std::vector 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(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> layers = {layer}; + + // expected color for each pixel + std::vector 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(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