1 /*
2 * Copyright (c) 2025 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 "feature/occlusion_culling/rs_occlusion_handler.h"
17
18 #include "common/rs_optional_trace.h"
19 #include "feature/occlusion_culling/rs_occlusion_node.h"
20 #include "feature_cfg/graphic_feature_param_manager.h"
21 #include "platform/common/rs_log.h"
22 #include "pipeline/rs_render_node.h"
23 #include "rs_trace.h"
24
25 namespace OHOS::Rosen {
ProcessOffTreeNodes(const std::unordered_set<NodeId> & offTreeNodeIds)26 void RSOcclusionHandler::ProcessOffTreeNodes(const std::unordered_set<NodeId>& offTreeNodeIds)
27 {
28 if (occlusionNodes_.empty()) {
29 return;
30 }
31 RS_TRACE_NAME_FMT("RSOcclusionHandler::ProcessOffTreeNodes");
32 for (const auto id : offTreeNodeIds) {
33 auto it = occlusionNodes_.find(id);
34 if (it == occlusionNodes_.end() || it->second == nullptr) {
35 continue;
36 }
37 it->second->RemoveSubTree(occlusionNodes_);
38 if (occlusionNodes_.empty()) {
39 return;
40 }
41 }
42 }
43
CollectNode(const RSRenderNode & node)44 void RSOcclusionHandler::CollectNode(const RSRenderNode& node)
45 {
46 if (node.GetId() == rootNodeId_) {
47 CollectRootNode(node);
48 } else {
49 CollectNodeInner(node);
50 }
51 }
52
CollectRootNode(const RSRenderNode & node)53 void RSOcclusionHandler::CollectRootNode(const RSRenderNode& node)
54 {
55 RS_TRACE_NAME_FMT("RSOcclusionHandler::CollectRootNode node id = %lu", node.GetId());
56 auto itNode = occlusionNodes_.find(node.GetId());
57 auto ocNode = (itNode != occlusionNodes_.end() && itNode->second != nullptr) ?
58 itNode->second : std::make_shared<OcclusionNode>(node.GetId(), node.GetType());
59 rootOcclusionNode_ = ocNode;
60 rootNode_ = node.weak_from_this();
61 ocNode->MarkAsRootOcclusionNode();
62 ocNode->UpdateClipRect(node);
63 ocNode->UpdateChildrenOutOfRectInfo(node.HasChildrenOutOfRect());
64 occlusionNodes_[ocNode->GetId()] = ocNode;
65 }
66
CollectNodeInner(const RSRenderNode & node)67 void RSOcclusionHandler::CollectNodeInner(const RSRenderNode& node)
68 {
69 auto nodeId = node.GetId();
70 auto parent = node.GetParent().lock();
71 if (!parent) {
72 return;
73 }
74 auto itParent = occlusionNodes_.find(parent->GetId());
75 auto itNode = occlusionNodes_.find(node.GetId());
76 if (itParent == occlusionNodes_.end() || itParent->second == nullptr || itParent->second->IsSubTreeIgnored()) {
77 // If the parent is not collected or ignored, but the node is already collected,
78 // we should delete the node and its subtree.
79 if (itNode != occlusionNodes_.end() && itNode->second != nullptr) {
80 itNode->second->RemoveSubTree(occlusionNodes_);
81 }
82 return;
83 }
84 auto parentOcNode = itParent->second;
85 auto ocNode = (itNode != occlusionNodes_.end() && itNode->second != nullptr) ?
86 itNode->second : std::make_shared<OcclusionNode>(nodeId, node.GetType());
87 parentOcNode->ForwardOrderInsert(ocNode);
88 ocNode->CollectNodeProperties(node);
89 ocNode->CalculateNodeAllBounds();
90 ocNode->UpdateChildrenOutOfRectInfo(node.HasChildrenOutOfRect());
91 occlusionNodes_[nodeId] = ocNode;
92 }
93
CollectSubTree(const RSRenderNode & node,bool isSubTreeNeedPrepare)94 void RSOcclusionHandler::CollectSubTree(const RSRenderNode& node,
95 bool isSubTreeNeedPrepare)
96 {
97 if (isSubTreeNeedPrepare) {
98 return;
99 }
100 // When the subtree does not need to be prepared, it does not mean that all nodes in the subtree remain unchanged,
101 // because when a node is deleted, its grandparent node is marked dirty, not its parent node.
102 // Therefore, the first-level children may have been deleted, so we need to traverse the children to ensure
103 // the lightweight tree correctly follows the RS tree.
104 CollectNodeInner(node);
105 auto sortChildren = *(node.GetSortedChildren());
106 std::for_each(sortChildren.begin(), sortChildren.end(), [&](std::shared_ptr<RSRenderNode> child) {
107 if (!child) {
108 return;
109 }
110 auto itNode = occlusionNodes_.find(child->GetId());
111 if (itNode == occlusionNodes_.end() || itNode->second == nullptr) {
112 RS_TRACE_NAME_FMT("RSOcclusionHandler::CollectSubTree node id = %llu", child->GetId());
113 CollectSubTreeInner(*child);
114 return;
115 }
116 // If the node is the root of a static subtree and the node can participate in occlusion culling,
117 // recollect the node's attributes and update the subtree.
118 if (!itNode->second->IsSubTreeIgnored()) {
119 CollectNodeInner(*child);
120 subTreeSkipPrepareNodes_.insert(child->GetId());
121 return;
122 }
123 RS_TRACE_NAME_FMT("RSOcclusionHandler::CollectSubTree node id = %llu", child->GetId());
124 CollectSubTreeInner(*child);
125 });
126 }
127
CollectSubTreeInner(const RSRenderNode & node)128 void RSOcclusionHandler::CollectSubTreeInner(const RSRenderNode& node)
129 {
130 CollectNodeInner(node);
131 auto itNode = occlusionNodes_.find(node.GetId());
132 // If the node is not collected or ignored, return.
133 if (itNode == occlusionNodes_.end() || itNode->second == nullptr || itNode->second->IsSubTreeIgnored()) {
134 return;
135 }
136 auto sortChildren = *(node.GetSortedChildren());
137 std::for_each(sortChildren.begin(), sortChildren.end(), [&](std::shared_ptr<RSRenderNode> node) {
138 if (node) {
139 CollectSubTreeInner(*node);
140 }
141 });
142 }
143
UpdateChildrenOutOfRectInfo(const RSRenderNode & node)144 void RSOcclusionHandler::UpdateChildrenOutOfRectInfo(const RSRenderNode& node)
145 {
146 auto it = occlusionNodes_.find(node.GetId());
147 if (it == occlusionNodes_.end() || it->second == nullptr) {
148 return;
149 }
150 it->second->UpdateChildrenOutOfRectInfo(node.HasChildrenOutOfRect());
151 }
152
UpdateSkippedSubTreeProp()153 void RSOcclusionHandler::UpdateSkippedSubTreeProp()
154 {
155 if (subTreeSkipPrepareNodes_.empty()) {
156 return;
157 }
158 RS_TRACE_NAME_FMT("RSOcclusionHandler::UpdateSkippedSubTreeProp");
159 for (auto nodeId : subTreeSkipPrepareNodes_) {
160 auto it = occlusionNodes_.find(nodeId);
161 if (it == occlusionNodes_.end() || it->second == nullptr) {
162 continue;
163 }
164 it->second->UpdateSubTreeProp();
165 }
166 subTreeSkipPrepareNodes_.clear();
167 }
168
CalculateFrameOcclusion()169 void RSOcclusionHandler::CalculateFrameOcclusion()
170 {
171 {
172 RS_TRACE_NAME_FMT("CalculateFrameOcclusion Started");
173 culledNodes_.clear();
174 std::unordered_set<NodeId> offTreeNodes_;
175 if (rootOcclusionNode_) {
176 rootOcclusionNode_->DetectOcclusion(culledNodes_, culledEntireSubtree_, offTreeNodes_);
177 }
178 ProcessOffTreeNodes(offTreeNodes_);
179 RS_TRACE_NAME_FMT(
180 "CalculateFrameOcclusion Finished, occlusionNodes size is %zu, culledNodes size is %zu, "
181 "culledEntireSubtree size is %zu", occlusionNodes_.size(), culledNodes_.size(),
182 culledEntireSubtree_.size());
183 }
184 DebugPostOcclusionProcessing();
185 }
186
DumpSubTreeOcclusionInfo(const RSRenderNode & node)187 void RSOcclusionHandler::DumpSubTreeOcclusionInfo(const RSRenderNode& node)
188 {
189 auto sortChildren = node.GetSortedChildren();
190 auto it = occlusionNodes_.find(node.GetId());
191 if (it == occlusionNodes_.end() || it->second == nullptr) {
192 RS_TRACE_NAME_FMT("DumpSubTreeOcclusionInfo: error, node id: %lld not collect", node.GetId());
193 for (const auto& child : *sortChildren) {
194 DumpSubTreeOcclusionInfo(*child);
195 }
196 return;
197 }
198 auto ocNode = it->second;
199 bool isNodeCulled = culledNodes_.count(ocNode->GetId()) > 0;
200 RS_TRACE_NAME_FMT("%s isNodeCulled %d", ocNode->GetOcclusionNodeInfoString().c_str(), isNodeCulled);
201 for (const auto& child : *sortChildren) {
202 DumpSubTreeOcclusionInfo(*child);
203 }
204 }
205
DebugPostOcclusionProcessing()206 void RSOcclusionHandler::DebugPostOcclusionProcessing()
207 {
208 if (debugLevel_ > 0 && rootOcclusionNode_) {
209 std::vector<std::shared_ptr<OcclusionNode>> occlusionNodesVec;
210 rootOcclusionNode_->PreorderTraversal(occlusionNodesVec);
211 auto rootNodeShared = rootNode_.lock();
212 if (rootNodeShared) {
213 DumpSubTreeOcclusionInfo(*rootNodeShared);
214 }
215 }
216 }
217
218 } // namespace OHOS::Rosen
219