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, 186 ISurfaceComposerClient::eFXSurfaceColor); 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 243 TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) { 244 fill_render(rgba_green); 245 246 sp<ISurfaceComposer> composer = ComposerService::getComposerService(); 247 sp<Listener> listener = new Listener(); 248 const Rect sampleArea{100, 100, 200, 200}; 249 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); 250 251 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; 252 EXPECT_NEAR(listener->luma(), luma_green, error_margin); 253 254 composer->removeRegionSamplingListener(listener); 255 } 256 257 TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) { 258 fill_render(rgba_green); 259 260 sp<ISurfaceComposer> composer = ComposerService::getComposerService(); 261 sp<Listener> listener = new Listener(); 262 const Rect sampleArea{100, 100, 200, 200}; 263 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); 264 265 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; 266 EXPECT_NEAR(listener->luma(), luma_green, error_margin); 267 268 listener->reset(); 269 270 fill_render(rgba_blue); 271 EXPECT_TRUE(listener->wait_event(300ms)) 272 << "timed out waiting for 2nd luma event to be received"; 273 EXPECT_NEAR(listener->luma(), luma_blue, error_margin); 274 275 composer->removeRegionSamplingListener(listener); 276 } 277 278 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { 279 fill_render(rgba_green); 280 sp<ISurfaceComposer> composer = ComposerService::getComposerService(); 281 sp<Listener> greenListener = new Listener(); 282 const Rect greenSampleArea{100, 100, 200, 200}; 283 composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener); 284 285 sp<Listener> grayListener = new Listener(); 286 const Rect graySampleArea{500, 100, 600, 200}; 287 composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener); 288 289 EXPECT_TRUE(grayListener->wait_event(300ms)) 290 << "timed out waiting for luma event to be received"; 291 EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin); 292 EXPECT_TRUE(greenListener->wait_event(300ms)) 293 << "timed out waiting for luma event to be received"; 294 EXPECT_NEAR(greenListener->luma(), luma_green, error_margin); 295 296 composer->removeRegionSamplingListener(greenListener); 297 composer->removeRegionSamplingListener(grayListener); 298 } 299 300 } // namespace android::test 301