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