• 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 
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