1 /*
2 * Copyright (C) 2014 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 "DrawFrameTask.h"
18
19 #include <dlfcn.h>
20 #include <gui/TraceUtils.h>
21 #include <utils/Log.h>
22 #include <algorithm>
23
24 #include "../DeferredLayerUpdater.h"
25 #include "../DisplayList.h"
26 #include "../Properties.h"
27 #include "../RenderNode.h"
28 #include "CanvasContext.h"
29 #include "RenderThread.h"
30 #include "thread/CommonPool.h"
31
32 namespace android {
33 namespace uirenderer {
34 namespace renderthread {
35
36 namespace {
37
38 typedef APerformanceHintManager* (*APH_getManager)();
39 typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
40 size_t, int64_t);
41 typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
42 typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
43 typedef void (*APH_closeSession)(APerformanceHintSession* session);
44
45 bool gAPerformanceHintBindingInitialized = false;
46 APH_getManager gAPH_getManagerFn = nullptr;
47 APH_createSession gAPH_createSessionFn = nullptr;
48 APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
49 APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
50 APH_closeSession gAPH_closeSessionFn = nullptr;
51
ensureAPerformanceHintBindingInitialized()52 void ensureAPerformanceHintBindingInitialized() {
53 if (gAPerformanceHintBindingInitialized) return;
54
55 void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
56 LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
57
58 gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
59 LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
60 "Failed to find required symbol APerformanceHint_getManager!");
61
62 gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
63 LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
64 "Failed to find required symbol APerformanceHint_createSession!");
65
66 gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
67 handle_, "APerformanceHint_updateTargetWorkDuration");
68 LOG_ALWAYS_FATAL_IF(
69 gAPH_updateTargetWorkDurationFn == nullptr,
70 "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
71
72 gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
73 handle_, "APerformanceHint_reportActualWorkDuration");
74 LOG_ALWAYS_FATAL_IF(
75 gAPH_reportActualWorkDurationFn == nullptr,
76 "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
77
78 gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
79 LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
80 "Failed to find required symbol APerformanceHint_closeSession!");
81
82 gAPerformanceHintBindingInitialized = true;
83 }
84
85 } // namespace
86
DrawFrameTask()87 DrawFrameTask::DrawFrameTask()
88 : mRenderThread(nullptr)
89 , mContext(nullptr)
90 , mContentDrawBounds(0, 0, 0, 0)
91 , mSyncResult(SyncResult::OK) {}
92
~DrawFrameTask()93 DrawFrameTask::~DrawFrameTask() {}
94
setContext(RenderThread * thread,CanvasContext * context,RenderNode * targetNode,int32_t uiThreadId,int32_t renderThreadId)95 void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
96 int32_t uiThreadId, int32_t renderThreadId) {
97 mRenderThread = thread;
98 mContext = context;
99 mTargetNode = targetNode;
100 mUiThreadId = uiThreadId;
101 mRenderThreadId = renderThreadId;
102 }
103
pushLayerUpdate(DeferredLayerUpdater * layer)104 void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
105 LOG_ALWAYS_FATAL_IF(!mContext,
106 "Lifecycle violation, there's no context to pushLayerUpdate with!");
107
108 for (size_t i = 0; i < mLayers.size(); i++) {
109 if (mLayers[i].get() == layer) {
110 return;
111 }
112 }
113 mLayers.push_back(layer);
114 }
115
removeLayerUpdate(DeferredLayerUpdater * layer)116 void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
117 for (size_t i = 0; i < mLayers.size(); i++) {
118 if (mLayers[i].get() == layer) {
119 mLayers.erase(mLayers.begin() + i);
120 return;
121 }
122 }
123 }
124
drawFrame()125 int DrawFrameTask::drawFrame() {
126 LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
127
128 mSyncResult = SyncResult::OK;
129 mSyncQueued = systemTime(SYSTEM_TIME_MONOTONIC);
130 postAndWait();
131
132 return mSyncResult;
133 }
134
postAndWait()135 void DrawFrameTask::postAndWait() {
136 AutoMutex _lock(mLock);
137 mRenderThread->queue().post([this]() { run(); });
138 mSignal.wait(mLock);
139 }
140
run()141 void DrawFrameTask::run() {
142 const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
143 ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
144 nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
145
146 bool canUnblockUiThread;
147 bool canDrawThisFrame;
148 {
149 TreeInfo info(TreeInfo::MODE_FULL, *mContext);
150 canUnblockUiThread = syncFrameState(info);
151 canDrawThisFrame = info.out.canDrawThisFrame;
152
153 if (mFrameCommitCallback) {
154 mContext->addFrameCommitListener(std::move(mFrameCommitCallback));
155 mFrameCommitCallback = nullptr;
156 }
157 }
158
159 // Grab a copy of everything we need
160 CanvasContext* context = mContext;
161 std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
162 std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
163 mFrameCallback = nullptr;
164 mFrameCompleteCallback = nullptr;
165 int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
166 int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
167 int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
168
169 // From this point on anything in "this" is *UNSAFE TO ACCESS*
170 if (canUnblockUiThread) {
171 unblockUiThread();
172 }
173
174 // Even if we aren't drawing this vsync pulse the next frame number will still be accurate
175 if (CC_UNLIKELY(frameCallback)) {
176 context->enqueueFrameWork(
177 [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); });
178 }
179
180 nsecs_t dequeueBufferDuration = 0;
181 if (CC_LIKELY(canDrawThisFrame)) {
182 dequeueBufferDuration = context->draw();
183 } else {
184 // Do a flush in case syncFrameState performed any texture uploads. Since we skipped
185 // the draw() call, those uploads (or deletes) will end up sitting in the queue.
186 // Do them now
187 if (GrDirectContext* grContext = mRenderThread->getGrContext()) {
188 grContext->flushAndSubmit();
189 }
190 // wait on fences so tasks don't overlap next frame
191 context->waitOnFences();
192 }
193
194 if (CC_UNLIKELY(frameCompleteCallback)) {
195 std::invoke(frameCompleteCallback);
196 }
197
198 if (!canUnblockUiThread) {
199 unblockUiThread();
200 }
201
202 if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
203 constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
204 constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
205 int64_t targetWorkDuration = frameDeadline - intendedVsync;
206 targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
207 if (targetWorkDuration > kSanityCheckLowerBound &&
208 targetWorkDuration < kSanityCheckUpperBound &&
209 targetWorkDuration != mLastTargetWorkDuration) {
210 mLastTargetWorkDuration = targetWorkDuration;
211 mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
212 }
213 int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
214 int64_t actualDuration = frameDuration -
215 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
216 dequeueBufferDuration;
217 if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
218 mHintSessionWrapper->reportActualWorkDuration(actualDuration);
219 }
220
221 mLastDequeueBufferDuration = dequeueBufferDuration;
222 }
223
syncFrameState(TreeInfo & info)224 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
225 ATRACE_CALL();
226 int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
227 int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
228 int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
229 int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
230 int64_t frameInterval = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameInterval)];
231 mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId, frameDeadline,
232 frameInterval);
233 bool canDraw = mContext->makeCurrent();
234 mContext->unpinImages();
235
236 for (size_t i = 0; i < mLayers.size(); i++) {
237 mLayers[i]->apply();
238 }
239 mLayers.clear();
240 mContext->setContentDrawBounds(mContentDrawBounds);
241 mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
242
243 // This is after the prepareTree so that any pending operations
244 // (RenderNode tree state, prefetched layers, etc...) will be flushed.
245 if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
246 if (!mContext->hasSurface()) {
247 mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
248 } else {
249 // If we have a surface but can't draw we must be stopped
250 mSyncResult |= SyncResult::ContextIsStopped;
251 }
252 info.out.canDrawThisFrame = false;
253 }
254
255 if (info.out.hasAnimations) {
256 if (info.out.requiresUiRedraw) {
257 mSyncResult |= SyncResult::UIRedrawRequired;
258 }
259 }
260 if (!info.out.canDrawThisFrame) {
261 mSyncResult |= SyncResult::FrameDropped;
262 }
263 // If prepareTextures is false, we ran out of texture cache space
264 return info.prepareTextures;
265 }
266
unblockUiThread()267 void DrawFrameTask::unblockUiThread() {
268 AutoMutex _lock(mLock);
269 mSignal.signal();
270 }
271
HintSessionWrapper(int32_t uiThreadId,int32_t renderThreadId)272 DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
273 if (!Properties::useHintManager) return;
274 if (uiThreadId < 0 || renderThreadId < 0) return;
275
276 ensureAPerformanceHintBindingInitialized();
277
278 APerformanceHintManager* manager = gAPH_getManagerFn();
279 if (!manager) return;
280
281 std::vector<int32_t> tids = CommonPool::getThreadIds();
282 tids.push_back(uiThreadId);
283 tids.push_back(renderThreadId);
284
285 // DrawFrameTask code will always set a target duration before reporting actual durations.
286 // So this is just a placeholder value that's never used.
287 int64_t dummyTargetDurationNanos = 16666667;
288 mHintSession =
289 gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
290 }
291
~HintSessionWrapper()292 DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
293 if (mHintSession) {
294 gAPH_closeSessionFn(mHintSession);
295 }
296 }
297
updateTargetWorkDuration(long targetDurationNanos)298 void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
299 if (mHintSession) {
300 gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
301 }
302 }
303
reportActualWorkDuration(long actualDurationNanos)304 void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
305 if (mHintSession) {
306 gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
307 }
308 }
309
310 } /* namespace renderthread */
311 } /* namespace uirenderer */
312 } /* namespace android */
313