1 /*
2 * Copyright (c) 2024 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 "pipeline/rs_render_node_gc.h"
17
18 #include "params/rs_render_params.h"
19 #include "pipeline/rs_render_node.h"
20 #include "pipeline/rs_render_node_allocator.h"
21 #include "platform/common/rs_log.h"
22 #include "rs_trace.h"
23 #include <csignal>
24
25 namespace OHOS {
26 namespace Rosen {
27 struct MemoryHook {
28 void* virtualPtr; // RSRenderNodeDrawableAdapter vtable
29 void* ptr; // enable_shared_from_this's __ptr__*
30 uint32_t low; // enable_shared_fromthis's __ref__* low-bits
31 uint32_t high; // enable_shared_fromthis's __ref__* high-bits
32
ProtectOHOS::Rosen::MemoryHook33 inline void Protect()
34 {
35 static const bool isBeta = RSSystemProperties::GetVersionType() == "beta";
36 if (CheckIsNotValid()) {
37 if (isBeta) {
38 RS_LOGE("Drawable Protect %{public}u %{public}u", high, low);
39 }
40 high = 0;
41 low = 0;
42 }
43 }
44
CheckIsNotValidOHOS::Rosen::MemoryHook45 inline bool CheckIsNotValid()
46 {
47 static constexpr uint32_t THRESHOLD = 0x400u;
48 return low < THRESHOLD;
49 }
50 };
51
Instance()52 RSRenderNodeGC& RSRenderNodeGC::Instance()
53 {
54 static RSRenderNodeGC instance;
55 return instance;
56 }
57
NodeDestructor(RSRenderNode * ptr)58 void RSRenderNodeGC::NodeDestructor(RSRenderNode* ptr)
59 {
60 RSRenderNodeGC::Instance().NodeDestructorInner(ptr);
61 }
62
NodeDestructorInner(RSRenderNode * ptr)63 void RSRenderNodeGC::NodeDestructorInner(RSRenderNode* ptr)
64 {
65 std::lock_guard<std::mutex> lock(nodeMutex_);
66 if (nodeBucket_.size() > 0) {
67 auto& bucket = nodeBucket_.back();
68 if (bucket.size() < BUCKET_MAX_SIZE) {
69 bucket.push_back(ptr);
70 } else {
71 nodeBucket_.push({ptr});
72 }
73 } else {
74 nodeBucket_.push({ptr});
75 }
76 if (ptr != nullptr) {
77 DrawableV2::RSRenderNodeDrawableAdapter::RemoveDrawableFromCache(ptr->GetId());
78 }
79 }
80
IsBucketQueueEmpty()81 bool RSRenderNodeGC::IsBucketQueueEmpty()
82 {
83 std::lock_guard<std::mutex> lock(nodeMutex_);
84 return nodeBucket_.empty();
85 }
86
ReleaseNodeBucket()87 void RSRenderNodeGC::ReleaseNodeBucket()
88 {
89 static auto callback = [] (uint32_t size, bool isHigh) {
90 if (isHigh) {
91 ROSEN_LOGW("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
92 "exceed high threshold, cur[%{public}u]", size);
93 return;
94 }
95 ROSEN_LOGI("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
96 "recover below low threshold, cur[%{public}u]", size);
97 return;
98 };
99 std::vector<RSRenderNode*> toDele;
100 uint32_t remainBucketSize;
101 {
102 std::lock_guard<std::mutex> lock(nodeMutex_);
103 if (nodeBucket_.empty()) {
104 return;
105 }
106 toDele.swap(nodeBucket_.front());
107 nodeBucket_.pop();
108 remainBucketSize = nodeBucket_.size();
109 }
110 nodeBucketThrDetector_.Detect(remainBucketSize, callback);
111 RS_TRACE_NAME_FMT("ReleaseNodeMemory %zu, remain node buckets %u", toDele.size(), remainBucketSize);
112 for (auto ptr : toDele) {
113 if (ptr) {
114 if (RSRenderNodeAllocator::Instance().AddNodeToAllocator(ptr)) {
115 continue;
116 }
117 #if defined(__aarch64__)
118 auto* hook = reinterpret_cast<MemoryHook*>(ptr);
119 hook->Protect();
120 #endif
121 delete ptr;
122 ptr = nullptr;
123 }
124 }
125 }
126
ReleaseNodeMemory()127 void RSRenderNodeGC::ReleaseNodeMemory()
128 {
129 RS_TRACE_FUNC();
130 uint32_t remainBucketSize;
131 {
132 std::lock_guard<std::mutex> lock(nodeMutex_);
133 if (nodeBucket_.empty()) {
134 return;
135 }
136 remainBucketSize = nodeBucket_.size();
137 }
138 nodeGCLevel_ = JudgeGCLevel(remainBucketSize);
139 if (mainTask_) {
140 auto task = [this]() {
141 if (isEnable_.load() == false &&
142 nodeGCLevel_ != GCLevel::IMMEDIATE) {
143 return;
144 }
145 ReleaseNodeBucket();
146 ReleaseNodeMemory();
147 };
148 mainTask_(task, DELETE_NODE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(nodeGCLevel_));
149 } else {
150 ReleaseNodeBucket();
151 }
152 }
153
DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter * ptr)154 void RSRenderNodeGC::DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
155 {
156 RSRenderNodeGC::Instance().DrawableDestructorInner(ptr);
157 }
158
DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter * ptr)159 void RSRenderNodeGC::DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
160 {
161 std::lock_guard<std::mutex> lock(drawableMutex_);
162 if (drawableBucket_.size() > 0) {
163 auto& bucket = drawableBucket_.back();
164 if (bucket.size() < BUCKET_MAX_SIZE) {
165 bucket.push_back(ptr);
166 } else {
167 drawableBucket_.push({ptr});
168 }
169 } else {
170 drawableBucket_.push({ptr});
171 }
172 }
173
ReleaseDrawableBucket()174 void RSRenderNodeGC::ReleaseDrawableBucket()
175 {
176 static auto callback = [] (uint32_t size, bool isHigh) {
177 if (isHigh) {
178 ROSEN_LOGW("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
179 "exceed high threshold, cur[%{public}u]", size);
180 return;
181 }
182 ROSEN_LOGI("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
183 "recover below low threshold, cur[%{public}u]", size);
184 return;
185 };
186 std::vector<DrawableV2::RSRenderNodeDrawableAdapter*> toDele;
187 uint32_t remainBucketSize;
188 {
189 std::lock_guard<std::mutex> lock(drawableMutex_);
190 if (drawableBucket_.empty()) {
191 return;
192 }
193 toDele.swap(drawableBucket_.front());
194 drawableBucket_.pop();
195 remainBucketSize = drawableBucket_.size();
196 }
197 drawableBucketThrDetector_.Detect(remainBucketSize, callback);
198 RS_TRACE_NAME_FMT("ReleaseDrawableMemory %zu, remain drawable buckets %u", toDele.size(), remainBucketSize);
199 for (auto ptr : toDele) {
200 if (ptr) {
201 if (RSRenderNodeAllocator::Instance().AddDrawableToAllocator(ptr)) {
202 continue;
203 }
204 delete ptr;
205 ptr = nullptr;
206 }
207 }
208 }
209
ReleaseDrawableMemory()210 void RSRenderNodeGC::ReleaseDrawableMemory()
211 {
212 uint32_t remainBucketSize;
213 {
214 std::lock_guard<std::mutex> lock(drawableMutex_);
215 if (drawableBucket_.empty()) {
216 return;
217 }
218 remainBucketSize = drawableBucket_.size();
219 }
220 drawableGCLevel_ = JudgeGCLevel(remainBucketSize);
221 if (renderTask_) {
222 auto task = []() {
223 RSRenderNodeGC::Instance().ReleaseDrawableBucket();
224 RSRenderNodeGC::Instance().ReleaseDrawableMemory();
225 };
226 renderTask_(task, DELETE_DRAWABLE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(drawableGCLevel_));
227 } else {
228 ReleaseDrawableBucket();
229 }
230 }
231
AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode> & node)232 void RSRenderNodeGC::AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode>& node)
233 {
234 if (offTreeBucket_.size() > 0) {
235 auto& bucket = offTreeBucket_.back();
236 if (bucket.size() < OFF_TREE_BUCKET_MAX_SIZE) {
237 bucket.emplace_back(node);
238 } else {
239 offTreeBucket_.push({node});
240 }
241 } else {
242 offTreeBucket_.push({node});
243 }
244 }
245
ReleaseOffTreeNodeBucket()246 void RSRenderNodeGC::ReleaseOffTreeNodeBucket()
247 {
248 static auto callback = [] (uint32_t size, bool isHigh) {
249 if (isHigh) {
250 ROSEN_LOGW("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
251 "exceed high threshold, cur[%{public}u]", size);
252 return;
253 }
254 ROSEN_LOGI("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
255 "recover below low threshold, cur[%{public}u]", size);
256 return;
257 };
258 ReleaseOffTreeNodeForBucket(callback);
259 ReleaseOffTreeNodeForBucketMap(callback);
260 }
261
ReleaseFromTree()262 void RSRenderNodeGC::ReleaseFromTree()
263 {
264 if (offTreeBucket_.empty() && offTreeBucketMap_.empty()) {
265 return;
266 }
267 if (mainTask_) {
268 auto task = [this]() {
269 if (!isEnable_.load()) {
270 return;
271 }
272 ReleaseOffTreeNodeBucket();
273 ReleaseFromTree();
274 };
275 mainTask_(task, OFF_TREE_TASK, 0, AppExecFwk::EventQueue::Priority::IDLE);
276 } else {
277 ReleaseOffTreeNodeBucket();
278 }
279 }
280
JudgeGCLevel(uint32_t remainBucketSize)281 GCLevel RSRenderNodeGC::JudgeGCLevel(uint32_t remainBucketSize)
282 {
283 if (remainBucketSize < GC_LEVEL_THR_LOW) {
284 return GCLevel::IDLE;
285 } else if (remainBucketSize < GC_LEVEL_THR_HIGH) {
286 return GCLevel::LOW;
287 } else if (remainBucketSize < GC_LEVEL_THR_IMMEDIATE) {
288 return GCLevel::HIGH;
289 } else {
290 return GCLevel::IMMEDIATE;
291 }
292 }
293
AddToOffTreeNodeBucket(pid_t pid,std::unordered_map<NodeId,std::shared_ptr<RSBaseRenderNode>> & renderNodeMap)294 void RSRenderNodeGC::AddToOffTreeNodeBucket(pid_t pid,
295 std::unordered_map<NodeId, std::shared_ptr<RSBaseRenderNode>>& renderNodeMap)
296 {
297 renderNodeNumsInBucketMap_ += renderNodeMap.size();
298 RS_TRACE_NAME_FMT("AddToOffTreeNodeBucket, size=%u, nodes=%llu",
299 offTreeBucketMap_.size(), renderNodeNumsInBucketMap_);
300 offTreeBucketMap_.push(
301 std::pair<pid_t, std::unordered_map<NodeId, std::shared_ptr<RSBaseRenderNode>>>(
302 pid, std::move(renderNodeMap)));
303 }
304
ReleaseOffTreeNodeForBucket(const RSThresholdDetector<uint32_t>::DetectCallBack & callBack)305 void RSRenderNodeGC::ReleaseOffTreeNodeForBucket(const RSThresholdDetector<uint32_t>::DetectCallBack &callBack)
306 {
307 std::vector<std::shared_ptr<RSBaseRenderNode>> toRemove;
308 if (offTreeBucket_.empty()) {
309 return;
310 }
311 toRemove.swap(offTreeBucket_.front());
312 offTreeBucket_.pop();
313 uint32_t remainBucketSize = offTreeBucket_.size();
314 offTreeBucketThrDetector_.Detect(remainBucketSize, callBack);
315 RS_TRACE_NAME_FMT("ReleaseOffTreeNodeForBucket %d, remain offTree buckets %u", toRemove.size(), remainBucketSize);
316 for (const auto& node : toRemove) {
317 if (!node) {
318 continue;
319 }
320 auto parent = node->GetParent().lock();
321 if (parent) {
322 parent->RemoveChildFromFulllist(node->GetId());
323 }
324 node->RemoveFromTree(false);
325 }
326 }
327
ReleaseOffTreeNodeForBucketMap(const RSThresholdDetector<uint32_t>::DetectCallBack & callBack)328 void RSRenderNodeGC::ReleaseOffTreeNodeForBucketMap(const RSThresholdDetector<uint32_t>::DetectCallBack &callBack)
329 {
330 if (offTreeBucketMap_.empty()) {
331 return;
332 }
333 auto& toRemove = offTreeBucketMap_.front();
334 pid_t pid = toRemove.first;
335 auto& subMap = toRemove.second;
336 uint32_t count = 0;
337 const size_t subMapSize = subMap.size();
338 uint64_t remainBucketSize = renderNodeNumsInBucketMap_ / OFF_TREE_BUCKET_MAX_SIZE +
339 (renderNodeNumsInBucketMap_ % OFF_TREE_BUCKET_MAX_SIZE == 0 ? 0 : 1);
340 offTreeBucketThrDetector_.Detect(remainBucketSize, callBack);
341 RS_TRACE_NAME_FMT("ReleaseOffTreeNodeForBucketMap %d, remain offTree buckets %u",
342 OFF_TREE_BUCKET_MAX_SIZE, subMapSize);
343 for (auto subIter = subMap.begin(); subIter != subMap.end();) {
344 if (count++ >= OFF_TREE_BUCKET_MAX_SIZE) {
345 break;
346 }
347 auto renderNode = subIter->second;
348 if (renderNode == nullptr) {
349 subIter = subMap.erase(subIter);
350 continue;
351 }
352 if (auto parent = renderNode->GetParent().lock()) {
353 parent->RemoveChildFromFulllist(renderNode->GetId());
354 }
355 renderNode->RemoveFromTree(false);
356 renderNode->GetAnimationManager().FilterAnimationByPid(pid);
357 subIter = subMap.erase(subIter);
358 }
359 renderNodeNumsInBucketMap_ -= (subMapSize - subMap.size());
360 if (subMap.empty()) {
361 offTreeBucketMap_.pop();
362 RS_TRACE_NAME_FMT("offTreeBucketMap size=%u", offTreeBucketMap_.size());
363 }
364 }
365 } // namespace Rosen
366 } // namespace OHOS
367