• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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