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