• 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 "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