• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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