blob: e6a9d6caafb46a99b6c155f75c47345c039ba00b [file] [log] [blame]
Kevin DuBois4df38a42019-02-14 12:59:43 -08001/*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <gtest/gtest.h>
18#include <thread>
19
Huihong Luoecc1f902021-11-20 11:55:05 -080020#include <android/gui/BnRegionSamplingListener.h>
Kevin DuBois4df38a42019-02-14 12:59:43 -080021#include <binder/ProcessState.h>
22#include <gui/DisplayEventReceiver.h>
Kevin DuBois4df38a42019-02-14 12:59:43 -080023#include <gui/ISurfaceComposer.h>
24#include <gui/Surface.h>
25#include <gui/SurfaceComposerClient.h>
Huihong Luo02186fb2022-02-23 14:21:54 -080026#include <private/gui/ComposerServiceAIDL.h>
Kevin DuBois4df38a42019-02-14 12:59:43 -080027#include <utils/Looper.h>
28
29using namespace std::chrono_literals;
30
31namespace android::test {
32
33struct ChoreographerSync {
34 ChoreographerSync(DisplayEventReceiver& receiver) : receiver_(receiver) {}
35 ~ChoreographerSync() = default;
36
37 void notify() const {
38 std::unique_lock<decltype(mutex_)> lk(mutex_);
39
40 auto check_event = [](auto const& ev) -> bool {
41 return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
42 };
43 DisplayEventReceiver::Event ev_;
44 int evs = receiver_.getEvents(&ev_, 1);
45 auto vsync_event_found = check_event(ev_);
46 while (evs) {
47 evs = receiver_.getEvents(&ev_, 1);
48 vsync_event_found |= check_event(ev_);
49 }
50
51 if (vsync_event_found) {
52 notification_arrived_ = true;
53 cv_.notify_all();
54 }
55 }
56
57 void wait_vsync_notify() const {
58 std::unique_lock<decltype(mutex_)> lk(mutex_);
59 cv_.wait(lk, [this] { return notification_arrived_; });
60 notification_arrived_ = false;
61 }
62
63private:
64 ChoreographerSync(ChoreographerSync const&) = delete;
65 ChoreographerSync& operator=(ChoreographerSync const&) = delete;
66
67 std::mutex mutable mutex_;
68 std::condition_variable mutable cv_;
69 bool mutable notification_arrived_ = false;
70 DisplayEventReceiver& receiver_;
71};
72
73struct ChoreographerSim {
74 static std::unique_ptr<ChoreographerSim> make() {
75 auto receiver = std::make_unique<DisplayEventReceiver>();
76 if (!receiver || receiver->initCheck() == NO_INIT) {
77 ALOGE("No display reciever");
78 return nullptr;
79 }
80 return std::unique_ptr<ChoreographerSim>(new ChoreographerSim(std::move(receiver)));
81 }
82
83 ~ChoreographerSim() {
84 poll_ = false;
85 looper->wake();
86 choreographer_thread_.join();
87 }
88
89 void request_render_wait(std::function<void()> const& render_fn) {
90 display_event_receiver_->requestNextVsync();
91 choreographer_.wait_vsync_notify();
92 render_fn();
93
94 // Purpose is to make sure that the content is latched by the time we sample.
95 // Waiting one vsync after queueing could still race with vsync, so wait for two, after
96 // which the content is pretty reliably on screen.
97 display_event_receiver_->requestNextVsync();
98 choreographer_.wait_vsync_notify();
99 display_event_receiver_->requestNextVsync();
100 choreographer_.wait_vsync_notify();
101 }
102
103private:
104 ChoreographerSim(std::unique_ptr<DisplayEventReceiver> receiver)
105 : display_event_receiver_{std::move(receiver)},
106 choreographer_{*display_event_receiver_},
107 looper{new Looper(false)} {
108 choreographer_thread_ = std::thread([this] {
109 auto vsync_notify_fd = display_event_receiver_->getFd();
110 looper->addFd(vsync_notify_fd, 0, Looper::EVENT_INPUT,
111 [](int /*fd*/, int /*events*/, void* data) -> int {
112 if (!data) return 0;
113 reinterpret_cast<ChoreographerSync*>(data)->notify();
114 return 1;
115 },
116 const_cast<void*>(reinterpret_cast<void const*>(&choreographer_)));
117
118 while (poll_) {
119 auto const poll_interval =
120 std::chrono::duration_cast<std::chrono::milliseconds>(1s).count();
121 auto rc = looper->pollOnce(poll_interval);
122 if ((rc != Looper::POLL_CALLBACK) && (rc != Looper::POLL_WAKE))
123 ALOGW("Vsync Looper returned: %i\n", rc);
124 }
125 });
126 }
127
128 ChoreographerSim(ChoreographerSim const&) = delete;
129 ChoreographerSim& operator=(ChoreographerSim const&) = delete;
130
131 std::unique_ptr<DisplayEventReceiver> const display_event_receiver_;
132 ChoreographerSync const choreographer_;
133 sp<Looper> looper;
134 std::thread choreographer_thread_;
135 std::atomic<bool> poll_{true};
136};
137
Huihong Luoecc1f902021-11-20 11:55:05 -0800138struct Listener : android::gui::BnRegionSamplingListener {
139 binder::Status onSampleCollected(float medianLuma) override {
Kevin DuBois4df38a42019-02-14 12:59:43 -0800140 std::unique_lock<decltype(mutex)> lk(mutex);
141 received = true;
142 mLuma = medianLuma;
143 cv.notify_all();
Huihong Luoecc1f902021-11-20 11:55:05 -0800144 return binder::Status::ok();
Kevin DuBois4df38a42019-02-14 12:59:43 -0800145 };
146 bool wait_event(std::chrono::milliseconds timeout) {
147 std::unique_lock<decltype(mutex)> lk(mutex);
148 return cv.wait_for(lk, timeout, [this] { return received; });
149 }
150
151 float luma() {
152 std::unique_lock<decltype(mutex)> lk(mutex);
153 return mLuma;
154 }
155
156 void reset() {
157 std::unique_lock<decltype(mutex)> lk(mutex);
158 received = false;
159 }
160
161private:
162 std::condition_variable cv;
163 std::mutex mutex;
164 bool received = false;
165 float mLuma = -0.0f;
166};
167
168// Hoisted to TestSuite setup to avoid flake in test (b/124675919)
169std::unique_ptr<ChoreographerSim> gChoreographerSim = nullptr;
170
171struct RegionSamplingTest : ::testing::Test {
172protected:
173 RegionSamplingTest() { ProcessState::self()->startThreadPool(); }
174
175 static void SetUpTestSuite() {
176 gChoreographerSim = ChoreographerSim::make();
177 ASSERT_NE(gChoreographerSim, nullptr);
178 }
179
180 void SetUp() override {
181 mSurfaceComposerClient = new SurfaceComposerClient;
182 ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
183
184 mBackgroundLayer =
185 mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0,
186 0, PIXEL_FORMAT_RGBA_8888,
Vishnu Nairfa247b12020-02-11 08:58:26 -0800187 ISurfaceComposerClient::eFXSurfaceEffect);
Kevin DuBois4df38a42019-02-14 12:59:43 -0800188 uint32_t layerPositionBottom = 0x7E000000;
189 SurfaceComposerClient::Transaction{}
190 .setLayer(mBackgroundLayer, layerPositionBottom)
191 .setPosition(mBackgroundLayer, 100, 100)
192 .setColor(mBackgroundLayer, half3{0.5, 0.5, 0.5})
193 .show(mBackgroundLayer)
194 .apply();
195
196 mContentLayer = mSurfaceComposerClient->createSurface(String8("Content RegionSamplingTest"),
197 300, 300, PIXEL_FORMAT_RGBA_8888, 0);
198
199 SurfaceComposerClient::Transaction{}
200 .setLayer(mContentLayer, layerPositionBottom + 1)
201 .setPosition(mContentLayer, 100, 100)
202 .setColor(mContentLayer, half3{0.5, 0.5, 0.5})
203 .show(mContentLayer)
204 .apply();
205
206 mTopLayer = mSurfaceComposerClient->createSurface(String8("TopLayer RegionSamplingTest"), 0,
207 0, PIXEL_FORMAT_RGBA_8888, 0);
208 SurfaceComposerClient::Transaction{}
209 .setLayer(mTopLayer, layerPositionBottom + 2)
210 .setPosition(mTopLayer, 0, 0)
211 .show(mBackgroundLayer)
212 .apply();
213 }
214
215 void fill_render(uint32_t rgba_value) {
216 auto surface = mContentLayer->getSurface();
217 ANativeWindow_Buffer outBuffer;
218 status_t status = surface->lock(&outBuffer, NULL);
219 ASSERT_EQ(status, android::OK);
220 auto b = reinterpret_cast<uint32_t*>(outBuffer.bits);
221 for (auto i = 0; i < outBuffer.height; i++) {
222 for (auto j = 0; j < outBuffer.width; j++) {
223 b[j] = rgba_value;
224 }
225 b += outBuffer.stride;
226 }
227
228 gChoreographerSim->request_render_wait([&surface] { surface->unlockAndPost(); });
229 }
230
231 sp<SurfaceComposerClient> mSurfaceComposerClient;
232 sp<SurfaceControl> mBackgroundLayer;
233 sp<SurfaceControl> mContentLayer;
234 sp<SurfaceControl> mTopLayer;
235
236 uint32_t const rgba_green = 0xFF00FF00;
237 float const luma_green = 0.7152;
238 uint32_t const rgba_blue = 0xFFFF0000;
239 float const luma_blue = 0.0722;
240 float const error_margin = 0.01;
241 float const luma_gray = 0.50;
242};
243
Alec Mouri9a02eda2020-04-21 17:39:34 -0700244TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) {
Huihong Luo02186fb2022-02-23 14:21:54 -0800245 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
Alec Mouri9a02eda2020-04-21 17:39:34 -0700246 sp<Listener> listener = new Listener();
Huihong Luo02186fb2022-02-23 14:21:54 -0800247 gui::ARect sampleArea;
248 sampleArea.left = 100;
249 sampleArea.top = 100;
250 sampleArea.right = 200;
251 sampleArea.bottom = 200;
Alec Mouri9a02eda2020-04-21 17:39:34 -0700252 // Passing in composer service as the layer handle should not crash, we'll
253 // treat it as a layer that no longer exists and silently allow sampling to
254 // occur.
Huihong Luo02186fb2022-02-23 14:21:54 -0800255 binder::Status status =
256 composer->addRegionSamplingListener(sampleArea, IInterface::asBinder(composer),
257 listener);
258 ASSERT_EQ(NO_ERROR, status.transactionError());
Alec Mouri9a02eda2020-04-21 17:39:34 -0700259 composer->removeRegionSamplingListener(listener);
260}
261
Kevin DuBois9dcf0492019-03-05 12:47:47 -0800262TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
Kevin DuBois4df38a42019-02-14 12:59:43 -0800263 fill_render(rgba_green);
264
Huihong Luo02186fb2022-02-23 14:21:54 -0800265 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
Kevin DuBois4df38a42019-02-14 12:59:43 -0800266 sp<Listener> listener = new Listener();
Huihong Luo02186fb2022-02-23 14:21:54 -0800267 gui::ARect sampleArea;
268 sampleArea.left = 100;
269 sampleArea.top = 100;
270 sampleArea.right = 200;
271 sampleArea.bottom = 200;
Kevin DuBois4df38a42019-02-14 12:59:43 -0800272 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
273
274 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
275 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
276
277 composer->removeRegionSamplingListener(listener);
278}
279
Kevin DuBois9dcf0492019-03-05 12:47:47 -0800280TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) {
Kevin DuBois4df38a42019-02-14 12:59:43 -0800281 fill_render(rgba_green);
282
Huihong Luo02186fb2022-02-23 14:21:54 -0800283 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
Kevin DuBois4df38a42019-02-14 12:59:43 -0800284 sp<Listener> listener = new Listener();
Huihong Luo02186fb2022-02-23 14:21:54 -0800285 gui::ARect sampleArea;
286 sampleArea.left = 100;
287 sampleArea.top = 100;
288 sampleArea.right = 200;
289 sampleArea.bottom = 200;
Kevin DuBois4df38a42019-02-14 12:59:43 -0800290 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
291
292 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
293 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
294
295 listener->reset();
296
297 fill_render(rgba_blue);
298 EXPECT_TRUE(listener->wait_event(300ms))
299 << "timed out waiting for 2nd luma event to be received";
300 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
301
302 composer->removeRegionSamplingListener(listener);
303}
304
Kevin DuBois9dcf0492019-03-05 12:47:47 -0800305TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) {
Kevin DuBois4df38a42019-02-14 12:59:43 -0800306 fill_render(rgba_green);
Huihong Luo02186fb2022-02-23 14:21:54 -0800307 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
Kevin DuBois4df38a42019-02-14 12:59:43 -0800308 sp<Listener> greenListener = new Listener();
Huihong Luo02186fb2022-02-23 14:21:54 -0800309 gui::ARect greenSampleArea;
310 greenSampleArea.left = 100;
311 greenSampleArea.top = 100;
312 greenSampleArea.right = 200;
313 greenSampleArea.bottom = 200;
Kevin DuBois4df38a42019-02-14 12:59:43 -0800314 composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener);
315
316 sp<Listener> grayListener = new Listener();
Huihong Luo02186fb2022-02-23 14:21:54 -0800317 gui::ARect graySampleArea;
318 graySampleArea.left = 500;
319 graySampleArea.top = 100;
320 graySampleArea.right = 600;
321 graySampleArea.bottom = 200;
Kevin DuBois4df38a42019-02-14 12:59:43 -0800322 composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener);
323
324 EXPECT_TRUE(grayListener->wait_event(300ms))
325 << "timed out waiting for luma event to be received";
326 EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin);
327 EXPECT_TRUE(greenListener->wait_event(300ms))
328 << "timed out waiting for luma event to be received";
329 EXPECT_NEAR(greenListener->luma(), luma_green, error_margin);
330
331 composer->removeRegionSamplingListener(greenListener);
332 composer->removeRegionSamplingListener(grayListener);
333}
334
tangrobinacec5322019-02-21 19:40:26 +0800335TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) {
Huihong Luo02186fb2022-02-23 14:21:54 -0800336 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
tangrobinacec5322019-02-21 19:40:26 +0800337 sp<Listener> listener = new Listener();
Huihong Luo02186fb2022-02-23 14:21:54 -0800338
339 gui::ARect invalidRect;
340 invalidRect.left = Rect::INVALID_RECT.left;
341 invalidRect.top = Rect::INVALID_RECT.top;
342 invalidRect.right = Rect::INVALID_RECT.right;
343 invalidRect.bottom = Rect::INVALID_RECT.bottom;
344
345 gui::ARect sampleArea;
346 sampleArea.left = 100;
347 sampleArea.top = 100;
348 sampleArea.right = 200;
349 sampleArea.bottom = 200;
tangrobinacec5322019-02-21 19:40:26 +0800350 // Invalid input sampleArea
351 EXPECT_EQ(BAD_VALUE,
Huihong Luo02186fb2022-02-23 14:21:54 -0800352 composer->addRegionSamplingListener(invalidRect, mTopLayer->getHandle(), listener)
353 .transactionError());
tangrobinacec5322019-02-21 19:40:26 +0800354 listener->reset();
355 // Invalid input binder
Huihong Luo02186fb2022-02-23 14:21:54 -0800356 EXPECT_EQ(NO_ERROR,
357 composer->addRegionSamplingListener(sampleArea, NULL, listener).transactionError());
tangrobinacec5322019-02-21 19:40:26 +0800358 // Invalid input listener
359 EXPECT_EQ(BAD_VALUE,
Huihong Luo02186fb2022-02-23 14:21:54 -0800360 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), NULL)
361 .transactionError());
362 EXPECT_EQ(BAD_VALUE, composer->removeRegionSamplingListener(NULL).transactionError());
tangrobinacec5322019-02-21 19:40:26 +0800363 // remove the listener
364 composer->removeRegionSamplingListener(listener);
365}
366
367TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) {
368 fill_render(rgba_green);
Huihong Luo02186fb2022-02-23 14:21:54 -0800369 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
tangrobinacec5322019-02-21 19:40:26 +0800370 sp<Listener> listener = new Listener();
Huihong Luo02186fb2022-02-23 14:21:54 -0800371 gui::ARect sampleArea;
372 sampleArea.left = 100;
373 sampleArea.top = 100;
374 sampleArea.right = 200;
375 sampleArea.bottom = 200;
tangrobinacec5322019-02-21 19:40:26 +0800376 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
377 fill_render(rgba_green);
378
379 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
380 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
381
382 listener->reset();
383 composer->removeRegionSamplingListener(listener);
384 fill_render(rgba_green);
385 EXPECT_FALSE(listener->wait_event(100ms))
386 << "callback should stop after remove the region sampling listener";
387}
388
389TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) {
Huihong Luo02186fb2022-02-23 14:21:54 -0800390 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
tangrobinacec5322019-02-21 19:40:26 +0800391 sp<Listener> listener = new Listener();
392 Rect sampleArea{100, 100, 200, 200};
Huihong Luo02186fb2022-02-23 14:21:54 -0800393 gui::ARect sampleAreaA;
394 sampleAreaA.left = sampleArea.left;
395 sampleAreaA.top = sampleArea.top;
396 sampleAreaA.right = sampleArea.right;
397 sampleAreaA.bottom = sampleArea.bottom;
tangrobinacec5322019-02-21 19:40:26 +0800398
399 // Test: listener in (100, 100). See layer before move, no layer after move.
400 fill_render(rgba_blue);
Huihong Luo02186fb2022-02-23 14:21:54 -0800401 composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
tangrobinacec5322019-02-21 19:40:26 +0800402 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
403 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
404 listener->reset();
405 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
406 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
407 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
408 composer->removeRegionSamplingListener(listener);
409
410 // Test: listener offset to (600, 600). No layer before move, see layer after move.
411 fill_render(rgba_green);
412 sampleArea.offsetTo(600, 600);
Huihong Luo02186fb2022-02-23 14:21:54 -0800413 sampleAreaA.left = sampleArea.left;
414 sampleAreaA.top = sampleArea.top;
415 sampleAreaA.right = sampleArea.right;
416 sampleAreaA.bottom = sampleArea.bottom;
417 composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
tangrobinacec5322019-02-21 19:40:26 +0800418 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
419 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
420 listener->reset();
421 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
422 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
423 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
424 composer->removeRegionSamplingListener(listener);
425}
426
Kevin DuBois4df38a42019-02-14 12:59:43 -0800427} // namespace android::test