| Kevin DuBois | 4df38a4 | 2019-02-14 12:59:43 -0800 | [diff] [blame] | 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 { | 
 | 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 |  | 
 | 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 { | 
 | 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 |  | 
 | 103 | private: | 
 | 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 |  | 
 | 138 | struct Listener : BnRegionSamplingListener { | 
 | 139 |     void onSampleCollected(float medianLuma) override { | 
 | 140 |         std::unique_lock<decltype(mutex)> lk(mutex); | 
 | 141 |         received = true; | 
 | 142 |         mLuma = medianLuma; | 
 | 143 |         cv.notify_all(); | 
 | 144 |     }; | 
 | 145 |     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 |  | 
 | 150 |     float luma() { | 
 | 151 |         std::unique_lock<decltype(mutex)> lk(mutex); | 
 | 152 |         return mLuma; | 
 | 153 |     } | 
 | 154 |  | 
 | 155 |     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: | 
 | 172 |     RegionSamplingTest() { ProcessState::self()->startThreadPool(); } | 
 | 173 |  | 
 | 174 |     static void SetUpTestSuite() { | 
 | 175 |         gChoreographerSim = ChoreographerSim::make(); | 
 | 176 |         ASSERT_NE(gChoreographerSim, nullptr); | 
 | 177 |     } | 
 | 178 |  | 
 | 179 |     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, | 
| Vishnu Nair | fa247b1 | 2020-02-11 08:58:26 -0800 | [diff] [blame] | 186 |                                                       ISurfaceComposerClient::eFXSurfaceEffect); | 
| Kevin DuBois | 4df38a4 | 2019-02-14 12:59:43 -0800 | [diff] [blame] | 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 |  | 
 | 214 |     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 |  | 
| Alec Mouri | 9a02eda | 2020-04-21 17:39:34 -0700 | [diff] [blame] | 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 |  | 
| Kevin DuBois | 9dcf049 | 2019-03-05 12:47:47 -0800 | [diff] [blame] | 256 | TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) { | 
| Kevin DuBois | 4df38a4 | 2019-02-14 12:59:43 -0800 | [diff] [blame] | 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 |  | 
| Kevin DuBois | 9dcf049 | 2019-03-05 12:47:47 -0800 | [diff] [blame] | 270 | TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) { | 
| Kevin DuBois | 4df38a4 | 2019-02-14 12:59:43 -0800 | [diff] [blame] | 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 |  | 
| Kevin DuBois | 9dcf049 | 2019-03-05 12:47:47 -0800 | [diff] [blame] | 291 | TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { | 
| Kevin DuBois | 4df38a4 | 2019-02-14 12:59:43 -0800 | [diff] [blame] | 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 |  | 
| tangrobin | acec532 | 2019-02-21 19:40:26 +0800 | [diff] [blame] | 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 |  | 
 | 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 |  | 
 | 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 |  | 
| Kevin DuBois | 4df38a4 | 2019-02-14 12:59:43 -0800 | [diff] [blame] | 379 | } // namespace android::test |