blob: 9b319854e6ba1ab484a94e645e119bfc395a7845 [file] [log] [blame]
/*
* Copyright (C) 2017 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_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "FakeHwcTest"
#include "FakeComposerClient.h"
#include "FakeComposerService.h"
#include "FakeComposerUtils.h"
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <android/looper.h>
#include <android/native_window.h>
#include <binder/ProcessState.h>
#include <hwbinder/ProcessState.h>
#include <log/log.h>
#include <private/gui/ComposerService.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <limits>
using namespace std::chrono_literals;
using namespace android;
using namespace android::hardware;
using namespace sftest;
namespace {
// Mock test helpers
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::_;
using Transaction = SurfaceComposerClient::Transaction;
///////////////////////////////////////////////
struct TestColor {
public:
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
constexpr static TestColor RED = {195, 63, 63, 255};
constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
constexpr static TestColor GREEN = {63, 195, 63, 255};
constexpr static TestColor BLUE = {63, 63, 195, 255};
constexpr static TestColor DARK_GRAY = {63, 63, 63, 255};
constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
// Fill an RGBA_8888 formatted surface with a single color.
static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
bool unlock = true) {
ANativeWindow_Buffer outBuffer;
sp<Surface> s = sc->getSurface();
ASSERT_TRUE(s != nullptr);
ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
for (int y = 0; y < outBuffer.height; y++) {
for (int x = 0; x < outBuffer.width; x++) {
uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
pixel[0] = color.r;
pixel[1] = color.g;
pixel[2] = color.b;
pixel[3] = color.a;
}
}
if (unlock) {
ASSERT_EQ(NO_ERROR, s->unlockAndPost());
}
}
inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
RenderState res;
res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
res.mPlaneAlpha = 1.0f;
res.mSwapCount = 0;
res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
static_cast<float>(bottom - top)};
return res;
}
inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
unsigned int bottom) {
EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
static_cast<int>(bottom));
}
////////////////////////////////////////////////
class DisplayTest : public ::testing::Test {
public:
class MockComposerClient : public FakeComposerClient {
public:
MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
MOCK_METHOD4(getDisplayAttribute,
Error(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue));
// Re-routing to basic fake implementation
Error getDisplayAttributeFake(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) {
return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
}
};
protected:
static int processDisplayEvents(int fd, int events, void* data);
void SetUp() override;
void TearDown() override;
void waitForDisplayTransaction();
bool waitForHotplugEvent(uint32_t id, bool connected);
sp<IComposer> mFakeService;
sp<SurfaceComposerClient> mComposerClient;
MockComposerClient* mMockComposer;
std::unique_ptr<DisplayEventReceiver> mReceiver;
sp<Looper> mLooper;;
std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
};
void DisplayTest::SetUp() {
// TODO: The mMockComposer should be a unique_ptr, but it needs to
// outlive the test class. Currently ComposerClient only dies
// when the service is replaced. The Mock deletes itself when
// removeClient is called on it, which is ugly. This can be
// changed if HIDL ServiceManager allows removing services or
// ComposerClient starts taking the ownership of the contained
// implementation class. Moving the fake class to the HWC2
// interface instead of the current Composer interface might also
// change the situation.
mMockComposer = new MockComposerClient;
sp<ComposerClient> client = new ComposerClient(mMockComposer);
mFakeService = new FakeComposerService(client);
(void)mFakeService->registerAsService("mock");
android::hardware::ProcessState::self()->startThreadPool();
android::ProcessState::self()->startThreadPool();
EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
.WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
Return(Error::NONE)));
// Primary display will be queried twice for all 5 attributes. One
// set of queries comes from the SurfaceFlinger proper an the
// other set from the VR composer.
// TODO: Is VR composer always present? Change to atLeast(5)?
EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
.Times(2 * 5)
.WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
startSurfaceFlinger();
// Fake composer wants to enable VSync injection
mMockComposer->onSurfaceFlingerStart();
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
mReceiver.reset(new DisplayEventReceiver());
mLooper = new Looper(false);
mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
}
void DisplayTest::TearDown() {
mLooper = nullptr;
mReceiver = nullptr;
mComposerClient->dispose();
mComposerClient = nullptr;
// Fake composer needs to release SurfaceComposerClient before the stop.
mMockComposer->onSurfaceFlingerStop();
stopSurfaceFlinger();
mFakeService = nullptr;
// TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
// management.
mMockComposer = nullptr;
}
int DisplayTest::processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
auto self = static_cast<DisplayTest*>(data);
ssize_t n;
DisplayEventReceiver::Event buffer[1];
while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
for (int i=0 ; i<n ; i++) {
self->mReceivedDisplayEvents.push_back(buffer[i]);
}
}
ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
return 1;
}
void DisplayTest::waitForDisplayTransaction() {
// Both a refresh and a vsync event are needed to apply pending display
// transactions.
mMockComposer->refreshDisplay(EXTERNAL_DISPLAY);
mMockComposer->runVSyncAndWait();
// Extra vsync and wait to avoid a 10% flake due to a race.
mMockComposer->runVSyncAndWait();
}
bool DisplayTest::waitForHotplugEvent(uint32_t id, bool connected) {
int waitCount = 20;
while (waitCount--) {
while (!mReceivedDisplayEvents.empty()) {
auto event = mReceivedDisplayEvents.front();
mReceivedDisplayEvents.pop_front();
ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
"event hotplug: id %d, connected %d\t", event.header.id,
event.hotplug.connected);
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
event.header.id == id && event.hotplug.connected == connected) {
return true;
}
}
mLooper->pollOnce(1);
}
return false;
}
TEST_F(DisplayTest, Hotplug) {
ALOGD("DisplayTest::Hotplug");
EXPECT_CALL(*mMockComposer, getDisplayType(EXTERNAL_DISPLAY, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
Return(Error::NONE)));
// The attribute queries will get done twice. This is for defaults
EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
.Times(2 * 3)
.WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
// ... and then special handling for dimensions. Specifying these
// rules later means that gmock will try them first, i.e.,
// ordering of width/height vs. the default implementation for
// other queries is significant.
EXPECT_CALL(*mMockComposer,
getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
EXPECT_CALL(*mMockComposer,
getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
waitForDisplayTransaction();
EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
{
sp<android::IBinder> display(
SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
DisplayInfo info;
SurfaceComposerClient::getDisplayInfo(display, &info);
ASSERT_EQ(400u, info.w);
ASSERT_EQ(200u, info.h);
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(surfaceControl != nullptr);
ASSERT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
{
TransactionScope ts(*mMockComposer);
ts.setDisplayLayerStack(display, 0);
ts.setLayer(surfaceControl, INT32_MAX - 2)
.show(surfaceControl);
}
}
mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
mMockComposer->clearFrames();
mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
waitForDisplayTransaction();
EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, false));
EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
{
sp<android::IBinder> display(
SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
DisplayInfo info;
SurfaceComposerClient::getDisplayInfo(display, &info);
ASSERT_EQ(400u, info.w);
ASSERT_EQ(200u, info.h);
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(surfaceControl != nullptr);
ASSERT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
{
TransactionScope ts(*mMockComposer);
ts.setDisplayLayerStack(display, 0);
ts.setLayer(surfaceControl, INT32_MAX - 2)
.show(surfaceControl);
}
}
mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
}
TEST_F(DisplayTest, HotplugPrimaryDisplay) {
ALOGD("DisplayTest::HotplugPrimaryDisplay");
mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
waitForDisplayTransaction();
EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, false));
{
sp<android::IBinder> display(
SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
DisplayInfo info;
auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
EXPECT_NE(NO_ERROR, result);
}
mMockComposer->clearFrames();
EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
Return(Error::NONE)));
// The attribute queries will get done twice. This is for defaults
EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
.Times(2 * 3)
.WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
// ... and then special handling for dimensions. Specifying these
// rules later means that gmock will try them first, i.e.,
// ordering of width/height vs. the default implementation for
// other queries is significant.
EXPECT_CALL(*mMockComposer,
getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
EXPECT_CALL(*mMockComposer,
getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
waitForDisplayTransaction();
EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, true));
{
sp<android::IBinder> display(
SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
DisplayInfo info;
auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
EXPECT_EQ(NO_ERROR, result);
ASSERT_EQ(400u, info.w);
ASSERT_EQ(200u, info.h);
}
}
////////////////////////////////////////////////
class TransactionTest : public ::testing::Test {
protected:
// Layer array indexing constants.
constexpr static int BG_LAYER = 0;
constexpr static int FG_LAYER = 1;
static void SetUpTestCase();
static void TearDownTestCase();
void SetUp() override;
void TearDown() override;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
std::vector<RenderState> mBaseFrame;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
static FakeComposerClient* sFakeComposer;
};
FakeComposerClient* TransactionTest::sFakeComposer;
void TransactionTest::SetUpTestCase() {
// TODO: See TODO comment at DisplayTest::SetUp for background on
// the lifetime of the FakeComposerClient.
sFakeComposer = new FakeComposerClient;
sp<ComposerClient> client = new ComposerClient(sFakeComposer);
sp<IComposer> fakeService = new FakeComposerService(client);
(void)fakeService->registerAsService("mock");
android::hardware::ProcessState::self()->startThreadPool();
android::ProcessState::self()->startThreadPool();
startSurfaceFlinger();
// Fake composer wants to enable VSync injection
sFakeComposer->onSurfaceFlingerStart();
}
void TransactionTest::TearDownTestCase() {
// Fake composer needs to release SurfaceComposerClient before the stop.
sFakeComposer->onSurfaceFlingerStop();
stopSurfaceFlinger();
// TODO: This is deleted when the ComposerClient calls
// removeClient. Devise better lifetime control.
sFakeComposer = nullptr;
}
void TransactionTest::SetUp() {
ALOGI("TransactionTest::SetUp");
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
ALOGI("TransactionTest::SetUp - display");
sp<android::IBinder> display(
SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
DisplayInfo info;
SurfaceComposerClient::getDisplayInfo(display, &info);
mDisplayWidth = info.w;
mDisplayHeight = info.h;
// Background surface
mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
// Foreground surface
mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mFGSurfaceControl != nullptr);
ASSERT_TRUE(mFGSurfaceControl->isValid());
fillSurfaceRGBA8(mFGSurfaceControl, RED);
Transaction t;
t.setDisplayLayerStack(display, 0);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
t.show(mBGSurfaceControl);
t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
t.setPosition(mFGSurfaceControl, 64, 64);
t.show(mFGSurfaceControl);
// Synchronous transaction will stop this thread, so we set up a
// delayed, off-thread vsync request before closing the
// transaction. In the test code this is usually done with
// TransactionScope. Leaving here in the 'vanilla' form for
// reference.
ASSERT_EQ(0, sFakeComposer->getFrameCount());
sFakeComposer->runVSyncAfter(1ms);
t.apply();
sFakeComposer->waitUntilFrame(1);
// Reference data. This is what the HWC should see.
static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
mBaseFrame[BG_LAYER].mSwapCount = 1;
mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
mBaseFrame[FG_LAYER].mSwapCount = 1;
auto frame = sFakeComposer->getFrameRects(0);
ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
}
void TransactionTest::TearDown() {
ALOGD("TransactionTest::TearDown");
mComposerClient->dispose();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
mComposerClient = 0;
sFakeComposer->runVSyncAndWait();
mBaseFrame.clear();
sFakeComposer->clearFrames();
ASSERT_EQ(0, sFakeComposer->getFrameCount());
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
std::vector<LayerDebugInfo> layers;
status_t result = sf->getLayerDebugInfo(&layers);
if (result != NO_ERROR) {
ALOGE("Failed to get layers %s %d", strerror(-result), result);
} else {
// If this fails, the test being torn down leaked layers.
EXPECT_EQ(0u, layers.size());
if (layers.size() > 0) {
for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
std::cout << to_string(*layer).c_str();
}
// To ensure the next test has clean slate, will run the class
// tear down and setup here.
TearDownTestCase();
SetUpTestCase();
}
}
ALOGD("TransactionTest::TearDown - complete");
}
TEST_F(TransactionTest, LayerMove) {
ALOGD("TransactionTest::LayerMove");
// The scope opens and closes a global transaction and, at the
// same time, makes sure the SurfaceFlinger progresses one frame
// after the transaction closes. The results of the transaction
// should be available in the latest frame stored by the fake
// composer.
{
TransactionScope ts(*sFakeComposer);
ts.setPosition(mFGSurfaceControl, 128, 128);
// NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
// (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
//
// sFakeComposer->runVSyncAndWait();
}
fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
sFakeComposer->runVSyncAndWait();
ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
// no extra frames.
// NOTE: Frame 0 is produced in the SetUp.
auto frame1Ref = mBaseFrame;
frame1Ref[FG_LAYER].mDisplayFrame =
hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
auto frame2Ref = frame1Ref;
frame2Ref[FG_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
}
TEST_F(TransactionTest, LayerResize) {
ALOGD("TransactionTest::LayerResize");
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
}
fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
sFakeComposer->runVSyncAndWait();
ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
// no extra frames.
auto frame1Ref = mBaseFrame;
// NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
auto frame2Ref = frame1Ref;
frame2Ref[FG_LAYER].mSwapCount++;
frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
}
TEST_F(TransactionTest, LayerCrop) {
// TODO: Add scaling to confirm that crop happens in buffer space?
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(16, 16, 32, 32);
ts.setCrop(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerFinalCrop) {
// TODO: Add scaling to confirm that crop happens in display space?
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(32, 32, 32 + 64, 32 + 64);
ts.setFinalCrop(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
// In display space we are cropping with [32, 32, 96, 96] against display rect
// [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96]
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerFinalCropEmpty) {
// TODO: Add scaling to confirm that crop happens in display space?
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(16, 16, 32, 32);
ts.setFinalCrop(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
// In display space we are cropping with [16, 16, 32, 32] against display rect
// [64, 64, 128, 128]. The intersection is empty and only the background layer is composited.
std::vector<RenderState> referenceFrame(1);
referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerSetLayer) {
{
TransactionScope ts(*sFakeComposer);
ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
// The layers will switch order, but both are rendered because the background layer is
// transparent (RGBA8888).
std::vector<RenderState> referenceFrame(2);
referenceFrame[0] = mBaseFrame[FG_LAYER];
referenceFrame[1] = mBaseFrame[BG_LAYER];
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerSetLayerOpaque) {
{
TransactionScope ts(*sFakeComposer);
ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
layer_state_t::eLayerOpaque);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
// The former foreground layer is now covered with opaque layer - it should have disappeared
std::vector<RenderState> referenceFrame(1);
referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, SetLayerStack) {
ALOGD("TransactionTest::SetLayerStack");
{
TransactionScope ts(*sFakeComposer);
ts.setLayerStack(mFGSurfaceControl, 1);
}
// Foreground layer should have disappeared.
ASSERT_EQ(2, sFakeComposer->getFrameCount());
std::vector<RenderState> refFrame(1);
refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerShowHide) {
ALOGD("TransactionTest::LayerShowHide");
{
TransactionScope ts(*sFakeComposer);
ts.hide(mFGSurfaceControl);
}
// Foreground layer should have disappeared.
ASSERT_EQ(2, sFakeComposer->getFrameCount());
std::vector<RenderState> refFrame(1);
refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.show(mFGSurfaceControl);
}
// Foreground layer should be back
ASSERT_EQ(3, sFakeComposer->getFrameCount());
EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerSetAlpha) {
{
TransactionScope ts(*sFakeComposer);
ts.setAlpha(mFGSurfaceControl, 0.75f);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerSetFlags) {
{
TransactionScope ts(*sFakeComposer);
ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
layer_state_t::eLayerHidden);
}
// Foreground layer should have disappeared.
ASSERT_EQ(2, sFakeComposer->getFrameCount());
std::vector<RenderState> refFrame(1);
refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, LayerSetMatrix) {
struct matrixTestData {
float matrix[4];
hwc_transform_t expectedTransform;
hwc_rect_t expectedDisplayFrame;
};
// The matrix operates on the display frame and is applied before
// the position is added. So, the foreground layer rect is (0, 0,
// 64, 64) is first transformed, potentially yielding negative
// coordinates and then the position (64, 64) is added yielding
// the final on-screen rectangles given.
const matrixTestData MATRIX_TESTS[7] = // clang-format off
{{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}},
{{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}},
{{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}},
{{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}},
{{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}},
{{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}},
{{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}};
// clang-format on
constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
for (int i = 0; i < TEST_COUNT; i++) {
// TODO: How to leverage the HWC2 stringifiers?
const matrixTestData& xform = MATRIX_TESTS[i];
SCOPED_TRACE(i);
{
TransactionScope ts(*sFakeComposer);
ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1],
xform.matrix[2], xform.matrix[3]);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
}
#if 0
TEST_F(TransactionTest, LayerSetMatrix2) {
{
TransactionScope ts(*sFakeComposer);
// TODO: PLEASE SPEC THE FUNCTION!
ts.setMatrix(mFGSurfaceControl, 0.11f, 0.123f,
-2.33f, 0.22f);
}
auto referenceFrame = mBaseFrame;
// TODO: Is this correct for sure?
//referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
#endif
TEST_F(TransactionTest, DeferredTransaction) {
// Synchronization surface
constexpr static int SYNC_LAYER = 2;
auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(syncSurfaceControl != nullptr);
ASSERT_TRUE(syncSurfaceControl->isValid());
fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
{
TransactionScope ts(*sFakeComposer);
ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
ts.show(syncSurfaceControl);
}
auto referenceFrame = mBaseFrame;
referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
mDisplayWidth - 1, mDisplayHeight - 1));
referenceFrame[SYNC_LAYER].mSwapCount = 1;
EXPECT_EQ(2, sFakeComposer->getFrameCount());
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
// set up two deferred transactions on different frames - these should not yield composited
// frames
{
TransactionScope ts(*sFakeComposer);
ts.setAlpha(mFGSurfaceControl, 0.75);
ts.deferTransactionUntil(mFGSurfaceControl,
syncSurfaceControl->getHandle(),
syncSurfaceControl->getSurface()->getNextFrameNumber());
}
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.setPosition(mFGSurfaceControl, 128, 128);
ts.deferTransactionUntil(mFGSurfaceControl,
syncSurfaceControl->getHandle(),
syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
}
EXPECT_EQ(4, sFakeComposer->getFrameCount());
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
// should trigger the first deferred transaction, but not the second one
fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
sFakeComposer->runVSyncAndWait();
EXPECT_EQ(5, sFakeComposer->getFrameCount());
referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
referenceFrame[SYNC_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
// should show up immediately since it's not deferred
{
TransactionScope ts(*sFakeComposer);
ts.setAlpha(mFGSurfaceControl, 1.0);
}
referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
EXPECT_EQ(6, sFakeComposer->getFrameCount());
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
// trigger the second deferred transaction
fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
sFakeComposer->runVSyncAndWait();
// TODO: Compute from layer size?
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
referenceFrame[SYNC_LAYER].mSwapCount++;
EXPECT_EQ(7, sFakeComposer->getFrameCount());
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(TransactionTest, SetRelativeLayer) {
constexpr int RELATIVE_LAYER = 2;
auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
PIXEL_FORMAT_RGBA_8888, 0);
fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
// Now we stack the surface above the foreground surface and make sure it is visible.
{
TransactionScope ts(*sFakeComposer);
ts.setPosition(relativeSurfaceControl, 64, 64);
ts.show(relativeSurfaceControl);
ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
}
auto referenceFrame = mBaseFrame;
// NOTE: All three layers will be visible as the surfaces are
// transparent because of the RGBA format.
referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
// A call to setLayer will override a call to setRelativeLayer
{
TransactionScope ts(*sFakeComposer);
ts.setLayer(relativeSurfaceControl, 0);
}
// Previous top layer will now appear at the bottom.
auto referenceFrame2 = mBaseFrame;
referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
EXPECT_EQ(3, sFakeComposer->getFrameCount());
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
class ChildLayerTest : public TransactionTest {
protected:
constexpr static int CHILD_LAYER = 2;
void SetUp() override {
TransactionTest::SetUp();
mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(mChild, LIGHT_GRAY);
sFakeComposer->runVSyncAndWait();
mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
mBaseFrame[CHILD_LAYER].mSwapCount = 1;
ASSERT_EQ(2, sFakeComposer->getFrameCount());
ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
}
void TearDown() override {
mChild = 0;
TransactionTest::TearDown();
}
sp<SurfaceControl> mChild;
};
TEST_F(ChildLayerTest, Positioning) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 10, 10);
// Move to the same position as in the original setup.
ts.setPosition(mFGSurfaceControl, 64, 64);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
referenceFrame[CHILD_LAYER].mDisplayFrame =
hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.setPosition(mFGSurfaceControl, 0, 0);
}
auto referenceFrame2 = mBaseFrame;
referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
referenceFrame2[CHILD_LAYER].mDisplayFrame =
hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, Cropping) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(mFGSurfaceControl, 0, 0);
ts.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
}
// NOTE: The foreground surface would be occluded by the child
// now, but is included in the stack because the child is
// transparent.
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, FinalCropping) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(mFGSurfaceControl, 0, 0);
ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, Constraints) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mFGSurfaceControl, 0, 0);
ts.setPosition(mChild, 63, 63);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, Scaling) {
{
TransactionScope ts(*sFakeComposer);
ts.setPosition(mFGSurfaceControl, 0, 0);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0);
}
auto referenceFrame2 = mBaseFrame;
referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, LayerAlpha) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(mFGSurfaceControl, 0, 0);
ts.setAlpha(mChild, 0.5);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.setAlpha(mFGSurfaceControl, 0.5);
}
auto referenceFrame2 = referenceFrame;
referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, ReparentChildren) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 10, 10);
ts.setPosition(mFGSurfaceControl, 64, 64);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
referenceFrame[CHILD_LAYER].mDisplayFrame =
hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
}
auto referenceFrame2 = referenceFrame;
referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, DetachChildren) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 10, 10);
ts.setPosition(mFGSurfaceControl, 64, 64);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
referenceFrame[CHILD_LAYER].mDisplayFrame =
hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.detachChildren(mFGSurfaceControl);
}
{
TransactionScope ts(*sFakeComposer);
ts.hide(mChild);
}
// Nothing should have changed. The child control becomes a no-op
// zombie on detach. See comments for detachChildren in the
// SurfaceControl.h file.
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(mFGSurfaceControl, 0, 0);
}
{
TransactionScope ts(*sFakeComposer);
ts.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
// We cause scaling by 2.
ts.setSize(mFGSurfaceControl, 128, 128);
}
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
// Regression test for b/37673612
TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(mFGSurfaceControl, 0, 0);
}
// We set things up as in b/37673612 so that there is a mismatch between the buffer size and
// the WM specified state size.
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 64);
}
sp<Surface> s = mFGSurfaceControl->getSurface();
auto anw = static_cast<ANativeWindow*>(s.get());
native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
native_window_set_buffers_dimensions(anw, 64, 128);
fillSurfaceRGBA8(mFGSurfaceControl, RED);
sFakeComposer->runVSyncAndWait();
// The child should still be in the same place and not have any strange scaling as in
// b/37673612.
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
referenceFrame[FG_LAYER].mSwapCount++;
referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
TEST_F(ChildLayerTest, Bug36858924) {
// Destroy the child layer
mChild.clear();
// Now recreate it as hidden
mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
mFGSurfaceControl.get());
// Show the child layer in a deferred transaction
{
TransactionScope ts(*sFakeComposer);
ts.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(),
mFGSurfaceControl->getSurface()->getNextFrameNumber());
ts.show(mChild);
}
// Render the foreground surface a few times
//
// Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
// frame because SurfaceFlinger would never process the deferred transaction and would therefore
// never acquire/release the first buffer
ALOGI("Filling 1");
fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
sFakeComposer->runVSyncAndWait();
ALOGI("Filling 2");
fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
sFakeComposer->runVSyncAndWait();
ALOGI("Filling 3");
fillSurfaceRGBA8(mFGSurfaceControl, RED);
sFakeComposer->runVSyncAndWait();
ALOGI("Filling 4");
fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
sFakeComposer->runVSyncAndWait();
}
class LatchingTest : public TransactionTest {
protected:
void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
void unlockFGBuffer() {
sp<Surface> s = mFGSurfaceControl->getSurface();
ASSERT_EQ(NO_ERROR, s->unlockAndPost());
sFakeComposer->runVSyncAndWait();
}
void completeFGResize() {
fillSurfaceRGBA8(mFGSurfaceControl, RED);
sFakeComposer->runVSyncAndWait();
}
void restoreInitialState() {
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 64, 64);
ts.setPosition(mFGSurfaceControl, 64, 64);
ts.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
}
};
TEST_F(LatchingTest, SurfacePositionLatching) {
// By default position can be updated even while
// a resize is pending.
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 32, 32);
ts.setPosition(mFGSurfaceControl, 100, 100);
}
// The size should not have updated as we have not provided a new buffer.
auto referenceFrame1 = mBaseFrame;
referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
restoreInitialState();
// Now we repeat with setGeometryAppliesWithResize
// and verify the position DOESN'T latch.
{
TransactionScope ts(*sFakeComposer);
ts.setGeometryAppliesWithResize(mFGSurfaceControl);
ts.setSize(mFGSurfaceControl, 32, 32);
ts.setPosition(mFGSurfaceControl, 100, 100);
}
EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
completeFGResize();
auto referenceFrame2 = mBaseFrame;
referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
referenceFrame2[FG_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
TEST_F(LatchingTest, CropLatching) {
// Normally the crop applies immediately even while a resize is pending.
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63));
}
auto referenceFrame1 = mBaseFrame;
referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
restoreInitialState();
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setGeometryAppliesWithResize(mFGSurfaceControl);
ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63));
}
EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
completeFGResize();
auto referenceFrame2 = mBaseFrame;
referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
referenceFrame2[FG_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
TEST_F(LatchingTest, FinalCropLatching) {
// Normally the crop applies immediately even while a resize is pending.
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
}
auto referenceFrame1 = mBaseFrame;
referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
referenceFrame1[FG_LAYER].mSourceCrop =
hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
restoreInitialState();
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setGeometryAppliesWithResize(mFGSurfaceControl);
ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
}
EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
completeFGResize();
auto referenceFrame2 = mBaseFrame;
referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
referenceFrame2[FG_LAYER].mSourceCrop =
hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
referenceFrame2[FG_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
// In this test we ensure that setGeometryAppliesWithResize actually demands
// a buffer of the new size, and not just any size.
TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) {
// Normally the crop applies immediately even while a resize is pending.
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
}
auto referenceFrame1 = mBaseFrame;
referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
referenceFrame1[FG_LAYER].mSourceCrop =
hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
restoreInitialState();
// In order to prepare to submit a buffer at the wrong size, we acquire it prior to
// initiating the resize.
lockAndFillFGBuffer();
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setGeometryAppliesWithResize(mFGSurfaceControl);
ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
}
EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
// We now submit our old buffer, at the old size, and ensure it doesn't
// trigger geometry latching.
unlockFGBuffer();
auto referenceFrame2 = mBaseFrame;
referenceFrame2[FG_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
completeFGResize();
auto referenceFrame3 = referenceFrame2;
referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
referenceFrame3[FG_LAYER].mSourceCrop =
hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
referenceFrame3[FG_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame()));
}
TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) {
// In this scenario, we attempt to set the final crop a second time while the resize
// is still pending, and ensure we are successful. Success meaning the second crop
// is the one which eventually latches and not the first.
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setGeometryAppliesWithResize(mFGSurfaceControl);
ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
}
{
TransactionScope ts(*sFakeComposer);
ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
}
EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
completeFGResize();
auto referenceFrame = mBaseFrame;
referenceFrame[FG_LAYER].mSwapCount++;
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
::testing::AddGlobalTestEnvironment(fakeEnvironment);
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}