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 #include "base/log/frame_report.h"
21 #ifdef FFRT_EXISTS
22 #include "base/longframe/long_frame_report.h"
23 #endif
24 #include "base/memory/referenced.h"
25 #include "base/utils/time_util.h"
26 #include "base/utils/utils.h"
27 #include "core/common/thread_checker.h"
28 #include "core/components_ng/base/frame_node.h"
29 #include "core/components_ng/pattern/custom/custom_node.h"
30 #include "core/components_v2/inspector/inspector_constants.h"
31 #include "core/pipeline_ng/pipeline_context.h"
32
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr char LIBFFRT_LIB64_PATH[] = "/system/lib64/ndk/libffrt.z.so";
36 }
37 uint64_t UITaskScheduler::frameId_ = 0;
38
UITaskScheduler()39 UITaskScheduler::UITaskScheduler()
40 {
41 if (access(LIBFFRT_LIB64_PATH, F_OK) == -1) {
42 return ;
43 }
44 is64BitSystem_ = true;
45 }
46
~UITaskScheduler()47 UITaskScheduler::~UITaskScheduler()
48 {
49 persistAfterLayoutTasks_.clear();
50 }
51
AddDirtyLayoutNode(const RefPtr<FrameNode> & dirty)52 void UITaskScheduler::AddDirtyLayoutNode(const RefPtr<FrameNode>& dirty)
53 {
54 CHECK_RUN_ON(UI);
55 CHECK_NULL_VOID(dirty);
56 dirtyLayoutNodes_.emplace_back(dirty);
57 }
58
AddDirtyRenderNode(const RefPtr<FrameNode> & dirty)59 void UITaskScheduler::AddDirtyRenderNode(const RefPtr<FrameNode>& dirty)
60 {
61 CHECK_RUN_ON(UI);
62 CHECK_NULL_VOID(dirty);
63 auto result = dirtyRenderNodes_[dirty->GetPageId()].emplace(dirty);
64 if (!result.second) {
65 LOGW("Fail to emplace %{public}s render node", dirty->GetTag().c_str());
66 }
67 }
68
RestoreGeoState()69 void UITaskScheduler::RestoreGeoState()
70 {
71 auto pipeline = PipelineContext::GetCurrentContext();
72 CHECK_NULL_VOID(pipeline);
73 auto safeAreaManager = pipeline->GetSafeAreaManager();
74 CHECK_NULL_VOID(safeAreaManager);
75 if (safeAreaManager) {
76 std::set<WeakPtr<FrameNode>> geoRestoreNodes = safeAreaManager->GetGeoRestoreNodes();
77 for (auto& node : geoRestoreNodes) {
78 auto frameNode = node.Upgrade();
79 if (frameNode && frameNode->GetTag() != V2::PAGE_ETS_TAG) {
80 frameNode->RestoreGeoState();
81 }
82 }
83 }
84 }
85
ExpandSafeArea()86 void UITaskScheduler::ExpandSafeArea()
87 {
88 auto pipeline = PipelineContext::GetCurrentContext();
89 CHECK_NULL_VOID(pipeline);
90 auto safeAreaManager = pipeline->GetSafeAreaManager();
91 CHECK_NULL_VOID(safeAreaManager);
92 safeAreaManager->ExpandSafeArea();
93 }
94
FlushSyncGeometryNodeTasks()95 void UITaskScheduler::FlushSyncGeometryNodeTasks()
96 {
97 ExpandSafeArea();
98 auto tasks = std::move(syncGeometryNodeTasks_);
99 for (auto& task : tasks) {
100 if (task) {
101 task();
102 }
103 }
104 }
105
FlushLayoutTask(bool forceUseMainThread)106 void UITaskScheduler::FlushLayoutTask(bool forceUseMainThread)
107 {
108 CHECK_RUN_ON(UI);
109 ACE_FUNCTION_TRACE();
110 if (dirtyLayoutNodes_.empty()) {
111 return;
112 }
113 if (isLayouting_) {
114 LOGF("you are already in flushing layout!");
115 abort();
116 }
117
118 #ifdef FFRT_EXISTS
119 // Pause GC during long frame
120 std::unique_ptr<ILongFrame> longFrame = std::make_unique<ILongFrame>();
121 if (is64BitSystem_) {
122 longFrame->ReportStartEvent();
123 }
124 #endif
125
126 isLayouting_ = true;
127 auto dirtyLayoutNodes = std::move(dirtyLayoutNodes_);
128 PageDirtySet dirtyLayoutNodesSet(dirtyLayoutNodes.begin(), dirtyLayoutNodes.end());
129
130 // Priority task creation
131 int64_t time = 0;
132 for (auto&& node : dirtyLayoutNodesSet) {
133 // need to check the node is destroying or not before CreateLayoutTask
134 if (!node || node->IsInDestroying()) {
135 continue;
136 }
137 time = GetSysTimestamp();
138 node->CreateLayoutTask(forceUseMainThread);
139 time = GetSysTimestamp() - time;
140 if (frameInfo_ != nullptr) {
141 frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::LAYOUT);
142 }
143 }
144 FlushSyncGeometryNodeTasks();
145 #ifdef FFRT_EXISTS
146 if (is64BitSystem_) {
147 longFrame->ReportEndEvent();
148 }
149 #endif
150
151 isLayouting_ = false;
152 }
153
FlushRenderTask(bool forceUseMainThread)154 void UITaskScheduler::FlushRenderTask(bool forceUseMainThread)
155 {
156 CHECK_RUN_ON(UI);
157 if (FrameReport::GetInstance().GetEnable()) {
158 FrameReport::GetInstance().BeginFlushRender();
159 }
160 auto dirtyRenderNodes = std::move(dirtyRenderNodes_);
161 // Priority task creation
162 int64_t time = 0;
163 for (auto&& pageNodes : dirtyRenderNodes) {
164 ACE_SCOPED_TRACE("FlushRenderTask %zu", pageNodes.second.size());
165 for (auto&& node : pageNodes.second) {
166 if (!node) {
167 continue;
168 }
169 if (node->IsInDestroying()) {
170 continue;
171 }
172 time = GetSysTimestamp();
173 auto task = node->CreateRenderTask(forceUseMainThread);
174 if (task) {
175 if (forceUseMainThread || (task->GetTaskThreadType() == MAIN_TASK)) {
176 (*task)();
177 time = GetSysTimestamp() - time;
178 if (frameInfo_ != nullptr) {
179 frameInfo_->AddTaskInfo(node->GetTag(), node->GetId(), time, FrameInfo::TaskType::RENDER);
180 }
181 }
182 }
183 }
184 }
185 }
186
NeedAdditionalLayout()187 bool UITaskScheduler::NeedAdditionalLayout()
188 {
189 bool ret = false;
190 ElementRegister::GetInstance()->ReSyncGeometryTransition();
191
192 RootDirtyMap dirtyLayoutNodesMap;
193 for (auto&& dirty : dirtyLayoutNodes_) {
194 dirtyLayoutNodesMap[dirty->GetPageId()].emplace(dirty);
195 }
196
197 for (auto&& pageNodes : dirtyLayoutNodesMap) {
198 for (auto&& node : pageNodes.second) {
199 if (!node || node->IsInDestroying() || !node->GetLayoutProperty()) {
200 continue;
201 }
202 const auto& geometryTransition = node->GetLayoutProperty()->GetGeometryTransition();
203 if (geometryTransition != nullptr) {
204 ret |= geometryTransition->OnAdditionalLayout(node);
205 }
206 }
207 }
208 return ret;
209 }
210
FlushTask()211 void UITaskScheduler::FlushTask()
212 {
213 CHECK_RUN_ON(UI);
214 ACE_SCOPED_TRACE("UITaskScheduler::FlushTask");
215 FlushLayoutTask();
216 if (NeedAdditionalLayout()) {
217 FlushLayoutTask();
218 }
219 if (!afterLayoutTasks_.empty()) {
220 FlushAfterLayoutTask();
221 }
222 FlushDelayJsActive();
223 ElementRegister::GetInstance()->ClearPendingRemoveNodes();
224 FlushRenderTask();
225 }
226
SetJSViewActive(bool active,WeakPtr<CustomNode> custom)227 void UITaskScheduler::SetJSViewActive(bool active, WeakPtr<CustomNode> custom)
228 {
229 auto iter = delayJsActiveNodes_.find(custom);
230 if (iter != delayJsActiveNodes_.end()) {
231 iter->second = active;
232 } else {
233 delayJsActiveNodes_.emplace(custom, active);
234 }
235 }
236
FlushDelayJsActive()237 void UITaskScheduler::FlushDelayJsActive()
238 {
239 auto nodes = std::move(delayJsActiveNodes_);
240 for (auto [node, active] : nodes) {
241 auto customNode = node.Upgrade();
242 if (customNode) {
243 if (customNode->GetJsActive() != active) {
244 customNode->SetJsActive(active);
245 customNode->FireSetActiveFunc(active);
246 }
247 }
248 }
249 }
250
AddPredictTask(PredictTask && task)251 void UITaskScheduler::AddPredictTask(PredictTask&& task)
252 {
253 predictTask_.push_back(std::move(task));
254 }
255
FlushPredictTask(int64_t deadline,bool canUseLongPredictTask)256 void UITaskScheduler::FlushPredictTask(int64_t deadline, bool canUseLongPredictTask)
257 {
258 decltype(predictTask_) tasks(std::move(predictTask_));
259 for (const auto& task : tasks) {
260 if (task) {
261 task(deadline, canUseLongPredictTask);
262 }
263 }
264 }
265
CleanUp()266 void UITaskScheduler::CleanUp()
267 {
268 dirtyLayoutNodes_.clear();
269 dirtyRenderNodes_.clear();
270 }
271
isEmpty()272 bool UITaskScheduler::isEmpty()
273 {
274 return dirtyLayoutNodes_.empty() && dirtyRenderNodes_.empty();
275 }
276
AddAfterLayoutTask(std::function<void ()> && task)277 void UITaskScheduler::AddAfterLayoutTask(std::function<void()>&& task)
278 {
279 afterLayoutTasks_.emplace_back(std::move(task));
280 }
281
AddPersistAfterLayoutTask(std::function<void ()> && task)282 void UITaskScheduler::AddPersistAfterLayoutTask(std::function<void()>&& task)
283 {
284 persistAfterLayoutTasks_.emplace_back(std::move(task));
285 LOGI("AddPersistAfterLayoutTask size: %{public}u", static_cast<uint32_t>(persistAfterLayoutTasks_.size()));
286 }
287
FlushAfterLayoutTask()288 void UITaskScheduler::FlushAfterLayoutTask()
289 {
290 decltype(afterLayoutTasks_) tasks(std::move(afterLayoutTasks_));
291 for (const auto& task : tasks) {
292 if (task) {
293 task();
294 }
295 }
296 // flush correct rect again and flush dirty node again
297 FlushPersistAfterLayoutTask();
298 }
299
FlushPersistAfterLayoutTask()300 void UITaskScheduler::FlushPersistAfterLayoutTask()
301 {
302 // only execute after layout
303 if (persistAfterLayoutTasks_.empty()) {
304 return;
305 }
306 ACE_SCOPED_TRACE("UITaskScheduler::FlushPersistAfterLayoutTask");
307 for (const auto& task : persistAfterLayoutTasks_) {
308 if (task) {
309 task();
310 }
311 }
312 }
313
AddAfterRenderTask(std::function<void ()> && task)314 void UITaskScheduler::AddAfterRenderTask(std::function<void()>&& task)
315 {
316 afterRenderTasks_.emplace_back(std::move(task));
317 }
318
FlushAfterRenderTask()319 void UITaskScheduler::FlushAfterRenderTask()
320 {
321 ACE_SCOPED_TRACE("UITaskScheduler::FlushAfterRenderTask");
322 decltype(afterRenderTasks_) tasks(std::move(afterRenderTasks_));
323 for (const auto& task : tasks) {
324 if (task) {
325 task();
326 }
327 }
328 }
329
330 } // namespace OHOS::Ace::NG
331