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 ATRACE_CALL();
137 AutoMutex _lock(mLock);
138 mRenderThread->queue().post([this]() { run(); });
139 mSignal.wait(mLock);
140 }
141
run()142 void DrawFrameTask::run() {
143 const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
144 ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
145 nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
146
147 bool canUnblockUiThread;
148 bool canDrawThisFrame;
149 {
150 TreeInfo info(TreeInfo::MODE_FULL, *mContext);
151 info.forceDrawFrame = mForceDrawFrame;
152 mForceDrawFrame = false;
153 canUnblockUiThread = syncFrameState(info);
154 canDrawThisFrame = info.out.canDrawThisFrame;
155
156 if (mFrameCommitCallback) {
157 mContext->addFrameCommitListener(std::move(mFrameCommitCallback));
158 mFrameCommitCallback = nullptr;
159 }
160 }
161
162 // Grab a copy of everything we need
163 CanvasContext* context = mContext;
164 std::function<std::function<void(bool)>(int32_t, int64_t)> frameCallback =
165 std::move(mFrameCallback);
166 std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
167 mFrameCallback = nullptr;
168 mFrameCompleteCallback = nullptr;
169 int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
170 int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
171 int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
172
173 // From this point on anything in "this" is *UNSAFE TO ACCESS*
174 if (canUnblockUiThread) {
175 unblockUiThread();
176 }
177
178 // Even if we aren't drawing this vsync pulse the next frame number will still be accurate
179 if (CC_UNLIKELY(frameCallback)) {
180 context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
181 frameNr = context->getFrameNumber()]() {
182 auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
183 if (frameCommitCallback) {
184 context->addFrameCommitListener(std::move(frameCommitCallback));
185 }
186 });
187 }
188
189 nsecs_t dequeueBufferDuration = 0;
190 if (CC_LIKELY(canDrawThisFrame)) {
191 dequeueBufferDuration = context->draw();
192 } else {
193 // Do a flush in case syncFrameState performed any texture uploads. Since we skipped
194 // the draw() call, those uploads (or deletes) will end up sitting in the queue.
195 // Do them now
196 if (GrDirectContext* grContext = mRenderThread->getGrContext()) {
197 grContext->flushAndSubmit();
198 }
199 // wait on fences so tasks don't overlap next frame
200 context->waitOnFences();
201 }
202
203 if (CC_UNLIKELY(frameCompleteCallback)) {
204 std::invoke(frameCompleteCallback);
205 }
206
207 if (!canUnblockUiThread) {
208 unblockUiThread();
209 }
210
211 if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
212 constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
213 constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
214 int64_t targetWorkDuration = frameDeadline - intendedVsync;
215 targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
216 if (targetWorkDuration > kSanityCheckLowerBound &&
217 targetWorkDuration < kSanityCheckUpperBound &&
218 targetWorkDuration != mLastTargetWorkDuration) {
219 mLastTargetWorkDuration = targetWorkDuration;
220 mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
221 }
222 int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
223 int64_t actualDuration = frameDuration -
224 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
225 dequeueBufferDuration;
226 if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
227 mHintSessionWrapper->reportActualWorkDuration(actualDuration);
228 }
229
230 mLastDequeueBufferDuration = dequeueBufferDuration;
231 }
232
syncFrameState(TreeInfo & info)233 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
234 ATRACE_CALL();
235 int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
236 int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
237 int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
238 int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
239 int64_t frameInterval = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameInterval)];
240 mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId, frameDeadline,
241 frameInterval);
242 bool canDraw = mContext->makeCurrent();
243 mContext->unpinImages();
244
245 for (size_t i = 0; i < mLayers.size(); i++) {
246 mLayers[i]->apply();
247 }
248 mLayers.clear();
249 mContext->setContentDrawBounds(mContentDrawBounds);
250 mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
251
252 // This is after the prepareTree so that any pending operations
253 // (RenderNode tree state, prefetched layers, etc...) will be flushed.
254 if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
255 if (!mContext->hasSurface()) {
256 mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
257 } else {
258 // If we have a surface but can't draw we must be stopped
259 mSyncResult |= SyncResult::ContextIsStopped;
260 }
261 info.out.canDrawThisFrame = false;
262 }
263
264 if (info.out.hasAnimations) {
265 if (info.out.requiresUiRedraw) {
266 mSyncResult |= SyncResult::UIRedrawRequired;
267 }
268 }
269 if (!info.out.canDrawThisFrame) {
270 mSyncResult |= SyncResult::FrameDropped;
271 }
272 // If prepareTextures is false, we ran out of texture cache space
273 return info.prepareTextures;
274 }
275
unblockUiThread()276 void DrawFrameTask::unblockUiThread() {
277 AutoMutex _lock(mLock);
278 mSignal.signal();
279 }
280
createHintSession(pid_t uiThreadId,pid_t renderThreadId)281 void DrawFrameTask::createHintSession(pid_t uiThreadId, pid_t renderThreadId) {
282 if (mHintSessionWrapper) return;
283 mHintSessionWrapper.emplace(uiThreadId, renderThreadId);
284 }
285
HintSessionWrapper(int32_t uiThreadId,int32_t renderThreadId)286 DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
287 if (!Properties::useHintManager) return;
288 if (uiThreadId < 0 || renderThreadId < 0) return;
289
290 ensureAPerformanceHintBindingInitialized();
291
292 APerformanceHintManager* manager = gAPH_getManagerFn();
293 if (!manager) return;
294
295 std::vector<int32_t> tids = CommonPool::getThreadIds();
296 tids.push_back(uiThreadId);
297 tids.push_back(renderThreadId);
298
299 // DrawFrameTask code will always set a target duration before reporting actual durations.
300 // So this is just a placeholder value that's never used.
301 int64_t dummyTargetDurationNanos = 16666667;
302 mHintSession =
303 gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
304 }
305
~HintSessionWrapper()306 DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
307 if (mHintSession) {
308 gAPH_closeSessionFn(mHintSession);
309 }
310 }
311
updateTargetWorkDuration(long targetDurationNanos)312 void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
313 if (mHintSession) {
314 gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
315 }
316 }
317
reportActualWorkDuration(long actualDurationNanos)318 void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
319 if (mHintSession) {
320 gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
321 }
322 }
323
324 } /* namespace renderthread */
325 } /* namespace uirenderer */
326 } /* namespace android */
327