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