• 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 "pipeline/rs_render_node_allocator.h"
21 #include "platform/common/rs_log.h"
22 #include "rs_trace.h"
23 #include <csignal>
24 
25 namespace OHOS {
26 namespace Rosen {
27 struct MemoryHook {
28     void* virtualPtr; // RSRenderNodeDrawableAdapter vtable
29     void* ptr; // enable_shared_from_this's __ptr__*
30     uint32_t low; // enable_shared_fromthis's __ref__* low-bits
31     uint32_t high; // enable_shared_fromthis's __ref__* high-bits
32 
ProtectOHOS::Rosen::MemoryHook33     inline void Protect()
34     {
35         static const bool isBeta = RSSystemProperties::GetVersionType() == "beta";
36         if (CheckIsNotValid()) {
37             if (isBeta) {
38                 RS_LOGE("Drawable Protect %{public}u %{public}u", high, low);
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     if (ptr != nullptr) {
77         DrawableV2::RSRenderNodeDrawableAdapter::RemoveDrawableFromCache(ptr->GetId());
78     }
79 }
80 
IsBucketQueueEmpty()81 bool RSRenderNodeGC::IsBucketQueueEmpty()
82 {
83     std::lock_guard<std::mutex> lock(nodeMutex_);
84     return nodeBucket_.empty();
85 }
86 
ReleaseNodeBucket()87 void RSRenderNodeGC::ReleaseNodeBucket()
88 {
89     static auto callback = [] (uint32_t size, bool isHigh) {
90         if (isHigh) {
91             ROSEN_LOGW("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
92                 "exceed high threshold, cur[%{public}u]", size);
93             return;
94         }
95         ROSEN_LOGI("RSRenderNodeGC::ReleaseNodeBucket remain buckets "
96             "recover below low threshold, cur[%{public}u]", size);
97         return;
98     };
99     std::vector<RSRenderNode*> toDele;
100     uint32_t remainBucketSize;
101     {
102         std::lock_guard<std::mutex> lock(nodeMutex_);
103         if (nodeBucket_.empty()) {
104             return;
105         }
106         toDele.swap(nodeBucket_.front());
107         nodeBucket_.pop();
108         remainBucketSize = nodeBucket_.size();
109     }
110     nodeBucketThrDetector_.Detect(remainBucketSize, callback);
111     RS_TRACE_NAME_FMT("ReleaseNodeMemory %zu, remain node buckets %u", toDele.size(), remainBucketSize);
112     for (auto ptr : toDele) {
113         if (ptr) {
114             if (RSRenderNodeAllocator::Instance().AddNodeToAllocator(ptr)) {
115                 continue;
116             }
117 #if defined(__aarch64__)
118             auto* hook = reinterpret_cast<MemoryHook*>(ptr);
119             hook->Protect();
120 #endif
121             delete ptr;
122             ptr = nullptr;
123         }
124     }
125 }
126 
ReleaseNodeMemory()127 void RSRenderNodeGC::ReleaseNodeMemory()
128 {
129     RS_TRACE_FUNC();
130     uint32_t remainBucketSize;
131     {
132         std::lock_guard<std::mutex> lock(nodeMutex_);
133         if (nodeBucket_.empty()) {
134             return;
135         }
136         remainBucketSize = nodeBucket_.size();
137     }
138     nodeGCLevel_ = JudgeGCLevel(remainBucketSize);
139     if (mainTask_) {
140         auto task = [this]() {
141             if (isEnable_.load() == false &&
142                 nodeGCLevel_ != GCLevel::IMMEDIATE) {
143                 return;
144             }
145             ReleaseNodeBucket();
146             ReleaseNodeMemory();
147         };
148         mainTask_(task, DELETE_NODE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(nodeGCLevel_));
149     } else {
150         ReleaseNodeBucket();
151     }
152 }
153 
DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter * ptr)154 void RSRenderNodeGC::DrawableDestructor(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
155 {
156     RSRenderNodeGC::Instance().DrawableDestructorInner(ptr);
157 }
158 
DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter * ptr)159 void RSRenderNodeGC::DrawableDestructorInner(DrawableV2::RSRenderNodeDrawableAdapter* ptr)
160 {
161     std::lock_guard<std::mutex> lock(drawableMutex_);
162     if (drawableBucket_.size() > 0) {
163         auto& bucket = drawableBucket_.back();
164         if (bucket.size() < BUCKET_MAX_SIZE) {
165             bucket.push_back(ptr);
166         } else {
167             drawableBucket_.push({ptr});
168         }
169     } else {
170         drawableBucket_.push({ptr});
171     }
172 }
173 
ReleaseDrawableBucket()174 void RSRenderNodeGC::ReleaseDrawableBucket()
175 {
176     static auto callback = [] (uint32_t size, bool isHigh) {
177         if (isHigh) {
178             ROSEN_LOGW("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
179                 "exceed high threshold, cur[%{public}u]", size);
180             return;
181         }
182         ROSEN_LOGI("RSRenderNodeGC::ReleaseDrawableBucket remain buckets "
183             "recover below low threshold, cur[%{public}u]", size);
184         return;
185     };
186     std::vector<DrawableV2::RSRenderNodeDrawableAdapter*> toDele;
187     uint32_t remainBucketSize;
188     {
189         std::lock_guard<std::mutex> lock(drawableMutex_);
190         if (drawableBucket_.empty()) {
191             return;
192         }
193         toDele.swap(drawableBucket_.front());
194         drawableBucket_.pop();
195         remainBucketSize = drawableBucket_.size();
196     }
197     drawableBucketThrDetector_.Detect(remainBucketSize, callback);
198     RS_TRACE_NAME_FMT("ReleaseDrawableMemory %zu, remain drawable buckets %u", toDele.size(), remainBucketSize);
199     for (auto ptr : toDele) {
200         if (ptr) {
201             if (RSRenderNodeAllocator::Instance().AddDrawableToAllocator(ptr)) {
202                 continue;
203             }
204             delete ptr;
205             ptr = nullptr;
206         }
207     }
208 }
209 
ReleaseDrawableMemory()210 void RSRenderNodeGC::ReleaseDrawableMemory()
211 {
212     uint32_t remainBucketSize;
213     {
214         std::lock_guard<std::mutex> lock(drawableMutex_);
215         if (drawableBucket_.empty()) {
216             return;
217         }
218         remainBucketSize = drawableBucket_.size();
219     }
220     drawableGCLevel_ = JudgeGCLevel(remainBucketSize);
221     if (renderTask_) {
222         auto task = []() {
223             RSRenderNodeGC::Instance().ReleaseDrawableBucket();
224             RSRenderNodeGC::Instance().ReleaseDrawableMemory();
225         };
226         renderTask_(task, DELETE_DRAWABLE_TASK, 0, static_cast<AppExecFwk::EventQueue::Priority>(drawableGCLevel_));
227     } else {
228         ReleaseDrawableBucket();
229     }
230 }
231 
AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode> & node)232 void RSRenderNodeGC::AddToOffTreeNodeBucket(const std::shared_ptr<RSBaseRenderNode>& node)
233 {
234     if (offTreeBucket_.size() > 0) {
235         auto& bucket = offTreeBucket_.back();
236         if (bucket.size() < OFF_TREE_BUCKET_MAX_SIZE) {
237             bucket.emplace_back(node);
238         } else {
239             offTreeBucket_.push({node});
240         }
241     } else {
242         offTreeBucket_.push({node});
243     }
244 }
245 
ReleaseOffTreeNodeBucket()246 void RSRenderNodeGC::ReleaseOffTreeNodeBucket()
247 {
248     static auto callback = [] (uint32_t size, bool isHigh) {
249         if (isHigh) {
250             ROSEN_LOGW("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
251                 "exceed high threshold, cur[%{public}u]", size);
252             return;
253         }
254         ROSEN_LOGI("RSRenderNodeGC::ReleaseOffTreeNodeBucket remain buckets "
255             "recover below low threshold, cur[%{public}u]", size);
256         return;
257     };
258     ReleaseOffTreeNodeForBucket(callback);
259     ReleaseOffTreeNodeForBucketMap(callback);
260 }
261 
ReleaseFromTree()262 void RSRenderNodeGC::ReleaseFromTree()
263 {
264     if (offTreeBucket_.empty() && offTreeBucketMap_.empty()) {
265         return;
266     }
267     if (mainTask_) {
268         auto task = [this]() {
269             if (!isEnable_.load()) {
270                 return;
271             }
272             ReleaseOffTreeNodeBucket();
273             ReleaseFromTree();
274         };
275         mainTask_(task, OFF_TREE_TASK, 0, AppExecFwk::EventQueue::Priority::IDLE);
276     } else {
277         ReleaseOffTreeNodeBucket();
278     }
279 }
280 
JudgeGCLevel(uint32_t remainBucketSize)281 GCLevel RSRenderNodeGC::JudgeGCLevel(uint32_t remainBucketSize)
282 {
283     if (remainBucketSize < GC_LEVEL_THR_LOW) {
284         return GCLevel::IDLE;
285     } else if (remainBucketSize < GC_LEVEL_THR_HIGH) {
286         return GCLevel::LOW;
287     } else if (remainBucketSize < GC_LEVEL_THR_IMMEDIATE) {
288         return GCLevel::HIGH;
289     } else {
290         return GCLevel::IMMEDIATE;
291     }
292 }
293 
AddToOffTreeNodeBucket(pid_t pid,std::unordered_map<NodeId,std::shared_ptr<RSBaseRenderNode>> & renderNodeMap)294 void RSRenderNodeGC::AddToOffTreeNodeBucket(pid_t pid,
295     std::unordered_map<NodeId, std::shared_ptr<RSBaseRenderNode>>& renderNodeMap)
296 {
297     renderNodeNumsInBucketMap_ += renderNodeMap.size();
298     RS_TRACE_NAME_FMT("AddToOffTreeNodeBucket, size=%u, nodes=%llu",
299         offTreeBucketMap_.size(), renderNodeNumsInBucketMap_);
300     offTreeBucketMap_.push(
301         std::pair<pid_t, std::unordered_map<NodeId, std::shared_ptr<RSBaseRenderNode>>>(
302             pid, std::move(renderNodeMap)));
303 }
304 
ReleaseOffTreeNodeForBucket(const RSThresholdDetector<uint32_t>::DetectCallBack & callBack)305 void RSRenderNodeGC::ReleaseOffTreeNodeForBucket(const RSThresholdDetector<uint32_t>::DetectCallBack &callBack)
306 {
307     std::vector<std::shared_ptr<RSBaseRenderNode>> toRemove;
308     if (offTreeBucket_.empty()) {
309         return;
310     }
311     toRemove.swap(offTreeBucket_.front());
312     offTreeBucket_.pop();
313     uint32_t remainBucketSize = offTreeBucket_.size();
314     offTreeBucketThrDetector_.Detect(remainBucketSize, callBack);
315     RS_TRACE_NAME_FMT("ReleaseOffTreeNodeForBucket %d, remain offTree buckets %u", toRemove.size(), remainBucketSize);
316     for (const auto& node : toRemove) {
317         if (!node) {
318             continue;
319         }
320         auto parent = node->GetParent().lock();
321         if (parent) {
322             parent->RemoveChildFromFulllist(node->GetId());
323         }
324         node->RemoveFromTree(false);
325     }
326 }
327 
ReleaseOffTreeNodeForBucketMap(const RSThresholdDetector<uint32_t>::DetectCallBack & callBack)328 void RSRenderNodeGC::ReleaseOffTreeNodeForBucketMap(const RSThresholdDetector<uint32_t>::DetectCallBack &callBack)
329 {
330     if (offTreeBucketMap_.empty()) {
331         return;
332     }
333     auto& toRemove = offTreeBucketMap_.front();
334     pid_t pid = toRemove.first;
335     auto& subMap = toRemove.second;
336     uint32_t count = 0;
337     const size_t subMapSize = subMap.size();
338     uint64_t remainBucketSize = renderNodeNumsInBucketMap_ / OFF_TREE_BUCKET_MAX_SIZE +
339         (renderNodeNumsInBucketMap_ % OFF_TREE_BUCKET_MAX_SIZE == 0 ? 0 : 1);
340     offTreeBucketThrDetector_.Detect(remainBucketSize, callBack);
341     RS_TRACE_NAME_FMT("ReleaseOffTreeNodeForBucketMap %d, remain offTree buckets %u",
342         OFF_TREE_BUCKET_MAX_SIZE, subMapSize);
343     for (auto subIter = subMap.begin(); subIter != subMap.end();) {
344         if (count++ >= OFF_TREE_BUCKET_MAX_SIZE) {
345             break;
346         }
347         auto renderNode = subIter->second;
348         if (renderNode == nullptr) {
349             subIter = subMap.erase(subIter);
350             continue;
351         }
352         if (auto parent = renderNode->GetParent().lock()) {
353             parent->RemoveChildFromFulllist(renderNode->GetId());
354         }
355         renderNode->RemoveFromTree(false);
356         renderNode->GetAnimationManager().FilterAnimationByPid(pid);
357         subIter = subMap.erase(subIter);
358     }
359     renderNodeNumsInBucketMap_ -= (subMapSize - subMap.size());
360     if (subMap.empty()) {
361         offTreeBucketMap_.pop();
362         RS_TRACE_NAME_FMT("offTreeBucketMap size=%u", offTreeBucketMap_.size());
363     }
364 }
365 } // namespace Rosen
366 } // namespace OHOS
367