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 
22 namespace OHOS {
23 namespace Rosen {
Instance()24 RSRenderNodeGC& RSRenderNodeGC::Instance()
25 {
26     static RSRenderNodeGC instance;
27     return instance;
28 }
29 
NodeDestructor(RSRenderNode * ptr)30 void RSRenderNodeGC::NodeDestructor(RSRenderNode* ptr)
31 {
32     RSRenderNodeGC::Instance().NodeDestructorInner(ptr);
33 }
34 
NodeDestructorInner(RSRenderNode * ptr)35 void RSRenderNodeGC::NodeDestructorInner(RSRenderNode* ptr)
36 {
37     std::lock_guard<std::mutex> lock(nodeMutex_);
38     if (nodeBucket_.size() > 0) {
39         auto& bucket = nodeBucket_.back();
40         if (bucket.size() < BUCKET_MAX_SIZE) {
41             bucket.push_back(ptr);
42         } else {
43             nodeBucket_.push({ptr});
44         }
45     } else {
46         nodeBucket_.push({ptr});
47     }
48 }
49 
ReleaseNodeBucket()50 void RSRenderNodeGC::ReleaseNodeBucket()
51 {
52     static auto callback = [] (uint32_t size, bool isHigh) {
53         if (isHigh) {
54             ROSEN_LOGW("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
55                 "exceed high threshold, cur[%{public}u]", size);
56             return;
57         }
58         ROSEN_LOGI("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
59             "recover below low threshold, cur[%{public}u]", size);
60         return;
61     };
62     std::vector<RSRenderNode*> toDele;
63     uint32_t remainBucketSize;
64     {
65         std::lock_guard<std::mutex> lock(nodeMutex_);
66         if (nodeBucket_.empty()) {
67             return;
68         }
69         toDele.swap(nodeBucket_.front());
70         nodeBucket_.pop();
71         remainBucketSize = nodeBucket_.size();
72     }
73     nodeBucketThrDetector_.Detect(remainBucketSize, callback);
74     RS_TRACE_NAME_FMT("ReleaseNodeMemory %zu, remain node buckets %u", toDele.size(), remainBucketSize);
75     for (auto ptr : toDele) {
76         if (ptr) {
77             delete ptr;
78             ptr = nullptr;
79         }
80     }
81 }
82 
ReleaseNodeMemory()83 void RSRenderNodeGC::ReleaseNodeMemory()
84 {
85     RS_TRACE_FUNC();
86     uint32_t remainBucketSize;
87     {
88         std::lock_guard<std::mutex> lock(nodeMutex_);
89         if (nodeBucket_.empty()) {
90             return;
91         }
92         remainBucketSize = nodeBucket_.size();
93     }
94     nodeGCLevel_ = JudgeGCLevel(remainBucketSize);
95     if (mainTask_) {
96         auto task = [this]() {
97             if (isEnable_.load() == false &&
98                 nodeGCLevel_ != GCLevel::IMMEDIATE) {
99                 return;
100             }
101             ReleaseNodeBucket();
102             ReleaseNodeMemory();
103         };
104         mainTask_(task, DELETE_NODE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(nodeGCLevel_));
105     } else {
106         ReleaseNodeBucket();
107     }
108 }
109 
DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter * ptr)110 void RSRenderNodeGC::DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
111 {
112     RSRenderNodeGC::Instance().DrawableDestructorInner(ptr);
113 }
114 
DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter * ptr)115 void RSRenderNodeGC::DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
116 {
117     std::lock_guard<std::mutex> lock(drawableMutex_);
118     if (drawableBucket_.size() > 0) {
119         auto& bucket = drawableBucket_.back();
120         if (bucket.size() < BUCKET_MAX_SIZE) {
121             bucket.push_back(ptr);
122         } else {
123             drawableBucket_.push({ptr});
124         }
125     } else {
126         drawableBucket_.push({ptr});
127     }
128 }
129 
ReleaseDrawableBucket()130 void RSRenderNodeGC::ReleaseDrawableBucket()
131 {
132     static auto callback = [] (uint32_t size, bool isHigh) {
133         if (isHigh) {
134             ROSEN_LOGW("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
135                 "exceed high threshold, cur[%{public}u]", size);
136             return;
137         }
138         ROSEN_LOGI("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
139             "recover below low threshold, cur[%{public}u]", size);
140         return;
141     };
142     std::vector<DrawableV2::RSRenderNodeDrawableAdapter*> toDele;
143     uint32_t remainBucketSize;
144     {
145         std::lock_guard<std::mutex> lock(drawableMutex_);
146         if (drawableBucket_.empty()) {
147             return;
148         }
149         toDele.swap(drawableBucket_.front());
150         drawableBucket_.pop();
151         remainBucketSize = drawableBucket_.size();
152     }
153     drawableBucketThrDetector_.Detect(remainBucketSize, callback);
154     RS_TRACE_NAME_FMT("ReleaseDrawableMemory %zu, remain drawable buckets %u", toDele.size(), remainBucketSize);
155     for (auto ptr : toDele) {
156         if (ptr) {
157             delete ptr;
158             ptr = nullptr;
159         }
160     }
161 }
162 
ReleaseDrawableMemory()163 void RSRenderNodeGC::ReleaseDrawableMemory()
164 {
165     uint32_t remainBucketSize;
166     {
167         std::lock_guard<std::mutex> lock(drawableMutex_);
168         if (drawableBucket_.empty()) {
169             return;
170         }
171         remainBucketSize = drawableBucket_.size();
172     }
173     drawableGCLevel_ = JudgeGCLevel(remainBucketSize);
174     if (renderTask_) {
175         auto task = []() {
176             RSRenderNodeGC::Instance().ReleaseDrawableBucket();
177             RSRenderNodeGC::Instance().ReleaseDrawableMemory();
178         };
179         renderTask_(task, DELETE_DRAWABLE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(drawableGCLevel_));
180     } else {
181         ReleaseDrawableBucket();
182     }
183 }
184 
AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode> & node)185 void RSRenderNodeGC::AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode>& node)
186 {
187     if (offTreeBucket_.size() > 0) {
188         auto& bucket = offTreeBucket_.back();
189         if (bucket.size() < OFF_TREE_BUCKET_MAX_SIZE) {
190             bucket.emplace_back(node);
191         } else {
192             offTreeBucket_.push({node});
193         }
194     } else {
195         offTreeBucket_.push({node});
196     }
197 }
198 
ReleaseOffTreeNodeBucket()199 void RSRenderNodeGC::ReleaseOffTreeNodeBucket()
200 {
201     static auto callback = [] (uint32_t size, bool isHigh) {
202         if (isHigh) {
203             ROSEN_LOGW("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
204                 "exceed high threshold, cur[%{public}u]", size);
205             return;
206         }
207         ROSEN_LOGI("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
208             "recover below low threshold, cur[%{public}u]", size);
209         return;
210     };
211     std::vector<std::shared_ptr<RSBaseRenderNode>> toRemove;
212     if (offTreeBucket_.empty()) {
213         return;
214     }
215     toRemove.swap(offTreeBucket_.front());
216     offTreeBucket_.pop();
217     uint32_t remainBucketSize = offTreeBucket_.size();
218     offTreeBucketThrDetector_.Detect(remainBucketSize, callback);
219     RS_TRACE_NAME_FMT("ReleaseOffTreeNodeBucket %d, remain offTree buckets %u", toRemove.size(), remainBucketSize);
220     for (const auto& node : toRemove) {
221         if (!node) {
222             continue;
223         }
224         auto parent = node->GetParent().lock();
225         if (parent) {
226             parent->RemoveChildFromFulllist(node->GetId());
227         }
228         node->RemoveFromTree(false);
229     }
230 }
231 
ReleaseFromTree()232 void RSRenderNodeGC::ReleaseFromTree()
233 {
234     if (offTreeBucket_.empty()) {
235         return;
236     }
237     if (mainTask_) {
238         auto task = [this]() {
239             if (!isEnable_.load()) {
240                 return;
241             }
242             ReleaseOffTreeNodeBucket();
243             ReleaseFromTree();
244         };
245         mainTask_(task, OFF_TREE_TASK, 0, AppExecFwk::EventQueue::Priority::IDLE);
246     } else {
247         ReleaseOffTreeNodeBucket();
248     }
249 }
250 
JudgeGCLevel(uint32_t remainBucketSize)251 GCLevel RSRenderNodeGC::JudgeGCLevel(uint32_t remainBucketSize)
252 {
253     if (remainBucketSize < GC_LEVEL_THR_LOW) {
254         return GCLevel::IDLE;
255     } else if (remainBucketSize < GC_LEVEL_THR_HIGH) {
256         return GCLevel::LOW;
257     } else if (remainBucketSize < GC_LEVEL_THR_IMMEDIATE) {
258         return GCLevel::HIGH;
259     } else {
260         return GCLevel::IMMEDIATE;
261     }
262 }
263 
264 } // namespace Rosen
265 } // namespace OHOS
266