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