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 <gui/TraceUtils.h>
20 #include <utils/Log.h>
21
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 "HardwareBufferRenderParams.h"
30 #include "RenderThread.h"
31
32 namespace android {
33 namespace uirenderer {
34 namespace renderthread {
35
DrawFrameTask()36 DrawFrameTask::DrawFrameTask()
37 : mRenderThread(nullptr)
38 , mContext(nullptr)
39 , mContentDrawBounds(0, 0, 0, 0)
40 , mSyncResult(SyncResult::OK) {}
41
~DrawFrameTask()42 DrawFrameTask::~DrawFrameTask() {}
43
setContext(RenderThread * thread,CanvasContext * context,RenderNode * targetNode)44 void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
45 RenderNode* targetNode) {
46 mRenderThread = thread;
47 mContext = context;
48 mTargetNode = targetNode;
49 }
50
pushLayerUpdate(DeferredLayerUpdater * layer)51 void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
52 LOG_ALWAYS_FATAL_IF(!mContext,
53 "Lifecycle violation, there's no context to pushLayerUpdate with!");
54
55 for (size_t i = 0; i < mLayers.size(); i++) {
56 if (mLayers[i].get() == layer) {
57 return;
58 }
59 }
60 mLayers.push_back(layer);
61 }
62
removeLayerUpdate(DeferredLayerUpdater * layer)63 void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
64 for (size_t i = 0; i < mLayers.size(); i++) {
65 if (mLayers[i].get() == layer) {
66 mLayers.erase(mLayers.begin() + i);
67 return;
68 }
69 }
70 }
71
drawFrame()72 int DrawFrameTask::drawFrame() {
73 LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
74
75 mSyncResult = SyncResult::OK;
76 mSyncQueued = systemTime(SYSTEM_TIME_MONOTONIC);
77 postAndWait();
78
79 return mSyncResult;
80 }
81
postAndWait()82 void DrawFrameTask::postAndWait() {
83 ATRACE_CALL();
84 AutoMutex _lock(mLock);
85 mRenderThread->queue().post([this]() { run(); });
86 mSignal.wait(mLock);
87 }
88
run()89 void DrawFrameTask::run() {
90 const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
91 ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
92
93 mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
94 mContext->setTargetSdrHdrRatio(mRenderSdrHdrRatio);
95
96 auto hardwareBufferParams = mHardwareBufferParams;
97 mContext->setHardwareBufferRenderParams(hardwareBufferParams);
98 IRenderPipeline* pipeline = mContext->getRenderPipeline();
99 bool canUnblockUiThread;
100 bool canDrawThisFrame;
101 bool solelyTextureViewUpdates;
102 {
103 TreeInfo info(TreeInfo::MODE_FULL, *mContext);
104 info.forceDrawFrame = mForceDrawFrame;
105 mForceDrawFrame = false;
106 canUnblockUiThread = syncFrameState(info);
107 canDrawThisFrame = info.out.canDrawThisFrame;
108 solelyTextureViewUpdates = info.out.solelyTextureViewUpdates;
109
110 if (mFrameCommitCallback) {
111 mContext->addFrameCommitListener(std::move(mFrameCommitCallback));
112 mFrameCommitCallback = nullptr;
113 }
114 }
115
116 // Grab a copy of everything we need
117 CanvasContext* context = mContext;
118 std::function<std::function<void(bool)>(int32_t, int64_t)> frameCallback =
119 std::move(mFrameCallback);
120 std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
121 mFrameCallback = nullptr;
122 mFrameCompleteCallback = nullptr;
123
124 // From this point on anything in "this" is *UNSAFE TO ACCESS*
125 if (canUnblockUiThread) {
126 unblockUiThread();
127 }
128
129 // Even if we aren't drawing this vsync pulse the next frame number will still be accurate
130 if (CC_UNLIKELY(frameCallback)) {
131 context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
132 frameNr = context->getFrameNumber()]() {
133 auto frameCommitCallback = frameCallback(syncResult, frameNr);
134 if (frameCommitCallback) {
135 context->addFrameCommitListener(std::move(frameCommitCallback));
136 }
137 });
138 }
139
140 if (CC_LIKELY(canDrawThisFrame)) {
141 context->draw(solelyTextureViewUpdates);
142 } else {
143 // Do a flush in case syncFrameState performed any texture uploads. Since we skipped
144 // the draw() call, those uploads (or deletes) will end up sitting in the queue.
145 // Do them now
146 if (GrDirectContext* grContext = mRenderThread->getGrContext()) {
147 grContext->flushAndSubmit();
148 }
149 // wait on fences so tasks don't overlap next frame
150 context->waitOnFences();
151 }
152
153 if (CC_UNLIKELY(frameCompleteCallback)) {
154 std::invoke(frameCompleteCallback);
155 }
156
157 if (!canUnblockUiThread) {
158 unblockUiThread();
159 }
160
161 if (pipeline->hasHardwareBuffer()) {
162 auto fence = pipeline->flush();
163 hardwareBufferParams.invokeRenderCallback(std::move(fence), 0);
164 }
165 }
166
syncFrameState(TreeInfo & info)167 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
168 ATRACE_CALL();
169 int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
170 int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
171 int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
172 int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
173 int64_t frameInterval = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameInterval)];
174 mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId, frameDeadline,
175 frameInterval);
176 bool canDraw = mContext->makeCurrent();
177 mContext->unpinImages();
178
179 for (size_t i = 0; i < mLayers.size(); i++) {
180 if (mLayers[i]) {
181 mLayers[i]->apply();
182 }
183 }
184
185 mLayers.clear();
186 mContext->setContentDrawBounds(mContentDrawBounds);
187 mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
188
189 // This is after the prepareTree so that any pending operations
190 // (RenderNode tree state, prefetched layers, etc...) will be flushed.
191 bool hasTarget = mContext->hasOutputTarget();
192 if (CC_UNLIKELY(!hasTarget || !canDraw)) {
193 if (!hasTarget) {
194 mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
195 } else {
196 // If we have a surface but can't draw we must be stopped
197 mSyncResult |= SyncResult::ContextIsStopped;
198 }
199 info.out.canDrawThisFrame = false;
200 }
201
202 if (info.out.hasAnimations) {
203 if (info.out.requiresUiRedraw) {
204 mSyncResult |= SyncResult::UIRedrawRequired;
205 }
206 }
207 if (!info.out.canDrawThisFrame) {
208 mSyncResult |= SyncResult::FrameDropped;
209 }
210 // If prepareTextures is false, we ran out of texture cache space
211 return info.prepareTextures;
212 }
213
unblockUiThread()214 void DrawFrameTask::unblockUiThread() {
215 AutoMutex _lock(mLock);
216 mSignal.signal();
217 }
218
219 } /* namespace renderthread */
220 } /* namespace uirenderer */
221 } /* namespace android */
222