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