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 // TODO(b/129481165): remove the #pragma below and fix conversion issues
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wconversion"
20 
21 //#define LOG_NDEBUG 0
22 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
23 #undef LOG_TAG
24 #define LOG_TAG "RegionSamplingThread"
25 
26 #include "RegionSamplingThread.h"
27 
28 #include <compositionengine/Display.h>
29 #include <compositionengine/impl/OutputCompositionState.h>
30 #include <cutils/properties.h>
31 #include <gui/IRegionSamplingListener.h>
32 #include <ui/DisplayStatInfo.h>
33 #include <utils/Trace.h>
34 
35 #include <string>
36 
37 #include "DisplayDevice.h"
38 #include "Layer.h"
39 #include "Scheduler/DispSync.h"
40 #include "SurfaceFlinger.h"
41 
42 namespace android {
43 using namespace std::chrono_literals;
44 
45 template <typename T>
46 struct SpHash {
operator ()android::SpHash47     size_t operator()(const sp<T>& p) const { return std::hash<T*>()(p.get()); }
48 };
49 
50 constexpr auto lumaSamplingStepTag = "LumaSamplingStep";
51 enum class samplingStep {
52     noWorkNeeded,
53     idleTimerWaiting,
54     waitForQuietFrame,
55     waitForZeroPhase,
56     waitForSamplePhase,
57     sample
58 };
59 
60 constexpr auto timeForRegionSampling = 5000000ns;
61 constexpr auto maxRegionSamplingSkips = 10;
62 constexpr auto defaultRegionSamplingOffset = -3ms;
63 constexpr auto defaultRegionSamplingPeriod = 100ms;
64 constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
65 // TODO: (b/127403193) duration to string conversion could probably be constexpr
66 template <typename Rep, typename Per>
toNsString(std::chrono::duration<Rep,Per> t)67 inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
68     return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(t).count());
69 }
70 
EnvironmentTimingTunables()71 RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
72     char value[PROPERTY_VALUE_MAX] = {};
73 
74     property_get("debug.sf.region_sampling_offset_ns", value,
75                  toNsString(defaultRegionSamplingOffset).c_str());
76     int const samplingOffsetNsRaw = atoi(value);
77 
78     property_get("debug.sf.region_sampling_period_ns", value,
79                  toNsString(defaultRegionSamplingPeriod).c_str());
80     int const samplingPeriodNsRaw = atoi(value);
81 
82     property_get("debug.sf.region_sampling_timer_timeout_ns", value,
83                  toNsString(defaultRegionSamplingTimerTimeout).c_str());
84     int const samplingTimerTimeoutNsRaw = atoi(value);
85 
86     if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
87         ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
88         mSamplingOffset = defaultRegionSamplingOffset;
89         mSamplingPeriod = defaultRegionSamplingPeriod;
90         mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
91     } else {
92         mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
93         mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
94         mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
95     }
96 }
97 
98 struct SamplingOffsetCallback : DispSync::Callback {
SamplingOffsetCallbackandroid::SamplingOffsetCallback99     SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
100                            std::chrono::nanoseconds targetSamplingOffset)
101           : mRegionSamplingThread(samplingThread),
102             mScheduler(scheduler),
103             mTargetSamplingOffset(targetSamplingOffset) {}
104 
~SamplingOffsetCallbackandroid::SamplingOffsetCallback105     ~SamplingOffsetCallback() { stopVsyncListener(); }
106 
107     SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
108     SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
109 
startVsyncListenerandroid::SamplingOffsetCallback110     void startVsyncListener() {
111         std::lock_guard lock(mMutex);
112         if (mVsyncListening) return;
113 
114         mPhaseIntervalSetting = Phase::ZERO;
115         mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
116                                                          mLastCallbackTime);
117         mVsyncListening = true;
118     }
119 
stopVsyncListenerandroid::SamplingOffsetCallback120     void stopVsyncListener() {
121         std::lock_guard lock(mMutex);
122         stopVsyncListenerLocked();
123     }
124 
125 private:
stopVsyncListenerLockedandroid::SamplingOffsetCallback126     void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
127         if (!mVsyncListening) return;
128 
129         mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime);
130         mVsyncListening = false;
131     }
132 
onDispSyncEventandroid::SamplingOffsetCallback133     void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) final {
134         std::unique_lock<decltype(mMutex)> lock(mMutex);
135 
136         if (mPhaseIntervalSetting == Phase::ZERO) {
137             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
138             mPhaseIntervalSetting = Phase::SAMPLING;
139             mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count());
140             return;
141         }
142 
143         if (mPhaseIntervalSetting == Phase::SAMPLING) {
144             mPhaseIntervalSetting = Phase::ZERO;
145             mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0);
146             stopVsyncListenerLocked();
147             lock.unlock();
148             mRegionSamplingThread.notifySamplingOffset();
149             return;
150         }
151     }
152 
153     RegionSamplingThread& mRegionSamplingThread;
154     Scheduler& mScheduler;
155     const std::chrono::nanoseconds mTargetSamplingOffset;
156     mutable std::mutex mMutex;
157     nsecs_t mLastCallbackTime = 0;
158     enum class Phase {
159         ZERO,
160         SAMPLING
161     } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
162             = Phase::ZERO;
163     bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
164 };
165 
RegionSamplingThread(SurfaceFlinger & flinger,Scheduler & scheduler,const TimingTunables & tunables)166 RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
167                                            const TimingTunables& tunables)
168       : mFlinger(flinger),
169         mScheduler(scheduler),
170         mTunables(tunables),
171         mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
172                            mTunables.mSamplingTimerTimeout),
173                    [] {}, [this] { checkForStaleLuma(); }),
174         mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
175                                                                 tunables.mSamplingOffset)),
176         lastSampleTime(0ns) {
__anonedeb02ff0302() 177     mThread = std::thread([this]() { threadMain(); });
178     pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
179     mIdleTimer.start();
180 }
181 
RegionSamplingThread(SurfaceFlinger & flinger,Scheduler & scheduler)182 RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
183       : RegionSamplingThread(flinger, scheduler,
184                              TimingTunables{defaultRegionSamplingOffset,
185                                             defaultRegionSamplingPeriod,
186                                             defaultRegionSamplingTimerTimeout}) {}
187 
~RegionSamplingThread()188 RegionSamplingThread::~RegionSamplingThread() {
189     mIdleTimer.stop();
190 
191     {
192         std::lock_guard lock(mThreadControlMutex);
193         mRunning = false;
194         mCondition.notify_one();
195     }
196 
197     if (mThread.joinable()) {
198         mThread.join();
199     }
200 }
201 
addListener(const Rect & samplingArea,const wp<Layer> & stopLayer,const sp<IRegionSamplingListener> & listener)202 void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
203                                        const sp<IRegionSamplingListener>& listener) {
204     sp<IBinder> asBinder = IInterface::asBinder(listener);
205     asBinder->linkToDeath(this);
206     std::lock_guard lock(mSamplingMutex);
207     mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener});
208 }
209 
removeListener(const sp<IRegionSamplingListener> & listener)210 void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) {
211     std::lock_guard lock(mSamplingMutex);
212     mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener)));
213 }
214 
checkForStaleLuma()215 void RegionSamplingThread::checkForStaleLuma() {
216     std::lock_guard lock(mThreadControlMutex);
217 
218     if (mDiscardedFrames > 0) {
219         ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
220         mDiscardedFrames = 0;
221         mPhaseCallback->startVsyncListener();
222     }
223 }
224 
notifyNewContent()225 void RegionSamplingThread::notifyNewContent() {
226     doSample();
227 }
228 
notifySamplingOffset()229 void RegionSamplingThread::notifySamplingOffset() {
230     doSample();
231 }
232 
doSample()233 void RegionSamplingThread::doSample() {
234     std::lock_guard lock(mThreadControlMutex);
235     auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
236     if (lastSampleTime + mTunables.mSamplingPeriod > now) {
237         ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
238         if (mDiscardedFrames == 0) mDiscardedFrames++;
239         return;
240     }
241     if (mDiscardedFrames < maxRegionSamplingSkips) {
242         // If there is relatively little time left for surfaceflinger
243         // until the next vsync deadline, defer this sampling work
244         // to a later frame, when hopefully there will be more time.
245         DisplayStatInfo stats;
246         mScheduler.getDisplayStatInfo(&stats);
247         if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
248             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
249             mDiscardedFrames++;
250             return;
251         }
252     }
253 
254     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
255 
256     mDiscardedFrames = 0;
257     lastSampleTime = now;
258 
259     mIdleTimer.reset();
260     mPhaseCallback->stopVsyncListener();
261 
262     mSampleRequested = true;
263     mCondition.notify_one();
264 }
265 
binderDied(const wp<IBinder> & who)266 void RegionSamplingThread::binderDied(const wp<IBinder>& who) {
267     std::lock_guard lock(mSamplingMutex);
268     mDescriptors.erase(who);
269 }
270 
sampleArea(const uint32_t * data,int32_t width,int32_t height,int32_t stride,uint32_t orientation,const Rect & sample_area)271 float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
272                  uint32_t orientation, const Rect& sample_area) {
273     if (!sample_area.isValid() || (sample_area.getWidth() > width) ||
274         (sample_area.getHeight() > height)) {
275         ALOGE("invalid sampling region requested");
276         return 0.0f;
277     }
278 
279     // (b/133849373) ROT_90 screencap images produced upside down
280     auto area = sample_area;
281     if (orientation & ui::Transform::ROT_90) {
282         area.top = height - area.top;
283         area.bottom = height - area.bottom;
284         std::swap(area.top, area.bottom);
285 
286         area.left = width - area.left;
287         area.right = width - area.right;
288         std::swap(area.left, area.right);
289     }
290 
291     const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
292     uint32_t accumulatedLuma = 0;
293 
294     // Calculates luma with approximation of Rec. 709 primaries
295     for (int32_t row = area.top; row < area.bottom; ++row) {
296         const uint32_t* rowBase = data + row * stride;
297         for (int32_t column = area.left; column < area.right; ++column) {
298             uint32_t pixel = rowBase[column];
299             const uint32_t r = pixel & 0xFF;
300             const uint32_t g = (pixel >> 8) & 0xFF;
301             const uint32_t b = (pixel >> 16) & 0xFF;
302             const uint32_t luma = (r * 7 + b * 2 + g * 23) >> 5;
303             accumulatedLuma += luma;
304         }
305     }
306 
307     return accumulatedLuma / (255.0f * pixelCount);
308 }
309 
sampleBuffer(const sp<GraphicBuffer> & buffer,const Point & leftTop,const std::vector<RegionSamplingThread::Descriptor> & descriptors,uint32_t orientation)310 std::vector<float> RegionSamplingThread::sampleBuffer(
311         const sp<GraphicBuffer>& buffer, const Point& leftTop,
312         const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation) {
313     void* data_raw = nullptr;
314     buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw);
315     std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw),
316                                    [&buffer](auto) { buffer->unlock(); });
317     if (!data) return {};
318 
319     const int32_t width = buffer->getWidth();
320     const int32_t height = buffer->getHeight();
321     const int32_t stride = buffer->getStride();
322     std::vector<float> lumas(descriptors.size());
323     std::transform(descriptors.begin(), descriptors.end(), lumas.begin(),
324                    [&](auto const& descriptor) {
325                        return sampleArea(data.get(), width, height, stride, orientation,
326                                          descriptor.area - leftTop);
327                    });
328     return lumas;
329 }
330 
captureSample()331 void RegionSamplingThread::captureSample() {
332     ATRACE_CALL();
333     std::lock_guard lock(mSamplingMutex);
334 
335     if (mDescriptors.empty()) {
336         return;
337     }
338 
339     const auto device = mFlinger.getDefaultDisplayDevice();
340     const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
341 
342     std::vector<RegionSamplingThread::Descriptor> descriptors;
343     Region sampleRegion;
344     for (const auto& [listener, descriptor] : mDescriptors) {
345         sampleRegion.orSelf(descriptor.area);
346         descriptors.emplace_back(descriptor);
347     }
348 
349     const Rect sampledArea = sampleRegion.bounds();
350 
351     auto dx = 0;
352     auto dy = 0;
353     switch (orientation) {
354         case ui::Transform::ROT_90:
355             dx = device->getWidth();
356             break;
357         case ui::Transform::ROT_180:
358             dx = device->getWidth();
359             dy = device->getHeight();
360             break;
361         case ui::Transform::ROT_270:
362             dy = device->getHeight();
363             break;
364         default:
365             break;
366     }
367 
368     ui::Transform t(orientation);
369     auto screencapRegion = t.transform(sampleRegion);
370     screencapRegion = screencapRegion.translate(dx, dy);
371     DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(),
372                                  sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation);
373 
374     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
375 
376     auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
377         bool stopLayerFound = false;
378         auto filterVisitor = [&](Layer* layer) {
379             // We don't want to capture any layers beyond the stop layer
380             if (stopLayerFound) return;
381 
382             // Likewise if we just found a stop layer, set the flag and abort
383             for (const auto& [area, stopLayer, listener] : descriptors) {
384                 if (layer == stopLayer.promote().get()) {
385                     stopLayerFound = true;
386                     return;
387                 }
388             }
389 
390             // Compute the layer's position on the screen
391             const Rect bounds = Rect(layer->getBounds());
392             const ui::Transform transform = layer->getTransform();
393             constexpr bool roundOutwards = true;
394             Rect transformed = transform.transform(bounds, roundOutwards);
395 
396             // If this layer doesn't intersect with the larger sampledArea, skip capturing it
397             Rect ignore;
398             if (!transformed.intersect(sampledArea, &ignore)) return;
399 
400             // If the layer doesn't intersect a sampling area, skip capturing it
401             bool intersectsAnyArea = false;
402             for (const auto& [area, stopLayer, listener] : descriptors) {
403                 if (transformed.intersect(area, &ignore)) {
404                     intersectsAnyArea = true;
405                     listeners.insert(listener);
406                 }
407             }
408             if (!intersectsAnyArea) return;
409 
410             ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
411                   bounds.top, bounds.right, bounds.bottom);
412             visitor(layer);
413         };
414         mFlinger.traverseLayersInDisplay(device, filterVisitor);
415     };
416 
417     sp<GraphicBuffer> buffer = nullptr;
418     if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() &&
419         mCachedBuffer->getHeight() == sampledArea.getHeight()) {
420         buffer = mCachedBuffer;
421     } else {
422         const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
423         buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
424                                    PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
425     }
426 
427     bool ignored;
428     mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */,
429                                  true /* regionSampling */, ignored);
430 
431     std::vector<Descriptor> activeDescriptors;
432     for (const auto& descriptor : descriptors) {
433         if (listeners.count(descriptor.listener) != 0) {
434             activeDescriptors.emplace_back(descriptor);
435         }
436     }
437 
438     ALOGV("Sampling %zu descriptors", activeDescriptors.size());
439     std::vector<float> lumas =
440             sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation);
441     if (lumas.size() != activeDescriptors.size()) {
442         ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
443               activeDescriptors.size());
444         return;
445     }
446 
447     for (size_t d = 0; d < activeDescriptors.size(); ++d) {
448         activeDescriptors[d].listener->onSampleCollected(lumas[d]);
449     }
450 
451     // Extend the lifetime of mCachedBuffer from the previous frame to here to ensure that:
452     // 1) The region sampling thread is the last owner of the buffer, and the freeing of the buffer
453     // happens in this thread, as opposed to the main thread.
454     // 2) The listener(s) receive their notifications prior to freeing the buffer.
455     mCachedBuffer = buffer;
456     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
457 }
458 
459 // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
threadMain()460 void RegionSamplingThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
461     std::unique_lock<std::mutex> lock(mThreadControlMutex);
462     while (mRunning) {
463         if (mSampleRequested) {
464             mSampleRequested = false;
465             lock.unlock();
466             captureSample();
467             lock.lock();
468         }
469         mCondition.wait(lock, [this]() REQUIRES(mThreadControlMutex) {
470             return mSampleRequested || !mRunning;
471         });
472     }
473 }
474 
475 } // namespace android
476 
477 // TODO(b/129481165): remove the #pragma below and fix conversion issues
478 #pragma clang diagnostic pop // ignored "-Wconversion"
479