Hardware composer VTS test harness
Bug: 111563608
Test: VtsHalGraphicsComposerV2_2TargetTest
Change-Id: I965efe8811e45a114978a9c8dd7a440c759787ec
diff --git a/graphics/composer/2.1/utils/vts/TestCommandReader.cpp b/graphics/composer/2.1/utils/vts/TestCommandReader.cpp
index 6f8f1ad..e1dc5b6 100644
--- a/graphics/composer/2.1/utils/vts/TestCommandReader.cpp
+++ b/graphics/composer/2.1/utils/vts/TestCommandReader.cpp
@@ -26,23 +26,53 @@
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
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 3888eeb..b9a4a5c 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 @@
// 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
diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
index f2596a4..459e0fe 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::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) {
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index 669fbae..acc9245 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 0000000..fc32951
--- /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 <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