• 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     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