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