1 /*
2 * Copyright 2023 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 <gui/TraceUtils.h>
18
19 #include <common/FlagManager.h>
20 #include <scheduler/FrameTargeter.h>
21 #include <scheduler/IVsyncSource.h>
22
23 namespace android::scheduler {
24
FrameTarget(const std::string & displayLabel)25 FrameTarget::FrameTarget(const std::string& displayLabel)
26 : mFramePending("PrevFramePending " + displayLabel, false),
27 mFrameMissed("PrevFrameMissed " + displayLabel, false),
28 mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
29 mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
30
pastVsyncTime(Period minFramePeriod) const31 TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const {
32 // TODO(b/267315508): Generalize to N VSYNCs.
33 const int shift = static_cast<int>(targetsVsyncsAhead<2>(minFramePeriod));
34 return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
35 }
36
presentFenceForPastVsync(Period minFramePeriod) const37 FenceTimePtr FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
38 if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
39 return pastVsyncTimePtr();
40 }
41 const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
42 return mPresentFences[i].fenceTime;
43 }
44
wouldPresentEarly(Period minFramePeriod) const45 bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const {
46 // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
47 // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
48
49 // TODO(b/267315508): Generalize to N VSYNCs.
50 const bool allowNVsyncs = FlagManager::getInstance().allow_n_vsyncs_in_targeter();
51 if (!allowNVsyncs && targetsVsyncsAhead<3>(minFramePeriod)) {
52 return true;
53 }
54
55 const auto fence = presentFenceForPastVsync(minFramePeriod);
56 return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
57 }
58
beginFrame(const BeginFrameArgs & args,const IVsyncSource & vsyncSource)59 void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) {
60 return beginFrame(args, vsyncSource, &FrameTargeter::isFencePending);
61 }
62
beginFrame(const BeginFrameArgs & args,const IVsyncSource & vsyncSource,IsFencePendingFuncPtr isFencePendingFuncPtr)63 void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource,
64 IsFencePendingFuncPtr isFencePendingFuncPtr) {
65 mVsyncId = args.vsyncId;
66 mFrameBeginTime = args.frameBeginTime;
67
68 // The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in
69 // the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust
70 // `mExpectedPresentTime` accordingly, but not `mScheduledPresentTime`.
71 const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
72 mScheduledPresentTime = args.expectedVsyncTime;
73
74 const Period vsyncPeriod = vsyncSource.period();
75 const Period minFramePeriod = vsyncSource.minFramePeriod();
76
77 // Calculate the expected present time once and use the cached value throughout this frame to
78 // make sure all layers are seeing this same value.
79 if (args.expectedVsyncTime >= args.frameBeginTime) {
80 mExpectedPresentTime = args.expectedVsyncTime;
81 } else {
82 mExpectedPresentTime = vsyncSource.vsyncDeadlineAfter(args.frameBeginTime);
83 if (args.sfWorkDuration > vsyncPeriod) {
84 // Inflate the expected present time if we're targeting the next VSYNC.
85 mExpectedPresentTime += vsyncPeriod;
86 }
87 }
88
89 if (!mSupportsExpectedPresentTime) {
90 mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration);
91 }
92
93 ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
94 ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
95 mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
96
97 const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(minFramePeriod);
98
99 // In cases where the present fence is about to fire, give it a small grace period instead of
100 // giving up on the frame.
101 //
102 // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being
103 // approximately equal, not whether backpressure propagation is enabled.
104 const int graceTimeForPresentFenceMs = static_cast<int>(
105 mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
106
107 // Pending frames may trigger backpressure propagation.
108 const auto& isFencePending = *isFencePendingFuncPtr;
109 mFramePending = pastPresentFence != FenceTime::NO_FENCE &&
110 isFencePending(pastPresentFence, graceTimeForPresentFenceMs);
111
112 // A frame is missed if the prior frame is still pending. If no longer pending, then we still
113 // count the frame as missed if the predicted present time was further in the past than when the
114 // fence actually fired. Add some slop to correct for drift. This should generally be smaller
115 // than a typical frame duration, but should not be so small that it reports reasonable drift as
116 // a missed frame.
117 mFrameMissed = mFramePending || [&] {
118 const nsecs_t pastPresentTime = pastPresentFence->getSignalTime();
119 if (pastPresentTime < 0) return false;
120 mLastSignaledFrameTime = TimePoint::fromNs(pastPresentTime);
121 const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
122 return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
123 }();
124
125 mHwcFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Hwc);
126 mGpuFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Gpu);
127
128 if (mFrameMissed) mFrameMissedCount++;
129 if (mHwcFrameMissed) mHwcFrameMissedCount++;
130 if (mGpuFrameMissed) mGpuFrameMissedCount++;
131 }
132
computeEarliestPresentTime(Period minFramePeriod,Duration hwcMinWorkDuration)133 std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod,
134 Duration hwcMinWorkDuration) {
135 if (wouldPresentEarly(minFramePeriod)) {
136 return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration;
137 }
138 return {};
139 }
140
endFrame(const CompositeResult & result)141 void FrameTargeter::endFrame(const CompositeResult& result) {
142 mCompositionCoverage = result.compositionCoverage;
143 }
144
setPresentFence(sp<Fence> presentFence)145 FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence) {
146 auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
147 return setPresentFence(std::move(presentFence), std::move(presentFenceTime));
148 }
149
setPresentFence(sp<Fence> presentFence,FenceTimePtr presentFenceTime)150 FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime) {
151 if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
152 addFence(std::move(presentFence), presentFenceTime, mExpectedPresentTime);
153 } else {
154 mPresentFences[1] = mPresentFences[0];
155 mPresentFences[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
156 }
157 return presentFenceTime;
158 }
159
dump(utils::Dumper & dumper) const160 void FrameTargeter::dump(utils::Dumper& dumper) const {
161 // There are scripts and tests that expect this (rather than "name=value") format.
162 dumper.dump({}, "Total missed frame count: " + std::to_string(mFrameMissedCount));
163 dumper.dump({}, "HWC missed frame count: " + std::to_string(mHwcFrameMissedCount));
164 dumper.dump({}, "GPU missed frame count: " + std::to_string(mGpuFrameMissedCount));
165 }
166
isFencePending(const FenceTimePtr & fence,int graceTimeMs)167 bool FrameTargeter::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
168 ATRACE_CALL();
169 const status_t status = fence->wait(graceTimeMs);
170
171 // This is the same as Fence::Status::Unsignaled, but it saves a call to getStatus,
172 // which calls wait(0) again internally.
173 return status == -ETIME;
174 }
175
176 } // namespace android::scheduler
177