• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #pragma clang diagnostic ignored "-Wextra"
21 
22 //#define LOG_NDEBUG 0
23 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
24 #undef LOG_TAG
25 #define LOG_TAG "RegionSamplingThread"
26 
27 #include "RegionSamplingThread.h"
28 
29 #include <compositionengine/Display.h>
30 #include <compositionengine/impl/OutputCompositionState.h>
31 #include <cutils/properties.h>
32 #include <ftl/future.h>
33 #include <gui/IRegionSamplingListener.h>
34 #include <gui/SyncScreenCaptureListener.h>
35 #include <ui/DisplayStatInfo.h>
36 #include <utils/Trace.h>
37 
38 #include <string>
39 
40 #include "DisplayDevice.h"
41 #include "DisplayRenderArea.h"
42 #include "Layer.h"
43 #include "Scheduler/VsyncController.h"
44 #include "SurfaceFlinger.h"
45 
46 namespace android {
47 using namespace std::chrono_literals;
48 
49 template <typename T>
50 struct SpHash {
operator ()android::SpHash51     size_t operator()(const sp<T>& p) const { return std::hash<T*>()(p.get()); }
52 };
53 
54 constexpr auto lumaSamplingStepTag = "LumaSamplingStep";
55 enum class samplingStep {
56     noWorkNeeded,
57     idleTimerWaiting,
58     waitForQuietFrame,
59     waitForSamplePhase,
60     sample
61 };
62 
63 constexpr auto defaultRegionSamplingWorkDuration = 3ms;
64 constexpr auto defaultRegionSamplingPeriod = 100ms;
65 constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
66 constexpr auto maxRegionSamplingDelay = 100ms;
67 // TODO: (b/127403193) duration to string conversion could probably be constexpr
68 template <typename Rep, typename Per>
toNsString(std::chrono::duration<Rep,Per> t)69 inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
70     return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(t).count());
71 }
72 
EnvironmentTimingTunables()73 RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
74     char value[PROPERTY_VALUE_MAX] = {};
75 
76     property_get("debug.sf.region_sampling_duration_ns", value,
77                  toNsString(defaultRegionSamplingWorkDuration).c_str());
78     int const samplingDurationNsRaw = atoi(value);
79 
80     property_get("debug.sf.region_sampling_period_ns", value,
81                  toNsString(defaultRegionSamplingPeriod).c_str());
82     int const samplingPeriodNsRaw = atoi(value);
83 
84     property_get("debug.sf.region_sampling_timer_timeout_ns", value,
85                  toNsString(defaultRegionSamplingTimerTimeout).c_str());
86     int const samplingTimerTimeoutNsRaw = atoi(value);
87 
88     if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
89         ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
90         mSamplingDuration = defaultRegionSamplingWorkDuration;
91         mSamplingPeriod = defaultRegionSamplingPeriod;
92         mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
93     } else {
94         mSamplingDuration = std::chrono::nanoseconds(samplingDurationNsRaw);
95         mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
96         mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
97     }
98 }
99 
RegionSamplingThread(SurfaceFlinger & flinger,const TimingTunables & tunables)100 RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables)
101       : mFlinger(flinger),
102         mTunables(tunables),
103         mIdleTimer(
104                 "RegSampIdle",
105                 std::chrono::duration_cast<std::chrono::milliseconds>(
106                         mTunables.mSamplingTimerTimeout),
107                 [] {}, [this] { checkForStaleLuma(); }),
108         mLastSampleTime(0ns) {
__anonaf11c5a60302() 109     mThread = std::thread([this]() { threadMain(); });
110     pthread_setname_np(mThread.native_handle(), "RegionSampling");
111     mIdleTimer.start();
112 }
113 
RegionSamplingThread(SurfaceFlinger & flinger)114 RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger)
115       : RegionSamplingThread(flinger,
116                              TimingTunables{defaultRegionSamplingWorkDuration,
117                                             defaultRegionSamplingPeriod,
118                                             defaultRegionSamplingTimerTimeout}) {}
119 
~RegionSamplingThread()120 RegionSamplingThread::~RegionSamplingThread() {
121     mIdleTimer.stop();
122 
123     {
124         std::lock_guard lock(mThreadControlMutex);
125         mRunning = false;
126         mCondition.notify_one();
127     }
128 
129     if (mThread.joinable()) {
130         mThread.join();
131     }
132 }
133 
addListener(const Rect & samplingArea,const wp<Layer> & stopLayer,const sp<IRegionSamplingListener> & listener)134 void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
135                                        const sp<IRegionSamplingListener>& listener) {
136     sp<IBinder> asBinder = IInterface::asBinder(listener);
137     asBinder->linkToDeath(this);
138     std::lock_guard lock(mSamplingMutex);
139     mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener});
140 }
141 
removeListener(const sp<IRegionSamplingListener> & listener)142 void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) {
143     std::lock_guard lock(mSamplingMutex);
144     mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener)));
145 }
146 
checkForStaleLuma()147 void RegionSamplingThread::checkForStaleLuma() {
148     std::lock_guard lock(mThreadControlMutex);
149 
150     if (mSampleRequestTime.has_value()) {
151         ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
152         mSampleRequestTime.reset();
153         mFlinger.scheduleRegionSamplingThread();
154     }
155 }
156 
onCompositionComplete(std::optional<std::chrono::steady_clock::time_point> samplingDeadline)157 void RegionSamplingThread::onCompositionComplete(
158         std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
159     doSample(samplingDeadline);
160 }
161 
doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline)162 void RegionSamplingThread::doSample(
163         std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
164     std::lock_guard lock(mThreadControlMutex);
165     const auto now = std::chrono::steady_clock::now();
166     if (mLastSampleTime + mTunables.mSamplingPeriod > now) {
167         // content changed, but we sampled not too long ago, so we need to sample some time in the
168         // future.
169         ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
170         mSampleRequestTime = now;
171         return;
172     }
173     if (!mSampleRequestTime.has_value() || now - *mSampleRequestTime < maxRegionSamplingDelay) {
174         // If there is relatively little time left for surfaceflinger
175         // until the next vsync deadline, defer this sampling work
176         // to a later frame, when hopefully there will be more time.
177         if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) {
178             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
179             mSampleRequestTime = mSampleRequestTime.value_or(now);
180             return;
181         }
182     }
183 
184     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
185 
186     mSampleRequestTime.reset();
187     mLastSampleTime = now;
188 
189     mIdleTimer.reset();
190 
191     mSampleRequested = true;
192     mCondition.notify_one();
193 }
194 
binderDied(const wp<IBinder> & who)195 void RegionSamplingThread::binderDied(const wp<IBinder>& who) {
196     std::lock_guard lock(mSamplingMutex);
197     mDescriptors.erase(who);
198 }
199 
sampleArea(const uint32_t * data,int32_t width,int32_t height,int32_t stride,uint32_t orientation,const Rect & sample_area)200 float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
201                  uint32_t orientation, const Rect& sample_area) {
202     if (!sample_area.isValid() || (sample_area.getWidth() > width) ||
203         (sample_area.getHeight() > height)) {
204         ALOGE("invalid sampling region requested");
205         return 0.0f;
206     }
207 
208     // (b/133849373) ROT_90 screencap images produced upside down
209     auto area = sample_area;
210     if (orientation & ui::Transform::ROT_90) {
211         area.top = height - area.top;
212         area.bottom = height - area.bottom;
213         std::swap(area.top, area.bottom);
214 
215         area.left = width - area.left;
216         area.right = width - area.right;
217         std::swap(area.left, area.right);
218     }
219 
220     const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
221     uint32_t accumulatedLuma = 0;
222 
223     // Calculates luma with approximation of Rec. 709 primaries
224     for (int32_t row = area.top; row < area.bottom; ++row) {
225         const uint32_t* rowBase = data + row * stride;
226         for (int32_t column = area.left; column < area.right; ++column) {
227             uint32_t pixel = rowBase[column];
228             const uint32_t r = pixel & 0xFF;
229             const uint32_t g = (pixel >> 8) & 0xFF;
230             const uint32_t b = (pixel >> 16) & 0xFF;
231             const uint32_t luma = (r * 7 + b * 2 + g * 23) >> 5;
232             accumulatedLuma += luma;
233         }
234     }
235 
236     return accumulatedLuma / (255.0f * pixelCount);
237 }
238 
sampleBuffer(const sp<GraphicBuffer> & buffer,const Point & leftTop,const std::vector<RegionSamplingThread::Descriptor> & descriptors,uint32_t orientation)239 std::vector<float> RegionSamplingThread::sampleBuffer(
240         const sp<GraphicBuffer>& buffer, const Point& leftTop,
241         const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation) {
242     void* data_raw = nullptr;
243     buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw);
244     std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw),
245                                    [&buffer](auto) { buffer->unlock(); });
246     if (!data) return {};
247 
248     const int32_t width = buffer->getWidth();
249     const int32_t height = buffer->getHeight();
250     const int32_t stride = buffer->getStride();
251     std::vector<float> lumas(descriptors.size());
252     std::transform(descriptors.begin(), descriptors.end(), lumas.begin(),
253                    [&](auto const& descriptor) {
254                        return sampleArea(data.get(), width, height, stride, orientation,
255                                          descriptor.area - leftTop);
256                    });
257     return lumas;
258 }
259 
captureSample()260 void RegionSamplingThread::captureSample() {
261     ATRACE_CALL();
262     std::lock_guard lock(mSamplingMutex);
263 
264     if (mDescriptors.empty()) {
265         return;
266     }
267 
268     wp<const DisplayDevice> displayWeak;
269 
270     ui::LayerStack layerStack;
271     ui::Transform::RotationFlags orientation;
272     ui::Size displaySize;
273 
274     {
275         // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread
276         const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice();
277         displayWeak = display;
278         layerStack = display->getLayerStack();
279         orientation = ui::Transform::toRotationFlags(display->getOrientation());
280         displaySize = display->getSize();
281     }
282 
283     std::vector<RegionSamplingThread::Descriptor> descriptors;
284     Region sampleRegion;
285     for (const auto& [listener, descriptor] : mDescriptors) {
286         sampleRegion.orSelf(descriptor.area);
287         descriptors.emplace_back(descriptor);
288     }
289 
290     const Rect sampledBounds = sampleRegion.bounds();
291     constexpr bool kUseIdentityTransform = false;
292 
293     SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
294         return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
295                                          ui::Dataspace::V0_SRGB, kUseIdentityTransform);
296     });
297 
298     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
299 
300     auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
301         bool stopLayerFound = false;
302         auto filterVisitor = [&](Layer* layer) {
303             // We don't want to capture any layers beyond the stop layer
304             if (stopLayerFound) return;
305 
306             // Likewise if we just found a stop layer, set the flag and abort
307             for (const auto& [area, stopLayer, listener] : descriptors) {
308                 if (layer == stopLayer.promote().get()) {
309                     stopLayerFound = true;
310                     return;
311                 }
312             }
313 
314             // Compute the layer's position on the screen
315             const Rect bounds = Rect(layer->getBounds());
316             const ui::Transform transform = layer->getTransform();
317             constexpr bool roundOutwards = true;
318             Rect transformed = transform.transform(bounds, roundOutwards);
319 
320             // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
321             Rect ignore;
322             if (!transformed.intersect(sampledBounds, &ignore)) return;
323 
324             // If the layer doesn't intersect a sampling area, skip capturing it
325             bool intersectsAnyArea = false;
326             for (const auto& [area, stopLayer, listener] : descriptors) {
327                 if (transformed.intersect(area, &ignore)) {
328                     intersectsAnyArea = true;
329                     listeners.insert(listener);
330                 }
331             }
332             if (!intersectsAnyArea) return;
333 
334             ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
335                   bounds.top, bounds.right, bounds.bottom);
336             visitor(layer);
337         };
338         mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
339     };
340 
341     std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
342     if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
343         mCachedBuffer->getBuffer()->getHeight() == sampledBounds.getHeight()) {
344         buffer = mCachedBuffer;
345     } else {
346         const uint32_t usage =
347                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
348         sp<GraphicBuffer> graphicBuffer =
349                 new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
350                                   PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
351         const status_t bufferStatus = graphicBuffer->initCheck();
352         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d",
353                             bufferStatus);
354         buffer = std::make_shared<
355                 renderengine::ExternalTexture>(graphicBuffer, mFlinger.getRenderEngine(),
356                                                renderengine::ExternalTexture::Usage::WRITEABLE);
357     }
358 
359     const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
360     mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
361                                  true /* regionSampling */, false /* grayscale */, captureListener);
362     ScreenCaptureResults captureResults = captureListener->waitForResults();
363 
364     std::vector<Descriptor> activeDescriptors;
365     for (const auto& descriptor : descriptors) {
366         if (listeners.count(descriptor.listener) != 0) {
367             activeDescriptors.emplace_back(descriptor);
368         }
369     }
370 
371     ALOGV("Sampling %zu descriptors", activeDescriptors.size());
372     std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(),
373                                             activeDescriptors, orientation);
374     if (lumas.size() != activeDescriptors.size()) {
375         ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
376               activeDescriptors.size());
377         return;
378     }
379 
380     for (size_t d = 0; d < activeDescriptors.size(); ++d) {
381         activeDescriptors[d].listener->onSampleCollected(lumas[d]);
382     }
383 
384     mCachedBuffer = buffer;
385     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
386 }
387 
388 // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
threadMain()389 void RegionSamplingThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
390     std::unique_lock<std::mutex> lock(mThreadControlMutex);
391     while (mRunning) {
392         if (mSampleRequested) {
393             mSampleRequested = false;
394             lock.unlock();
395             captureSample();
396             lock.lock();
397         }
398         mCondition.wait(lock, [this]() REQUIRES(mThreadControlMutex) {
399             return mSampleRequested || !mRunning;
400         });
401     }
402 }
403 
404 } // namespace android
405 
406 // TODO(b/129481165): remove the #pragma below and fix conversion issues
407 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
408