mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge "Hardware composer VTS test harness"
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user