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 "base/perfmonitor/perf_monitor.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25
26 namespace OHOS::Ace::NG {
27 namespace {
28 constexpr char LIBFFRT_LIB64_PATH[] = "/system/lib64/ndk/libffrt.z.so";
29 constexpr int32_t ENDORSE_LAYOUT_COUNT = 2;
30
31 #ifndef IS_RELEASE_VERSION
32 constexpr int32_t SINGLE_FRAME_TIME_NANOSEC = 16600000;
33 constexpr int32_t NANO_TO_MICRO = 1000;
34 #endif
35 } // namespace
36 uint64_t UITaskScheduler::frameId_ = 0;
37
UITaskScheduler()38 UITaskScheduler::UITaskScheduler()
39 {
40 if (access(LIBFFRT_LIB64_PATH, F_OK) == -1) {
41 return;
42 }
43 is64BitSystem_ = true;
44 }
45
~UITaskScheduler()46 UITaskScheduler::~UITaskScheduler()
47 {
48 persistAfterLayoutTasks_.clear();
49 }
50
AddDirtyLayoutNode(const RefPtr<FrameNode> & dirty)51 void UITaskScheduler::AddDirtyLayoutNode(const RefPtr<FrameNode>& dirty)
52 {
53 CHECK_RUN_ON(UI);
54 CHECK_NULL_VOID(dirty);
55 dirtyLayoutNodes_.emplace_back(dirty);
56 }
57
AddIgnoreLayoutSafeAreaBundle(IgnoreLayoutSafeAreaBundle && bundle)58 void UITaskScheduler::AddIgnoreLayoutSafeAreaBundle(IgnoreLayoutSafeAreaBundle&& bundle)
59 {
60 CHECK_RUN_ON(UI);
61 ignoreLayoutSafeAreaBundles_.emplace_back(std::move(bundle));
62 }
63
AddLayoutNode(const RefPtr<FrameNode> & layoutNode)64 void UITaskScheduler::AddLayoutNode(const RefPtr<FrameNode>& layoutNode)
65 {
66 CHECK_RUN_ON(UI);
67 CHECK_NULL_VOID(layoutNode);
68 layoutNodes_.emplace_back(layoutNode);
69 }
70
SetLayoutNodeRect()71 void UITaskScheduler::SetLayoutNodeRect()
72 {
73 if (layoutNodes_.empty()) {
74 return;
75 }
76 auto layoutNodes = std::move(layoutNodes_);
77 LayoutNodesSet layoutNodesSet(layoutNodes.begin(), layoutNodes.end());
78
79 for (auto& layoutNode : layoutNodesSet) {
80 if (layoutNode->GetIsFind()) {
81 layoutNode->SetIsFind(false);
82 continue;
83 }
84 std::list<RefPtr<FrameNode>> children;
85 OffsetF offset;
86 layoutNode->GetOneDepthVisibleFrameWithOffset(children, offset);
87 for (auto& child : children) {
88 child->GetRenderContext()->SetExtraOffset(offset);
89 }
90 }
91 }
92
AddDirtyRenderNode(const RefPtr<FrameNode> & dirty)93 void UITaskScheduler::AddDirtyRenderNode(const RefPtr<FrameNode>& dirty)
94 {
95 CHECK_RUN_ON(UI);
96 CHECK_NULL_VOID(dirty);
97 auto result = dirtyRenderNodes_[dirty->GetPageId()].emplace(dirty);
98 if (!result.second) {
99 LOGW("Fail to emplace %{public}s render node", dirty->GetTag().c_str());
100 }
101 }
102
ExpandSafeArea()103 void UITaskScheduler::ExpandSafeArea()
104 {
105 auto pipeline = PipelineContext::GetCurrentContext();
106 CHECK_NULL_VOID(pipeline);
107 auto safeAreaManager = pipeline->GetSafeAreaManager();
108 CHECK_NULL_VOID(safeAreaManager);
109 safeAreaManager->ExpandSafeArea();
110 }
111
FlushSyncGeometryNodeTasks()112 void UITaskScheduler::FlushSyncGeometryNodeTasks()
113 {
114 ACE_LAYOUT_SCOPED_TRACE("FlushSyncGeometryNodeTasks");
115 ExpandSafeArea();
116 SetLayoutNodeRect();
117 auto tasks = std::move(syncGeometryNodeTasks_);
118 for (auto& task : tasks) {
119 if (task) {
120 task();
121 }
122 }
123 }
124
FlushLayoutTask(bool forceUseMainThread)125 void UITaskScheduler::FlushLayoutTask(bool forceUseMainThread)
126 {
127 CHECK_RUN_ON(UI);
128 ACE_FUNCTION_TRACE_COMMERCIAL();
129 if (dirtyLayoutNodes_.empty()) {
130 return;
131 }
132 if (isLayouting_ && SystemProperties::GetLayoutDetectEnabled()) {
133 LOGF_ABORT("you are already in flushing layout!");
134 }
135
136 #ifdef FFRT_EXISTS
137 // Pause GC during long frame
138 std::unique_ptr<ILongFrame> longFrame = std::make_unique<ILongFrame>();
139 if (is64BitSystem_) {
140 ACE_LAYOUT_SCOPED_TRACE("ReportStartEvent");
141 longFrame->ReportStartEvent();
142 }
143 #endif
144
145 isLayouting_ = true;
146 auto dirtyLayoutNodes = std::move(dirtyLayoutNodes_);
147 PageDirtySet dirtyLayoutNodesSet(dirtyLayoutNodes.begin(), dirtyLayoutNodes.end());
148
149 // Priority task creation
150 int64_t time = 0;
151 #ifndef IS_RELEASE_VERSION
152 int64_t duration = 0;
153 #endif
154 for (auto&& node : dirtyLayoutNodesSet) {
155 // need to check the node is destroying or not before CreateLayoutTask
156 if (!node || node->IsInDestroying()) {
157 continue;
158 }
159 time = GetSysTimestamp();
160 node->CreateLayoutTask(forceUseMainThread);
161 time = GetSysTimestamp() - time;
162 if (frameInfo_ != nullptr) {
163 frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::LAYOUT);
164 }
165 #ifndef IS_RELEASE_VERSION
166 duration += time;
167 #endif
168 }
169
170 while (!ignoreLayoutSafeAreaBundles_.empty()) {
171 FlushPostponedLayoutTask(forceUseMainThread);
172 }
173
174 FlushSyncGeometryNodeTasks();
175 #ifdef FFRT_EXISTS
176 if (is64BitSystem_) {
177 ACE_LAYOUT_SCOPED_TRACE("ReportEndEvent");
178 longFrame->ReportEndEvent();
179 }
180 #endif
181
182 isLayouting_ = false;
183 #ifndef IS_RELEASE_VERSION
184 if (duration > SINGLE_FRAME_TIME_NANOSEC) {
185 PerfMonitor::GetPerfMonitor()->SetSubHealthInfo("SUBHEALTH", "FlushLayoutTask", duration / NANO_TO_MICRO);
186 }
187 #endif
188 }
189
FlushPostponedLayoutTask(bool forceUseMainThread)190 void UITaskScheduler::FlushPostponedLayoutTask(bool forceUseMainThread)
191 {
192 auto ignoreLayoutSafeAreaBundles = std::move(ignoreLayoutSafeAreaBundles_);
193 for (auto&& bundle = ignoreLayoutSafeAreaBundles.rbegin(); bundle != ignoreLayoutSafeAreaBundles.rend();
194 ++bundle) {
195 for (auto&& node : bundle->first) {
196 if (!node || node->IsInDestroying()) {
197 continue;
198 }
199 node->CreateLayoutTask(forceUseMainThread, LayoutType::MEASURE_FOR_IGNORE);
200 }
201 auto&& container = bundle->second;
202 if (!container || container->IsInDestroying()) {
203 continue;
204 }
205 if (!container->PostponedTaskForIgnore()) {
206 container->CreateLayoutTask(forceUseMainThread, LayoutType::LAYOUT_FOR_IGNORE);
207 }
208 }
209 }
210
FlushRenderTask(bool forceUseMainThread)211 void UITaskScheduler::FlushRenderTask(bool forceUseMainThread)
212 {
213 CHECK_RUN_ON(UI);
214 if (FrameReport::GetInstance().GetEnable()) {
215 FrameReport::GetInstance().BeginFlushRender();
216 }
217
218 auto dirtyRenderNodes = std::move(dirtyRenderNodes_);
219 // Priority task creation
220 int64_t time = 0;
221 for (auto&& pageNodes : dirtyRenderNodes) {
222 ACE_SCOPED_TRACE_COMMERCIAL("FlushRenderTask %zu", pageNodes.second.size());
223 for (auto&& node : pageNodes.second) {
224 if (!node) {
225 continue;
226 }
227 if (node->IsInDestroying()) {
228 // reset RenderDirtyMarked for recycle node
229 node->ResetRenderDirtyMarked(false);
230 continue;
231 }
232 time = GetSysTimestamp();
233 auto task = node->CreateRenderTask(forceUseMainThread);
234 if (task) {
235 if (forceUseMainThread || (task->GetTaskThreadType() == MAIN_TASK)) {
236 (*task)();
237 time = GetSysTimestamp() - time;
238 if (frameInfo_ != nullptr) {
239 frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::RENDER);
240 }
241 }
242 }
243 }
244 }
245 }
246
NeedAdditionalLayout()247 bool UITaskScheduler::NeedAdditionalLayout()
248 {
249 bool ret = false;
250 ElementRegister::GetInstance()->ReSyncGeometryTransition();
251
252 RootDirtyMap dirtyLayoutNodesMap;
253 for (auto&& dirty : dirtyLayoutNodes_) {
254 dirtyLayoutNodesMap[dirty->GetPageId()].emplace(dirty);
255 }
256
257 for (auto&& pageNodes : dirtyLayoutNodesMap) {
258 for (auto&& node : pageNodes.second) {
259 if (!node || node->IsInDestroying() || !node->GetLayoutProperty()) {
260 continue;
261 }
262 const auto& geometryTransition = node->GetLayoutProperty()->GetGeometryTransition();
263 if (geometryTransition != nullptr) {
264 ret |= geometryTransition->OnAdditionalLayout(node);
265 }
266 }
267 }
268 return ret;
269 }
270
FlushTaskWithCheck(bool triggeredByImplicitAnimation)271 void UITaskScheduler::FlushTaskWithCheck(bool triggeredByImplicitAnimation)
272 {
273 layoutWithImplicitAnimation_.push(triggeredByImplicitAnimation);
274 if (IsLayouting()) {
275 multiLayoutCount_++;
276 return;
277 }
278 FlushTask();
279 }
280
FlushTask()281 void UITaskScheduler::FlushTask()
282 {
283 CHECK_RUN_ON(UI);
284 ACE_SCOPED_TRACE("UITaskScheduler::FlushTask");
285 // update for first entry from flushVSync
286 // and reset to avoid infinite add
287 int32_t layoutedCount = 0;
288 multiLayoutCount_ = 1;
289 singleDirtyNodesToFlush_.clear();
290 do {
291 if (layoutedCount >= ENDORSE_LAYOUT_COUNT && RequestFrameOnLayoutCountExceeds()) {
292 break;
293 }
294 FlushLayoutTask();
295 if (NeedAdditionalLayout()) {
296 FlushLayoutTask();
297 }
298 if (!afterLayoutTasks_.empty()) {
299 FlushAfterLayoutTask();
300 }
301 layoutedCount++;
302 multiLayoutCount_--;
303 FlushSafeAreaPaddingProcess();
304 auto triggeredByImplicitAnimation =
305 layoutWithImplicitAnimation_.empty() ? false : layoutWithImplicitAnimation_.front();
306 if (!triggeredByImplicitAnimation && !afterLayoutCallbacksInImplicitAnimationTask_.empty()) {
307 FlushAfterLayoutCallbackInImplicitAnimationTask();
308 }
309 if (!layoutWithImplicitAnimation_.empty()) {
310 layoutWithImplicitAnimation_.pop();
311 }
312 } while (multiLayoutCount_ > 0);
313 // abandon unused params
314 layoutWithImplicitAnimation_ = std::queue<bool>();
315 FlushAllSingleNodeTasks();
316 multiLayoutCount_ = 0;
317 ElementRegister::GetInstance()->ClearPendingRemoveNodes();
318 FlushRenderTask();
319 }
320
FlushAllSingleNodeTasks()321 void UITaskScheduler::FlushAllSingleNodeTasks()
322 {
323 // handle case of components executing FlushUITaskWithSingleDirtyNode during FlushLayoutTask
324 if (singleDirtyNodesToFlush_.empty()) {
325 return;
326 }
327 ACE_SCOPED_TRACE("Flush after-layout singleNode task, count %zu", singleDirtyNodesToFlush_.size());
328 auto pipeline = PipelineContext::GetCurrentContextPtrSafelyWithCheck();
329 CHECK_NULL_VOID(pipeline);
330 auto singleDirtyNodes = std::move(singleDirtyNodesToFlush_);
331 for (const auto& node : singleDirtyNodes) {
332 // skip if already flushed by any previous tasks
333 if (!node || (!node->IsLayoutDirtyMarked() && !node->CheckNeedForceMeasureAndLayout())) {
334 continue;
335 }
336 pipeline->FlushUITaskWithSingleDirtyNode(node);
337 }
338 }
339
AddSingleNodeToFlush(const RefPtr<FrameNode> & dirtyNode)340 void UITaskScheduler::AddSingleNodeToFlush(const RefPtr<FrameNode>& dirtyNode)
341 {
342 singleDirtyNodesToFlush_.insert(dirtyNode);
343 }
344
RequestFrameOnLayoutCountExceeds()345 bool UITaskScheduler::RequestFrameOnLayoutCountExceeds()
346 {
347 auto pipeline = PipelineContext::GetCurrentContextPtrSafelyWithCheck();
348 if (pipeline) {
349 pipeline->RequestFrame();
350 }
351 return true;
352 }
353
AddSafeAreaPaddingProcessTask(FrameNode * node)354 void UITaskScheduler::AddSafeAreaPaddingProcessTask(FrameNode* node)
355 {
356 safeAreaPaddingProcessTasks_.insert(node);
357 }
358
RemoveSafeAreaPaddingProcessTask(FrameNode * node)359 void UITaskScheduler::RemoveSafeAreaPaddingProcessTask(FrameNode* node)
360 {
361 safeAreaPaddingProcessTasks_.erase(node);
362 }
363
FlushSafeAreaPaddingProcess()364 void UITaskScheduler::FlushSafeAreaPaddingProcess()
365 {
366 if (safeAreaPaddingProcessTasks_.empty()) {
367 return;
368 }
369 auto safeAreaPaddingProcessTasks = safeAreaPaddingProcessTasks_;
370 auto iter = safeAreaPaddingProcessTasks.begin();
371 while (iter != safeAreaPaddingProcessTasks.end()) {
372 auto node = *iter;
373 if (!node) {
374 iter = safeAreaPaddingProcessTasks.erase(iter);
375 } else {
376 node->ProcessSafeAreaPadding();
377 ++iter;
378 }
379 }
380 // clear caches after all process tasks
381 iter = safeAreaPaddingProcessTasks.begin();
382 while (iter != safeAreaPaddingProcessTasks.end()) {
383 auto node = *iter;
384 if (node) {
385 const auto& geometryNode = node->GetGeometryNode();
386 if (geometryNode) {
387 geometryNode->ResetAccumulatedSafeAreaPadding();
388 }
389 }
390 ++iter;
391 }
392
393 auto eraseIter = safeAreaPaddingProcessTasks_.begin();
394 while (eraseIter != safeAreaPaddingProcessTasks_.end()) {
395 auto node = *eraseIter;
396 if (!node) {
397 eraseIter = safeAreaPaddingProcessTasks_.erase(eraseIter);
398 } else {
399 ++eraseIter;
400 }
401 }
402 }
403
AddPredictTask(PredictTask && task)404 void UITaskScheduler::AddPredictTask(PredictTask&& task)
405 {
406 predictTask_.push_back(std::move(task));
407 }
408
FlushPredictTask(int64_t deadline,bool canUseLongPredictTask)409 void UITaskScheduler::FlushPredictTask(int64_t deadline, bool canUseLongPredictTask)
410 {
411 decltype(predictTask_) tasks(std::move(predictTask_));
412 for (const auto& task : tasks) {
413 if (task) {
414 task(deadline, canUseLongPredictTask);
415 }
416 }
417 }
418
CleanUp()419 void UITaskScheduler::CleanUp()
420 {
421 dirtyLayoutNodes_.clear();
422 dirtyRenderNodes_.clear();
423 }
424
isEmpty()425 bool UITaskScheduler::isEmpty()
426 {
427 return dirtyLayoutNodes_.empty() && dirtyRenderNodes_.empty();
428 }
429
IsPredictTaskEmpty()430 bool UITaskScheduler::IsPredictTaskEmpty()
431 {
432 return predictTask_.empty();
433 }
434
AddAfterLayoutTask(std::function<void ()> && task,bool isFlushInImplicitAnimationTask)435 void UITaskScheduler::AddAfterLayoutTask(std::function<void()>&& task, bool isFlushInImplicitAnimationTask)
436 {
437 if (isFlushInImplicitAnimationTask) {
438 afterLayoutCallbacksInImplicitAnimationTask_.emplace_back(std::move(task));
439 } else {
440 afterLayoutTasks_.emplace_back(std::move(task));
441 }
442 }
443
AddPersistAfterLayoutTask(std::function<void ()> && task)444 void UITaskScheduler::AddPersistAfterLayoutTask(std::function<void()>&& task)
445 {
446 persistAfterLayoutTasks_.emplace_back(std::move(task));
447 LOGI("AddPersistAfterLayoutTask size: %{public}u", static_cast<uint32_t>(persistAfterLayoutTasks_.size()));
448 }
449
FlushAfterLayoutTask()450 void UITaskScheduler::FlushAfterLayoutTask()
451 {
452 decltype(afterLayoutTasks_) tasks(std::move(afterLayoutTasks_));
453 for (const auto& task : tasks) {
454 if (task) {
455 task();
456 }
457 }
458 // flush correct rect again and flush dirty node again
459 FlushPersistAfterLayoutTask();
460 }
461
FlushAfterModifierTask()462 void UITaskScheduler::FlushAfterModifierTask()
463 {
464 decltype(afterModifierTasks_) tasks(std::move(afterModifierTasks_));
465 for (const auto& task : tasks) {
466 if (task) {
467 task();
468 }
469 }
470 }
471
FlushAfterLayoutCallbackInImplicitAnimationTask()472 void UITaskScheduler::FlushAfterLayoutCallbackInImplicitAnimationTask()
473 {
474 decltype(afterLayoutCallbacksInImplicitAnimationTask_) tasks(
475 std::move(afterLayoutCallbacksInImplicitAnimationTask_));
476 for (const auto& task : tasks) {
477 if (task) {
478 task();
479 }
480 }
481 }
482
FlushPersistAfterLayoutTask()483 void UITaskScheduler::FlushPersistAfterLayoutTask()
484 {
485 // only execute after layout
486 if (persistAfterLayoutTasks_.empty()) {
487 return;
488 }
489 ACE_SCOPED_TRACE("UITaskScheduler::FlushPersistAfterLayoutTask");
490 for (const auto& task : persistAfterLayoutTasks_) {
491 if (task) {
492 task();
493 }
494 }
495 }
496
AddAfterRenderTask(std::function<void ()> && task)497 void UITaskScheduler::AddAfterRenderTask(std::function<void()>&& task)
498 {
499 afterRenderTasks_.emplace_back(std::move(task));
500 }
501
AddAfterModifierTask(std::function<void ()> && task)502 void UITaskScheduler::AddAfterModifierTask(std::function<void()>&& task)
503 {
504 afterModifierTasks_.emplace_back(std::move(task));
505 }
506
FlushAfterRenderTask()507 void UITaskScheduler::FlushAfterRenderTask()
508 {
509 ACE_SCOPED_TRACE("UITaskScheduler::FlushAfterRenderTask");
510 decltype(afterRenderTasks_) tasks(std::move(afterRenderTasks_));
511 for (const auto& task : tasks) {
512 if (task) {
513 task();
514 }
515 }
516 }
517
518 } // namespace OHOS::Ace::NG
519