1 /*
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
20 #include <binder/ProcessState.h>
21 #include <gui/DisplayEventReceiver.h>
22 #include <gui/IRegionSamplingListener.h>
23 #include <gui/ISurfaceComposer.h>
24 #include <gui/Surface.h>
25 #include <gui/SurfaceComposerClient.h>
26 #include <private/gui/ComposerService.h>
27 #include <utils/Looper.h>
28
29 using namespace std::chrono_literals;
30
31 namespace android::test {
32
33 struct ChoreographerSync {
ChoreographerSyncandroid::test::ChoreographerSync34 ChoreographerSync(DisplayEventReceiver& receiver) : receiver_(receiver) {}
35 ~ChoreographerSync() = default;
36
notifyandroid::test::ChoreographerSync37 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
wait_vsync_notifyandroid::test::ChoreographerSync57 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
63 private:
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
73 struct ChoreographerSim {
makeandroid::test::ChoreographerSim74 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
~ChoreographerSimandroid::test::ChoreographerSim83 ~ChoreographerSim() {
84 poll_ = false;
85 looper->wake();
86 choreographer_thread_.join();
87 }
88
request_render_waitandroid::test::ChoreographerSim89 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
103 private:
ChoreographerSimandroid::test::ChoreographerSim104 ChoreographerSim(std::unique_ptr<DisplayEventReceiver> receiver)
105 : display_event_receiver_{std::move(receiver)},
106 choreographer_{*display_event_receiver_},
107 looper{new Looper(false)} {
__anon4d7e458f0302null108 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
138 struct Listener : BnRegionSamplingListener {
onSampleCollectedandroid::test::Listener139 void onSampleCollected(float medianLuma) override {
140 std::unique_lock<decltype(mutex)> lk(mutex);
141 received = true;
142 mLuma = medianLuma;
143 cv.notify_all();
144 };
wait_eventandroid::test::Listener145 bool wait_event(std::chrono::milliseconds timeout) {
146 std::unique_lock<decltype(mutex)> lk(mutex);
147 return cv.wait_for(lk, timeout, [this] { return received; });
148 }
149
lumaandroid::test::Listener150 float luma() {
151 std::unique_lock<decltype(mutex)> lk(mutex);
152 return mLuma;
153 }
154
resetandroid::test::Listener155 void reset() {
156 std::unique_lock<decltype(mutex)> lk(mutex);
157 received = false;
158 }
159
160 private:
161 std::condition_variable cv;
162 std::mutex mutex;
163 bool received = false;
164 float mLuma = -0.0f;
165 };
166
167 // Hoisted to TestSuite setup to avoid flake in test (b/124675919)
168 std::unique_ptr<ChoreographerSim> gChoreographerSim = nullptr;
169
170 struct RegionSamplingTest : ::testing::Test {
171 protected:
RegionSamplingTestandroid::test::RegionSamplingTest172 RegionSamplingTest() { ProcessState::self()->startThreadPool(); }
173
SetUpTestSuiteandroid::test::RegionSamplingTest174 static void SetUpTestSuite() {
175 gChoreographerSim = ChoreographerSim::make();
176 ASSERT_NE(gChoreographerSim, nullptr);
177 }
178
SetUpandroid::test::RegionSamplingTest179 void SetUp() override {
180 mSurfaceComposerClient = new SurfaceComposerClient;
181 ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
182
183 mBackgroundLayer =
184 mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0,
185 0, PIXEL_FORMAT_RGBA_8888,
186 ISurfaceComposerClient::eFXSurfaceEffect);
187 uint32_t layerPositionBottom = 0x7E000000;
188 SurfaceComposerClient::Transaction{}
189 .setLayer(mBackgroundLayer, layerPositionBottom)
190 .setPosition(mBackgroundLayer, 100, 100)
191 .setColor(mBackgroundLayer, half3{0.5, 0.5, 0.5})
192 .show(mBackgroundLayer)
193 .apply();
194
195 mContentLayer = mSurfaceComposerClient->createSurface(String8("Content RegionSamplingTest"),
196 300, 300, PIXEL_FORMAT_RGBA_8888, 0);
197
198 SurfaceComposerClient::Transaction{}
199 .setLayer(mContentLayer, layerPositionBottom + 1)
200 .setPosition(mContentLayer, 100, 100)
201 .setColor(mContentLayer, half3{0.5, 0.5, 0.5})
202 .show(mContentLayer)
203 .apply();
204
205 mTopLayer = mSurfaceComposerClient->createSurface(String8("TopLayer RegionSamplingTest"), 0,
206 0, PIXEL_FORMAT_RGBA_8888, 0);
207 SurfaceComposerClient::Transaction{}
208 .setLayer(mTopLayer, layerPositionBottom + 2)
209 .setPosition(mTopLayer, 0, 0)
210 .show(mBackgroundLayer)
211 .apply();
212 }
213
fill_renderandroid::test::RegionSamplingTest214 void fill_render(uint32_t rgba_value) {
215 auto surface = mContentLayer->getSurface();
216 ANativeWindow_Buffer outBuffer;
217 status_t status = surface->lock(&outBuffer, NULL);
218 ASSERT_EQ(status, android::OK);
219 auto b = reinterpret_cast<uint32_t*>(outBuffer.bits);
220 for (auto i = 0; i < outBuffer.height; i++) {
221 for (auto j = 0; j < outBuffer.width; j++) {
222 b[j] = rgba_value;
223 }
224 b += outBuffer.stride;
225 }
226
227 gChoreographerSim->request_render_wait([&surface] { surface->unlockAndPost(); });
228 }
229
230 sp<SurfaceComposerClient> mSurfaceComposerClient;
231 sp<SurfaceControl> mBackgroundLayer;
232 sp<SurfaceControl> mContentLayer;
233 sp<SurfaceControl> mTopLayer;
234
235 uint32_t const rgba_green = 0xFF00FF00;
236 float const luma_green = 0.7152;
237 uint32_t const rgba_blue = 0xFFFF0000;
238 float const luma_blue = 0.0722;
239 float const error_margin = 0.01;
240 float const luma_gray = 0.50;
241 };
242
TEST_F(RegionSamplingTest,invalidLayerHandle_doesNotCrash)243 TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) {
244 sp<ISurfaceComposer> composer = ComposerService::getComposerService();
245 sp<Listener> listener = new Listener();
246 const Rect sampleArea{100, 100, 200, 200};
247 // Passing in composer service as the layer handle should not crash, we'll
248 // treat it as a layer that no longer exists and silently allow sampling to
249 // occur.
250 status_t status = composer->addRegionSamplingListener(sampleArea,
251 IInterface::asBinder(composer), listener);
252 ASSERT_EQ(NO_ERROR, status);
253 composer->removeRegionSamplingListener(listener);
254 }
255
TEST_F(RegionSamplingTest,DISABLED_CollectsLuma)256 TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
257 fill_render(rgba_green);
258
259 sp<ISurfaceComposer> composer = ComposerService::getComposerService();
260 sp<Listener> listener = new Listener();
261 const Rect sampleArea{100, 100, 200, 200};
262 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
263
264 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
265 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
266
267 composer->removeRegionSamplingListener(listener);
268 }
269
TEST_F(RegionSamplingTest,DISABLED_CollectsChangingLuma)270 TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) {
271 fill_render(rgba_green);
272
273 sp<ISurfaceComposer> composer = ComposerService::getComposerService();
274 sp<Listener> listener = new Listener();
275 const Rect sampleArea{100, 100, 200, 200};
276 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
277
278 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
279 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
280
281 listener->reset();
282
283 fill_render(rgba_blue);
284 EXPECT_TRUE(listener->wait_event(300ms))
285 << "timed out waiting for 2nd luma event to be received";
286 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
287
288 composer->removeRegionSamplingListener(listener);
289 }
290
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromTwoRegions)291 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) {
292 fill_render(rgba_green);
293 sp<ISurfaceComposer> composer = ComposerService::getComposerService();
294 sp<Listener> greenListener = new Listener();
295 const Rect greenSampleArea{100, 100, 200, 200};
296 composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener);
297
298 sp<Listener> grayListener = new Listener();
299 const Rect graySampleArea{500, 100, 600, 200};
300 composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener);
301
302 EXPECT_TRUE(grayListener->wait_event(300ms))
303 << "timed out waiting for luma event to be received";
304 EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin);
305 EXPECT_TRUE(greenListener->wait_event(300ms))
306 << "timed out waiting for luma event to be received";
307 EXPECT_NEAR(greenListener->luma(), luma_green, error_margin);
308
309 composer->removeRegionSamplingListener(greenListener);
310 composer->removeRegionSamplingListener(grayListener);
311 }
312
TEST_F(RegionSamplingTest,DISABLED_TestIfInvalidInputParameters)313 TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) {
314 sp<ISurfaceComposer> composer = ComposerService::getComposerService();
315 sp<Listener> listener = new Listener();
316 const Rect sampleArea{100, 100, 200, 200};
317 // Invalid input sampleArea
318 EXPECT_EQ(BAD_VALUE,
319 composer->addRegionSamplingListener(Rect::INVALID_RECT, mTopLayer->getHandle(),
320 listener));
321 listener->reset();
322 // Invalid input binder
323 EXPECT_EQ(NO_ERROR, composer->addRegionSamplingListener(sampleArea, NULL, listener));
324 // Invalid input listener
325 EXPECT_EQ(BAD_VALUE,
326 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), NULL));
327 EXPECT_EQ(BAD_VALUE, composer->removeRegionSamplingListener(NULL));
328 // remove the listener
329 composer->removeRegionSamplingListener(listener);
330 }
331
TEST_F(RegionSamplingTest,DISABLED_TestCallbackAfterRemoveListener)332 TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) {
333 fill_render(rgba_green);
334 sp<ISurfaceComposer> composer = ComposerService::getComposerService();
335 sp<Listener> listener = new Listener();
336 const Rect sampleArea{100, 100, 200, 200};
337 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
338 fill_render(rgba_green);
339
340 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
341 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
342
343 listener->reset();
344 composer->removeRegionSamplingListener(listener);
345 fill_render(rgba_green);
346 EXPECT_FALSE(listener->wait_event(100ms))
347 << "callback should stop after remove the region sampling listener";
348 }
349
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromMovingLayer)350 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) {
351 sp<ISurfaceComposer> composer = ComposerService::getComposerService();
352 sp<Listener> listener = new Listener();
353 Rect sampleArea{100, 100, 200, 200};
354
355 // Test: listener in (100, 100). See layer before move, no layer after move.
356 fill_render(rgba_blue);
357 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
358 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
359 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
360 listener->reset();
361 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
362 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
363 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
364 composer->removeRegionSamplingListener(listener);
365
366 // Test: listener offset to (600, 600). No layer before move, see layer after move.
367 fill_render(rgba_green);
368 sampleArea.offsetTo(600, 600);
369 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
370 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
371 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
372 listener->reset();
373 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
374 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
375 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
376 composer->removeRegionSamplingListener(listener);
377 }
378
379 } // namespace android::test
380