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 "rs_trace.h"
21 #include <csignal>
22
23 namespace OHOS {
24 namespace Rosen {
25 struct MemoryHook {
26 void* virtualPtr; // RSRenderNodeDrawableAdapter vtable
27 void* ptr; // enable_shared_from_this's __ptr__*
28 uint32_t low; // enable_shared_fromthis's __ref__* low-bits
29 uint32_t high; // enable_shared_fromthis's __ref__* high-bits
30
ProtectOHOS::Rosen::MemoryHook31 inline void Protect()
32 {
33 static const int sigNo = 42; // Report to HiViewOcean
34 static const bool isBeta = RSSystemProperties::GetVersionType() == "beta";
35 if (CheckIsNotValid()) {
36 if (isBeta) {
37 RS_LOGE("Drawable Protect %{public}u %{public}u", high, low);
38 raise(sigNo);
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 }
77
ReleaseNodeBucket()78 void RSRenderNodeGC::ReleaseNodeBucket()
79 {
80 static auto callback = [] (uint32_t size, bool isHigh) {
81 if (isHigh) {
82 ROSEN_LOGW("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
83 "exceed high threshold, cur[%{public}u]", size);
84 return;
85 }
86 ROSEN_LOGI("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
87 "recover below low threshold, cur[%{public}u]", size);
88 return;
89 };
90 std::vector<RSRenderNode*> toDele;
91 uint32_t remainBucketSize;
92 {
93 std::lock_guard<std::mutex> lock(nodeMutex_);
94 if (nodeBucket_.empty()) {
95 return;
96 }
97 toDele.swap(nodeBucket_.front());
98 nodeBucket_.pop();
99 remainBucketSize = nodeBucket_.size();
100 }
101 nodeBucketThrDetector_.Detect(remainBucketSize, callback);
102 RS_TRACE_NAME_FMT("ReleaseNodeMemory %zu, remain node buckets %u", toDele.size(), remainBucketSize);
103 for (auto ptr : toDele) {
104 if (ptr) {
105 #if defined(__aarch64__)
106 static const bool isPhone = RSSystemProperties::IsPhoneType();
107 if (isPhone) {
108 auto* hook = reinterpret_cast<MemoryHook*>(ptr);
109 hook->Protect();
110 }
111 #endif
112 delete ptr;
113 ptr = nullptr;
114 }
115 }
116 }
117
ReleaseNodeMemory()118 void RSRenderNodeGC::ReleaseNodeMemory()
119 {
120 RS_TRACE_FUNC();
121 uint32_t remainBucketSize;
122 {
123 std::lock_guard<std::mutex> lock(nodeMutex_);
124 if (nodeBucket_.empty()) {
125 return;
126 }
127 remainBucketSize = nodeBucket_.size();
128 }
129 nodeGCLevel_ = JudgeGCLevel(remainBucketSize);
130 if (mainTask_) {
131 auto task = [this]() {
132 if (isEnable_.load() == false &&
133 nodeGCLevel_ != GCLevel::IMMEDIATE) {
134 return;
135 }
136 ReleaseNodeBucket();
137 ReleaseNodeMemory();
138 };
139 mainTask_(task, DELETE_NODE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(nodeGCLevel_));
140 } else {
141 ReleaseNodeBucket();
142 }
143 }
144
DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter * ptr)145 void RSRenderNodeGC::DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
146 {
147 RSRenderNodeGC::Instance().DrawableDestructorInner(ptr);
148 }
149
DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter * ptr)150 void RSRenderNodeGC::DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
151 {
152 std::lock_guard<std::mutex> lock(drawableMutex_);
153 if (drawableBucket_.size() > 0) {
154 auto& bucket = drawableBucket_.back();
155 if (bucket.size() < BUCKET_MAX_SIZE) {
156 bucket.push_back(ptr);
157 } else {
158 drawableBucket_.push({ptr});
159 }
160 } else {
161 drawableBucket_.push({ptr});
162 }
163 }
164
ReleaseDrawableBucket()165 void RSRenderNodeGC::ReleaseDrawableBucket()
166 {
167 static auto callback = [] (uint32_t size, bool isHigh) {
168 if (isHigh) {
169 ROSEN_LOGW("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
170 "exceed high threshold, cur[%{public}u]", size);
171 return;
172 }
173 ROSEN_LOGI("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
174 "recover below low threshold, cur[%{public}u]", size);
175 return;
176 };
177 std::vector<DrawableV2::RSRenderNodeDrawableAdapter*> toDele;
178 uint32_t remainBucketSize;
179 {
180 std::lock_guard<std::mutex> lock(drawableMutex_);
181 if (drawableBucket_.empty()) {
182 return;
183 }
184 toDele.swap(drawableBucket_.front());
185 drawableBucket_.pop();
186 remainBucketSize = drawableBucket_.size();
187 }
188 drawableBucketThrDetector_.Detect(remainBucketSize, callback);
189 RS_TRACE_NAME_FMT("ReleaseDrawableMemory %zu, remain drawable buckets %u", toDele.size(), remainBucketSize);
190 for (auto ptr : toDele) {
191 if (ptr) {
192 delete ptr;
193 ptr = nullptr;
194 }
195 }
196 }
197
ReleaseDrawableMemory()198 void RSRenderNodeGC::ReleaseDrawableMemory()
199 {
200 uint32_t remainBucketSize;
201 {
202 std::lock_guard<std::mutex> lock(drawableMutex_);
203 if (drawableBucket_.empty()) {
204 return;
205 }
206 remainBucketSize = drawableBucket_.size();
207 }
208 drawableGCLevel_ = JudgeGCLevel(remainBucketSize);
209 if (renderTask_) {
210 auto task = []() {
211 RSRenderNodeGC::Instance().ReleaseDrawableBucket();
212 RSRenderNodeGC::Instance().ReleaseDrawableMemory();
213 };
214 renderTask_(task, DELETE_DRAWABLE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(drawableGCLevel_));
215 } else {
216 ReleaseDrawableBucket();
217 }
218 }
219
AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode> & node)220 void RSRenderNodeGC::AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode>& node)
221 {
222 if (offTreeBucket_.size() > 0) {
223 auto& bucket = offTreeBucket_.back();
224 if (bucket.size() < OFF_TREE_BUCKET_MAX_SIZE) {
225 bucket.emplace_back(node);
226 } else {
227 offTreeBucket_.push({node});
228 }
229 } else {
230 offTreeBucket_.push({node});
231 }
232 }
233
ReleaseOffTreeNodeBucket()234 void RSRenderNodeGC::ReleaseOffTreeNodeBucket()
235 {
236 static auto callback = [] (uint32_t size, bool isHigh) {
237 if (isHigh) {
238 ROSEN_LOGW("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
239 "exceed high threshold, cur[%{public}u]", size);
240 return;
241 }
242 ROSEN_LOGI("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
243 "recover below low threshold, cur[%{public}u]", size);
244 return;
245 };
246 std::vector<std::shared_ptr<RSBaseRenderNode>> toRemove;
247 if (offTreeBucket_.empty()) {
248 return;
249 }
250 toRemove.swap(offTreeBucket_.front());
251 offTreeBucket_.pop();
252 uint32_t remainBucketSize = offTreeBucket_.size();
253 offTreeBucketThrDetector_.Detect(remainBucketSize, callback);
254 RS_TRACE_NAME_FMT("ReleaseOffTreeNodeBucket %d, remain offTree buckets %u", toRemove.size(), remainBucketSize);
255 for (const auto& node : toRemove) {
256 if (!node) {
257 continue;
258 }
259 auto parent = node->GetParent().lock();
260 if (parent) {
261 parent->RemoveChildFromFulllist(node->GetId());
262 }
263 node->RemoveFromTree(false);
264 }
265 }
266
ReleaseFromTree()267 void RSRenderNodeGC::ReleaseFromTree()
268 {
269 if (offTreeBucket_.empty()) {
270 return;
271 }
272 if (mainTask_) {
273 auto task = [this]() {
274 if (!isEnable_.load()) {
275 return;
276 }
277 ReleaseOffTreeNodeBucket();
278 ReleaseFromTree();
279 };
280 mainTask_(task, OFF_TREE_TASK, 0, AppExecFwk::EventQueue::Priority::IDLE);
281 } else {
282 ReleaseOffTreeNodeBucket();
283 }
284 }
285
JudgeGCLevel(uint32_t remainBucketSize)286 GCLevel RSRenderNodeGC::JudgeGCLevel(uint32_t remainBucketSize)
287 {
288 if (remainBucketSize < GC_LEVEL_THR_LOW) {
289 return GCLevel::IDLE;
290 } else if (remainBucketSize < GC_LEVEL_THR_HIGH) {
291 return GCLevel::LOW;
292 } else if (remainBucketSize < GC_LEVEL_THR_IMMEDIATE) {
293 return GCLevel::HIGH;
294 } else {
295 return GCLevel::IMMEDIATE;
296 }
297 }
298
299 } // namespace Rosen
300 } // namespace OHOS
301