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             auto paintRect = child->GetRenderContext()->GetPaintRectWithoutTransform();
77             paintRect.SetOffset(paintRect.GetOffset() + offset);
78             child->GetRenderContext()->UpdatePaintRect(paintRect);
79         }
80     }
81 }
82 
AddDirtyRenderNode(const RefPtr<FrameNode> & dirty)83 void UITaskScheduler::AddDirtyRenderNode(const RefPtr<FrameNode>& dirty)
84 {
85     CHECK_RUN_ON(UI);
86     CHECK_NULL_VOID(dirty);
87     auto result = dirtyRenderNodes_[dirty->GetPageId()].emplace(dirty);
88     if (!result.second) {
89         LOGW("Fail to emplace %{public}s render node", dirty->GetTag().c_str());
90     }
91 }
92 
ExpandSafeArea()93 void UITaskScheduler::ExpandSafeArea()
94 {
95     auto pipeline = PipelineContext::GetCurrentContext();
96     CHECK_NULL_VOID(pipeline);
97     auto safeAreaManager = pipeline->GetSafeAreaManager();
98     CHECK_NULL_VOID(safeAreaManager);
99     safeAreaManager->ExpandSafeArea();
100 }
101 
FlushSyncGeometryNodeTasks()102 void UITaskScheduler::FlushSyncGeometryNodeTasks()
103 {
104     ACE_LAYOUT_SCOPED_TRACE("FlushSyncGeometryNodeTasks");
105     ExpandSafeArea();
106     SetLayoutNodeRect();
107     auto tasks = std::move(syncGeometryNodeTasks_);
108     for (auto& task : tasks) {
109         if (task) {
110             task();
111         }
112     }
113 }
114 
FlushLayoutTask(bool forceUseMainThread)115 void UITaskScheduler::FlushLayoutTask(bool forceUseMainThread)
116 {
117     CHECK_RUN_ON(UI);
118     ACE_FUNCTION_TRACE();
119     if (dirtyLayoutNodes_.empty()) {
120         return;
121     }
122 #ifdef FFRT_EXISTS
123     // Pause GC during long frame
124     std::unique_ptr<ILongFrame> longFrame = std::make_unique<ILongFrame>();
125     if (is64BitSystem_) {
126         ACE_LAYOUT_SCOPED_TRACE("ReportStartEvent");
127         longFrame->ReportStartEvent();
128     }
129 #endif
130 
131     isLayouting_ = true;
132     auto dirtyLayoutNodes = std::move(dirtyLayoutNodes_);
133     PageDirtySet dirtyLayoutNodesSet(dirtyLayoutNodes.begin(), dirtyLayoutNodes.end());
134 
135     // Priority task creation
136     int64_t time = 0;
137     for (auto&& node : dirtyLayoutNodesSet) {
138         // need to check the node is destroying or not before CreateLayoutTask
139         if (!node || node->IsInDestroying()) {
140             continue;
141         }
142         time = GetSysTimestamp();
143         node->CreateLayoutTask(forceUseMainThread);
144         time = GetSysTimestamp() - time;
145         if (frameInfo_ != nullptr) {
146             frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::LAYOUT);
147         }
148     }
149     FlushSyncGeometryNodeTasks();
150 #ifdef FFRT_EXISTS
151     if (is64BitSystem_) {
152         ACE_LAYOUT_SCOPED_TRACE("ReportEndEvent");
153         longFrame->ReportEndEvent();
154     }
155 #endif
156 
157     isLayouting_ = false;
158 }
159 
FlushRenderTask(bool forceUseMainThread)160 void UITaskScheduler::FlushRenderTask(bool forceUseMainThread)
161 {
162     CHECK_RUN_ON(UI);
163     if (FrameReport::GetInstance().GetEnable()) {
164         FrameReport::GetInstance().BeginFlushRender();
165     }
166     auto dirtyRenderNodes = std::move(dirtyRenderNodes_);
167     // Priority task creation
168     int64_t time = 0;
169     for (auto&& pageNodes : dirtyRenderNodes) {
170         ACE_SCOPED_TRACE("FlushRenderTask %zu", pageNodes.second.size());
171         for (auto&& node : pageNodes.second) {
172             if (!node) {
173                 continue;
174             }
175             if (node->IsInDestroying()) {
176                 continue;
177             }
178             time = GetSysTimestamp();
179             auto task = node->CreateRenderTask(forceUseMainThread);
180             if (task) {
181                 if (forceUseMainThread || (task->GetTaskThreadType() == MAIN_TASK)) {
182                     (*task)();
183                     time = GetSysTimestamp() - time;
184                     if (frameInfo_ != nullptr) {
185                         frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::RENDER);
186                     }
187                 }
188             }
189         }
190     }
191 }
192 
NeedAdditionalLayout()193 bool UITaskScheduler::NeedAdditionalLayout()
194 {
195     bool ret = false;
196     ElementRegister::GetInstance()->ReSyncGeometryTransition();
197 
198     RootDirtyMap dirtyLayoutNodesMap;
199     for (auto&& dirty : dirtyLayoutNodes_) {
200         dirtyLayoutNodesMap[dirty->GetPageId()].emplace(dirty);
201     }
202 
203     for (auto&& pageNodes : dirtyLayoutNodesMap) {
204         for (auto&& node : pageNodes.second) {
205             if (!node || node->IsInDestroying() || !node->GetLayoutProperty()) {
206                 continue;
207             }
208             const auto& geometryTransition = node->GetLayoutProperty()->GetGeometryTransition();
209             if (geometryTransition != nullptr) {
210                 ret |= geometryTransition->OnAdditionalLayout(node);
211             }
212         }
213     }
214     return ret;
215 }
216 
FlushTaskWithCheck(bool triggeredByImplicitAnimation)217 void UITaskScheduler::FlushTaskWithCheck(bool triggeredByImplicitAnimation)
218 {
219     layoutWithImplicitAnimation_.push(triggeredByImplicitAnimation);
220     if (IsLayouting()) {
221         multiLayoutCount_++;
222         return;
223     }
224     FlushTask();
225 }
226 
FlushTask()227 void UITaskScheduler::FlushTask()
228 {
229     CHECK_RUN_ON(UI);
230     ACE_SCOPED_TRACE("UITaskScheduler::FlushTask");
231     // update for first entry from flushVSync
232     // and reset to avoid infinite add
233     layoutedCount_ = 0;
234     multiLayoutCount_ = 1;
235     singleDirtyNodesToFlush_.clear();
236     do {
237         if (RequestFrameOnLayoutCountExceeds()) {
238             break;
239         }
240         FlushLayoutTask();
241         if (NeedAdditionalLayout()) {
242             FlushLayoutTask();
243         }
244         if (!afterLayoutTasks_.empty()) {
245             FlushAfterLayoutTask();
246         }
247         FlushSafeAreaPaddingProcess();
248         layoutedCount_++;
249         multiLayoutCount_--;
250         auto triggeredByImplicitAnimation =
251             layoutWithImplicitAnimation_.empty() ? false : layoutWithImplicitAnimation_.front();
252         if (!triggeredByImplicitAnimation && !afterLayoutCallbacksInImplicitAnimationTask_.empty()) {
253             FlushAfterLayoutCallbackInImplicitAnimationTask();
254         }
255         if (!layoutWithImplicitAnimation_.empty()) {
256             layoutWithImplicitAnimation_.pop();
257         }
258     } while (multiLayoutCount_ > 0);
259     // abandon unused params
260     layoutWithImplicitAnimation_ = std::queue<bool>();
261     FlushAllSingleNodeTasks();
262     multiLayoutCount_ = 0;
263     layoutedCount_ = 0;
264     ElementRegister::GetInstance()->ClearPendingRemoveNodes();
265     FlushRenderTask();
266 }
267 
FlushAllSingleNodeTasks()268 void UITaskScheduler::FlushAllSingleNodeTasks()
269 {
270     // handle case of components executing FlushUITaskWithSingleDirtyNode during FlushLayoutTask
271     if (singleDirtyNodesToFlush_.empty()) {
272         return;
273     }
274     ACE_SCOPED_TRACE("Flush after-layout singleNode task, count %zu", singleDirtyNodesToFlush_.size());
275     auto pipeline = PipelineContext::GetCurrentContextPtrSafelyWithCheck();
276     CHECK_NULL_VOID(pipeline);
277     auto singleDirtyNodes = std::move(singleDirtyNodesToFlush_);
278     for (const auto& node : singleDirtyNodes) {
279         // skip if already flushed by any previous tasks
280         if (!node || (!node->IsLayoutDirtyMarked() && !node->CheckNeedForceMeasureAndLayout())) {
281             continue;
282         }
283         pipeline->FlushUITaskWithSingleDirtyNode(node);
284     }
285 }
286 
AddSingleNodeToFlush(const RefPtr<FrameNode> & dirtyNode)287 void UITaskScheduler::AddSingleNodeToFlush(const RefPtr<FrameNode>& dirtyNode)
288 {
289     if (std::find(singleDirtyNodesToFlush_.begin(), singleDirtyNodesToFlush_.end(), dirtyNode) !=
290         singleDirtyNodesToFlush_.end()) {
291         return;
292     }
293     singleDirtyNodesToFlush_.emplace_back(dirtyNode);
294 }
295 
RequestFrameOnLayoutCountExceeds()296 bool UITaskScheduler::RequestFrameOnLayoutCountExceeds()
297 {
298     if (layoutedCount_ < ENDORSE_LAYOUT_COUNT) {
299         return false;
300     }
301     auto pipeline = PipelineContext::GetCurrentContextPtrSafelyWithCheck();
302     if (pipeline) {
303         pipeline->RequestFrame();
304     }
305     return true;
306 }
307 
AddSafeAreaPaddingProcessTask(FrameNode * node)308 void UITaskScheduler::AddSafeAreaPaddingProcessTask(FrameNode* node)
309 {
310     safeAreaPaddingProcessTasks_.insert(node);
311 }
312 
RemoveSafeAreaPaddingProcessTask(FrameNode * node)313 void UITaskScheduler::RemoveSafeAreaPaddingProcessTask(FrameNode* node)
314 {
315     safeAreaPaddingProcessTasks_.erase(node);
316 }
317 
FlushSafeAreaPaddingProcess()318 void UITaskScheduler::FlushSafeAreaPaddingProcess()
319 {
320     if (safeAreaPaddingProcessTasks_.empty()) {
321         return;
322     }
323     auto iter = safeAreaPaddingProcessTasks_.begin();
324     while (iter != safeAreaPaddingProcessTasks_.end()) {
325         auto node = *iter;
326         if (!node) {
327             iter = safeAreaPaddingProcessTasks_.erase(iter);
328         } else {
329             node->ProcessSafeAreaPadding();
330             ++iter;
331         }
332     }
333     // clear caches after all process tasks
334     iter = safeAreaPaddingProcessTasks_.begin();
335     while (iter != safeAreaPaddingProcessTasks_.end()) {
336         auto node = *iter;
337         if (!node) {
338             iter = safeAreaPaddingProcessTasks_.erase(iter);
339         } else {
340             const auto& geometryNode = node->GetGeometryNode();
341             if (geometryNode) {
342                 geometryNode->ResetAccumulatedSafeAreaPadding();
343             }
344             ++iter;
345         }
346     }
347 }
348 
AddPredictTask(PredictTask && task)349 void UITaskScheduler::AddPredictTask(PredictTask&& task)
350 {
351     predictTask_.push_back(std::move(task));
352 }
353 
FlushPredictTask(int64_t deadline,bool canUseLongPredictTask)354 void UITaskScheduler::FlushPredictTask(int64_t deadline, bool canUseLongPredictTask)
355 {
356     decltype(predictTask_) tasks(std::move(predictTask_));
357     for (const auto& task : tasks) {
358         if (task) {
359             task(deadline, canUseLongPredictTask);
360         }
361     }
362 }
363 
CleanUp()364 void UITaskScheduler::CleanUp()
365 {
366     dirtyLayoutNodes_.clear();
367     dirtyRenderNodes_.clear();
368 }
369 
isEmpty()370 bool UITaskScheduler::isEmpty()
371 {
372     return dirtyLayoutNodes_.empty() && dirtyRenderNodes_.empty();
373 }
374 
IsPredictTaskEmpty()375 bool UITaskScheduler::IsPredictTaskEmpty()
376 {
377     return predictTask_.empty();
378 }
379 
AddAfterLayoutTask(std::function<void ()> && task,bool isFlushInImplicitAnimationTask)380 void UITaskScheduler::AddAfterLayoutTask(std::function<void()>&& task, bool isFlushInImplicitAnimationTask)
381 {
382     if (isFlushInImplicitAnimationTask) {
383         afterLayoutCallbacksInImplicitAnimationTask_.emplace_back(std::move(task));
384     } else {
385         afterLayoutTasks_.emplace_back(std::move(task));
386     }
387 }
388 
AddPersistAfterLayoutTask(std::function<void ()> && task)389 void UITaskScheduler::AddPersistAfterLayoutTask(std::function<void()>&& task)
390 {
391     persistAfterLayoutTasks_.emplace_back(std::move(task));
392     LOGI("AddPersistAfterLayoutTask size: %{public}u", static_cast<uint32_t>(persistAfterLayoutTasks_.size()));
393 }
394 
FlushAfterLayoutTask()395 void UITaskScheduler::FlushAfterLayoutTask()
396 {
397     decltype(afterLayoutTasks_) tasks(std::move(afterLayoutTasks_));
398     for (const auto& task : tasks) {
399         if (task) {
400             task();
401         }
402     }
403     // flush correct rect again and flush dirty node again
404     FlushPersistAfterLayoutTask();
405 }
406 
FlushAfterLayoutCallbackInImplicitAnimationTask()407 void UITaskScheduler::FlushAfterLayoutCallbackInImplicitAnimationTask()
408 {
409     decltype(afterLayoutCallbacksInImplicitAnimationTask_) tasks(
410         std::move(afterLayoutCallbacksInImplicitAnimationTask_));
411     for (const auto& task : tasks) {
412         if (task) {
413             task();
414         }
415     }
416 }
417 
FlushPersistAfterLayoutTask()418 void UITaskScheduler::FlushPersistAfterLayoutTask()
419 {
420     // only execute after layout
421     if (persistAfterLayoutTasks_.empty()) {
422         return;
423     }
424     ACE_SCOPED_TRACE("UITaskScheduler::FlushPersistAfterLayoutTask");
425     for (const auto& task : persistAfterLayoutTasks_) {
426         if (task) {
427             task();
428         }
429     }
430 }
431 
AddAfterRenderTask(std::function<void ()> && task)432 void UITaskScheduler::AddAfterRenderTask(std::function<void()>&& task)
433 {
434     afterRenderTasks_.emplace_back(std::move(task));
435 }
436 
FlushAfterRenderTask()437 void UITaskScheduler::FlushAfterRenderTask()
438 {
439     ACE_SCOPED_TRACE("UITaskScheduler::FlushAfterRenderTask");
440     decltype(afterRenderTasks_) tasks(std::move(afterRenderTasks_));
441     for (const auto& task : tasks) {
442         if (task) {
443             task();
444         }
445     }
446 }
447 
448 } // namespace OHOS::Ace::NG
449