1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/pipeline_ng/ui_task_scheduler.h"
17
18 #include <unistd.h>
19
20 #ifdef FFRT_EXISTS
21 #include "base/longframe/long_frame_report.h"
22 #endif
23 #include "core/pipeline_ng/pipeline_context.h"
24
25 namespace OHOS::Ace::NG {
26 namespace {
27 constexpr char LIBFFRT_LIB64_PATH[] = "/system/lib64/ndk/libffrt.z.so";
28 constexpr int32_t ENDORSE_LAYOUT_COUNT = 2;
29 }
30 uint64_t UITaskScheduler::frameId_ = 0;
31
UITaskScheduler()32 UITaskScheduler::UITaskScheduler()
33 {
34 if (access(LIBFFRT_LIB64_PATH, F_OK) == -1) {
35 return;
36 }
37 is64BitSystem_ = true;
38 }
39
~UITaskScheduler()40 UITaskScheduler::~UITaskScheduler()
41 {
42 persistAfterLayoutTasks_.clear();
43 }
44
AddDirtyLayoutNode(const RefPtr<FrameNode> & dirty)45 void UITaskScheduler::AddDirtyLayoutNode(const RefPtr<FrameNode>& dirty)
46 {
47 CHECK_RUN_ON(UI);
48 CHECK_NULL_VOID(dirty);
49 dirtyLayoutNodes_.emplace_back(dirty);
50 }
51
AddLayoutNode(const RefPtr<FrameNode> & layoutNode)52 void UITaskScheduler::AddLayoutNode(const RefPtr<FrameNode>& layoutNode)
53 {
54 CHECK_RUN_ON(UI);
55 CHECK_NULL_VOID(layoutNode);
56 layoutNodes_.emplace_back(layoutNode);
57 }
58
SetLayoutNodeRect()59 void UITaskScheduler::SetLayoutNodeRect()
60 {
61 if (layoutNodes_.empty()) {
62 return;
63 }
64 auto layoutNodes = std::move(layoutNodes_);
65 LayoutNodesSet layoutNodesSet(layoutNodes.begin(), layoutNodes.end());
66
67 for (auto& layoutNode : layoutNodesSet) {
68 if (layoutNode->GetIsFind()) {
69 layoutNode->SetIsFind(false);
70 continue;
71 }
72 std::list<RefPtr<FrameNode>> children;
73 OffsetF offset;
74 layoutNode->GetOneDepthVisibleFrameWithOffset(children, offset);
75 for (auto& child : children) {
76 child->GetRenderContext()->SetExtraOffset(offset);
77 }
78 }
79 }
80
AddDirtyRenderNode(const RefPtr<FrameNode> & dirty)81 void UITaskScheduler::AddDirtyRenderNode(const RefPtr<FrameNode>& dirty)
82 {
83 CHECK_RUN_ON(UI);
84 CHECK_NULL_VOID(dirty);
85 auto result = dirtyRenderNodes_[dirty->GetPageId()].emplace(dirty);
86 if (!result.second) {
87 LOGW("Fail to emplace %{public}s render node", dirty->GetTag().c_str());
88 }
89 }
90
ExpandSafeArea()91 void UITaskScheduler::ExpandSafeArea()
92 {
93 auto pipeline = PipelineContext::GetCurrentContext();
94 CHECK_NULL_VOID(pipeline);
95 auto safeAreaManager = pipeline->GetSafeAreaManager();
96 CHECK_NULL_VOID(safeAreaManager);
97 safeAreaManager->ExpandSafeArea();
98 }
99
FlushSyncGeometryNodeTasks()100 void UITaskScheduler::FlushSyncGeometryNodeTasks()
101 {
102 ACE_LAYOUT_SCOPED_TRACE("FlushSyncGeometryNodeTasks");
103 ExpandSafeArea();
104 SetLayoutNodeRect();
105 auto tasks = std::move(syncGeometryNodeTasks_);
106 for (auto& task : tasks) {
107 if (task) {
108 task();
109 }
110 }
111 }
112
FlushLayoutTask(bool forceUseMainThread)113 void UITaskScheduler::FlushLayoutTask(bool forceUseMainThread)
114 {
115 CHECK_RUN_ON(UI);
116 ACE_FUNCTION_TRACE();
117 if (dirtyLayoutNodes_.empty()) {
118 return;
119 }
120 #ifdef FFRT_EXISTS
121 // Pause GC during long frame
122 std::unique_ptr<ILongFrame> longFrame = std::make_unique<ILongFrame>();
123 if (is64BitSystem_) {
124 ACE_LAYOUT_SCOPED_TRACE("ReportStartEvent");
125 longFrame->ReportStartEvent();
126 }
127 #endif
128
129 isLayouting_ = true;
130 auto dirtyLayoutNodes = std::move(dirtyLayoutNodes_);
131 PageDirtySet dirtyLayoutNodesSet(dirtyLayoutNodes.begin(), dirtyLayoutNodes.end());
132
133 // Priority task creation
134 int64_t time = 0;
135 for (auto&& node : dirtyLayoutNodesSet) {
136 // need to check the node is destroying or not before CreateLayoutTask
137 if (!node || node->IsInDestroying()) {
138 continue;
139 }
140 time = GetSysTimestamp();
141 node->CreateLayoutTask(forceUseMainThread);
142 time = GetSysTimestamp() - time;
143 if (frameInfo_ != nullptr) {
144 frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::LAYOUT);
145 }
146 }
147 FlushSyncGeometryNodeTasks();
148 #ifdef FFRT_EXISTS
149 if (is64BitSystem_) {
150 ACE_LAYOUT_SCOPED_TRACE("ReportEndEvent");
151 longFrame->ReportEndEvent();
152 }
153 #endif
154
155 isLayouting_ = false;
156 }
157
FlushRenderTask(bool forceUseMainThread)158 void UITaskScheduler::FlushRenderTask(bool forceUseMainThread)
159 {
160 CHECK_RUN_ON(UI);
161 if (FrameReport::GetInstance().GetEnable()) {
162 FrameReport::GetInstance().BeginFlushRender();
163 }
164 auto dirtyRenderNodes = std::move(dirtyRenderNodes_);
165 // Priority task creation
166 int64_t time = 0;
167 for (auto&& pageNodes : dirtyRenderNodes) {
168 ACE_SCOPED_TRACE("FlushRenderTask %zu", pageNodes.second.size());
169 for (auto&& node : pageNodes.second) {
170 if (!node) {
171 continue;
172 }
173 if (node->IsInDestroying()) {
174 continue;
175 }
176 time = GetSysTimestamp();
177 auto task = node->CreateRenderTask(forceUseMainThread);
178 if (task) {
179 if (forceUseMainThread || (task->GetTaskThreadType() == MAIN_TASK)) {
180 (*task)();
181 time = GetSysTimestamp() - time;
182 if (frameInfo_ != nullptr) {
183 frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::RENDER);
184 }
185 }
186 }
187 }
188 }
189 }
190
NeedAdditionalLayout()191 bool UITaskScheduler::NeedAdditionalLayout()
192 {
193 bool ret = false;
194 ElementRegister::GetInstance()->ReSyncGeometryTransition();
195
196 RootDirtyMap dirtyLayoutNodesMap;
197 for (auto&& dirty : dirtyLayoutNodes_) {
198 dirtyLayoutNodesMap[dirty->GetPageId()].emplace(dirty);
199 }
200
201 for (auto&& pageNodes : dirtyLayoutNodesMap) {
202 for (auto&& node : pageNodes.second) {
203 if (!node || node->IsInDestroying() || !node->GetLayoutProperty()) {
204 continue;
205 }
206 const auto& geometryTransition = node->GetLayoutProperty()->GetGeometryTransition();
207 if (geometryTransition != nullptr) {
208 ret |= geometryTransition->OnAdditionalLayout(node);
209 }
210 }
211 }
212 return ret;
213 }
214
FlushTaskWithCheck(bool triggeredByImplicitAnimation)215 void UITaskScheduler::FlushTaskWithCheck(bool triggeredByImplicitAnimation)
216 {
217 layoutWithImplicitAnimation_.push(triggeredByImplicitAnimation);
218 if (IsLayouting()) {
219 multiLayoutCount_++;
220 return;
221 }
222 FlushTask();
223 }
224
FlushTask()225 void UITaskScheduler::FlushTask()
226 {
227 CHECK_RUN_ON(UI);
228 ACE_SCOPED_TRACE("UITaskScheduler::FlushTask");
229 // update for first entry from flushVSync
230 // and reset to avoid infinite add
231 layoutedCount_ = 0;
232 multiLayoutCount_ = 1;
233 singleDirtyNodesToFlush_.clear();
234 do {
235 if (RequestFrameOnLayoutCountExceeds()) {
236 break;
237 }
238 FlushLayoutTask();
239 if (NeedAdditionalLayout()) {
240 FlushLayoutTask();
241 }
242 if (!afterLayoutTasks_.empty()) {
243 FlushAfterLayoutTask();
244 }
245 FlushSafeAreaPaddingProcess();
246 layoutedCount_++;
247 multiLayoutCount_--;
248 auto triggeredByImplicitAnimation =
249 layoutWithImplicitAnimation_.empty() ? false : layoutWithImplicitAnimation_.front();
250 if (!triggeredByImplicitAnimation && !afterLayoutCallbacksInImplicitAnimationTask_.empty()) {
251 FlushAfterLayoutCallbackInImplicitAnimationTask();
252 }
253 if (!layoutWithImplicitAnimation_.empty()) {
254 layoutWithImplicitAnimation_.pop();
255 }
256 } while (multiLayoutCount_ > 0);
257 // abandon unused params
258 layoutWithImplicitAnimation_ = std::queue<bool>();
259 FlushAllSingleNodeTasks();
260 multiLayoutCount_ = 0;
261 layoutedCount_ = 0;
262 ElementRegister::GetInstance()->ClearPendingRemoveNodes();
263 FlushRenderTask();
264 }
265
FlushAllSingleNodeTasks()266 void UITaskScheduler::FlushAllSingleNodeTasks()
267 {
268 // handle case of components executing FlushUITaskWithSingleDirtyNode during FlushLayoutTask
269 if (singleDirtyNodesToFlush_.empty()) {
270 return;
271 }
272 ACE_SCOPED_TRACE("Flush after-layout singleNode task, count %zu", singleDirtyNodesToFlush_.size());
273 auto pipeline = PipelineContext::GetCurrentContextPtrSafelyWithCheck();
274 CHECK_NULL_VOID(pipeline);
275 auto singleDirtyNodes = std::move(singleDirtyNodesToFlush_);
276 for (const auto& node : singleDirtyNodes) {
277 // skip if already flushed by any previous tasks
278 if (!node || (!node->IsLayoutDirtyMarked() && !node->CheckNeedForceMeasureAndLayout())) {
279 continue;
280 }
281 pipeline->FlushUITaskWithSingleDirtyNode(node);
282 }
283 }
284
AddSingleNodeToFlush(const RefPtr<FrameNode> & dirtyNode)285 void UITaskScheduler::AddSingleNodeToFlush(const RefPtr<FrameNode>& dirtyNode)
286 {
287 if (std::find(singleDirtyNodesToFlush_.begin(), singleDirtyNodesToFlush_.end(), dirtyNode) !=
288 singleDirtyNodesToFlush_.end()) {
289 return;
290 }
291 singleDirtyNodesToFlush_.emplace_back(dirtyNode);
292 }
293
RequestFrameOnLayoutCountExceeds()294 bool UITaskScheduler::RequestFrameOnLayoutCountExceeds()
295 {
296 if (layoutedCount_ < ENDORSE_LAYOUT_COUNT) {
297 return false;
298 }
299 auto pipeline = PipelineContext::GetCurrentContextPtrSafelyWithCheck();
300 if (pipeline) {
301 pipeline->RequestFrame();
302 }
303 return true;
304 }
305
AddSafeAreaPaddingProcessTask(FrameNode * node)306 void UITaskScheduler::AddSafeAreaPaddingProcessTask(FrameNode* node)
307 {
308 safeAreaPaddingProcessTasks_.insert(node);
309 }
310
RemoveSafeAreaPaddingProcessTask(FrameNode * node)311 void UITaskScheduler::RemoveSafeAreaPaddingProcessTask(FrameNode* node)
312 {
313 safeAreaPaddingProcessTasks_.erase(node);
314 }
315
FlushSafeAreaPaddingProcess()316 void UITaskScheduler::FlushSafeAreaPaddingProcess()
317 {
318 if (safeAreaPaddingProcessTasks_.empty()) {
319 return;
320 }
321 auto iter = safeAreaPaddingProcessTasks_.begin();
322 while (iter != safeAreaPaddingProcessTasks_.end()) {
323 auto node = *iter;
324 if (!node) {
325 iter = safeAreaPaddingProcessTasks_.erase(iter);
326 } else {
327 node->ProcessSafeAreaPadding();
328 ++iter;
329 }
330 }
331 // clear caches after all process tasks
332 iter = safeAreaPaddingProcessTasks_.begin();
333 while (iter != safeAreaPaddingProcessTasks_.end()) {
334 auto node = *iter;
335 if (!node) {
336 iter = safeAreaPaddingProcessTasks_.erase(iter);
337 } else {
338 const auto& geometryNode = node->GetGeometryNode();
339 if (geometryNode) {
340 geometryNode->ResetAccumulatedSafeAreaPadding();
341 }
342 ++iter;
343 }
344 }
345 }
346
AddPredictTask(PredictTask && task)347 void UITaskScheduler::AddPredictTask(PredictTask&& task)
348 {
349 predictTask_.push_back(std::move(task));
350 }
351
FlushPredictTask(int64_t deadline,bool canUseLongPredictTask)352 void UITaskScheduler::FlushPredictTask(int64_t deadline, bool canUseLongPredictTask)
353 {
354 decltype(predictTask_) tasks(std::move(predictTask_));
355 for (const auto& task : tasks) {
356 if (task) {
357 task(deadline, canUseLongPredictTask);
358 }
359 }
360 }
361
CleanUp()362 void UITaskScheduler::CleanUp()
363 {
364 dirtyLayoutNodes_.clear();
365 dirtyRenderNodes_.clear();
366 }
367
isEmpty()368 bool UITaskScheduler::isEmpty()
369 {
370 return dirtyLayoutNodes_.empty() && dirtyRenderNodes_.empty();
371 }
372
IsPredictTaskEmpty()373 bool UITaskScheduler::IsPredictTaskEmpty()
374 {
375 return predictTask_.empty();
376 }
377
AddAfterLayoutTask(std::function<void ()> && task,bool isFlushInImplicitAnimationTask)378 void UITaskScheduler::AddAfterLayoutTask(std::function<void()>&& task, bool isFlushInImplicitAnimationTask)
379 {
380 if (isFlushInImplicitAnimationTask) {
381 afterLayoutCallbacksInImplicitAnimationTask_.emplace_back(std::move(task));
382 } else {
383 afterLayoutTasks_.emplace_back(std::move(task));
384 }
385 }
386
AddPersistAfterLayoutTask(std::function<void ()> && task)387 void UITaskScheduler::AddPersistAfterLayoutTask(std::function<void()>&& task)
388 {
389 persistAfterLayoutTasks_.emplace_back(std::move(task));
390 LOGI("AddPersistAfterLayoutTask size: %{public}u", static_cast<uint32_t>(persistAfterLayoutTasks_.size()));
391 }
392
FlushAfterLayoutTask()393 void UITaskScheduler::FlushAfterLayoutTask()
394 {
395 decltype(afterLayoutTasks_) tasks(std::move(afterLayoutTasks_));
396 for (const auto& task : tasks) {
397 if (task) {
398 task();
399 }
400 }
401 // flush correct rect again and flush dirty node again
402 FlushPersistAfterLayoutTask();
403 }
404
FlushAfterLayoutCallbackInImplicitAnimationTask()405 void UITaskScheduler::FlushAfterLayoutCallbackInImplicitAnimationTask()
406 {
407 decltype(afterLayoutCallbacksInImplicitAnimationTask_) tasks(
408 std::move(afterLayoutCallbacksInImplicitAnimationTask_));
409 for (const auto& task : tasks) {
410 if (task) {
411 task();
412 }
413 }
414 }
415
FlushPersistAfterLayoutTask()416 void UITaskScheduler::FlushPersistAfterLayoutTask()
417 {
418 // only execute after layout
419 if (persistAfterLayoutTasks_.empty()) {
420 return;
421 }
422 ACE_SCOPED_TRACE("UITaskScheduler::FlushPersistAfterLayoutTask");
423 for (const auto& task : persistAfterLayoutTasks_) {
424 if (task) {
425 task();
426 }
427 }
428 }
429
AddAfterRenderTask(std::function<void ()> && task)430 void UITaskScheduler::AddAfterRenderTask(std::function<void()>&& task)
431 {
432 afterRenderTasks_.emplace_back(std::move(task));
433 }
434
FlushAfterRenderTask()435 void UITaskScheduler::FlushAfterRenderTask()
436 {
437 ACE_SCOPED_TRACE("UITaskScheduler::FlushAfterRenderTask");
438 decltype(afterRenderTasks_) tasks(std::move(afterRenderTasks_));
439 for (const auto& task : tasks) {
440 if (task) {
441 task();
442 }
443 }
444 }
445
446 } // namespace OHOS::Ace::NG
447