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 "rs_uni_dirty_compute_util.h"
17
18 #include <cstdint>
19 #include <memory>
20 #include <parameter.h>
21 #include <parameters.h>
22 #include <string>
23
24 #include "common/rs_optional_trace.h"
25 #include "common/rs_rectangles_merger.h"
26 #include "drawable/dfx/rs_dirty_rects_dfx.h"
27 #include "drawable/rs_logical_display_render_node_drawable.h"
28 #include "drawable/rs_screen_render_node_drawable.h"
29 #include "drawable/rs_surface_render_node_drawable.h"
30 #ifdef RS_ENABLE_OVERLAY_DISPLAY
31 #include "feature/overlay_display/rs_overlay_display_manager.h"
32 #endif
33 #include "feature/uifirst/rs_uifirst_manager.h"
34 #include "info_collection/rs_gpu_dirty_region_collection.h"
35 #include "params/rs_screen_render_params.h"
36 #include "params/rs_surface_render_params.h"
37 #include "pipeline/main_thread/rs_main_thread.h"
38 #include "pipeline/render_thread/rs_base_render_util.h"
39 #include "pipeline/rs_effect_render_node.h"
40 #include "pipeline/rs_render_node.h"
41 #include "pipeline/rs_surface_render_node.h"
42 #include "platform/common/rs_log.h"
43 #include "property/rs_properties.h"
44 #include "rs_trace.h"
45
46 namespace OHOS {
47 namespace Rosen {
GetCurrentFrameVisibleDirty(DrawableV2::RSScreenRenderNodeDrawable & screenNodeDrawable,ScreenInfo & screenInfo,RSScreenRenderParams & params)48 std::vector<RectI> RSUniDirtyComputeUtil::GetCurrentFrameVisibleDirty(
49 DrawableV2::RSScreenRenderNodeDrawable& screenNodeDrawable, ScreenInfo& screenInfo, RSScreenRenderParams& params)
50 {
51 Occlusion::Region damageRegions;
52 auto& curAllSurfaceDrawables = params.GetAllMainAndLeashSurfaceDrawables();
53 // update all child surfacenode history
54 for (auto it = curAllSurfaceDrawables.rbegin(); it != curAllSurfaceDrawables.rend(); ++it) {
55 auto surfaceNodeDrawable = std::static_pointer_cast<DrawableV2::RSSurfaceRenderNodeDrawable>(*it);
56 if (surfaceNodeDrawable == nullptr) {
57 RS_LOGI("GetCurrentFrameVisibleDirty surfaceNodeDrawable is nullptr");
58 continue;
59 }
60 auto surfaceParams = static_cast<RSSurfaceRenderParams*>(surfaceNodeDrawable->GetRenderParams().get());
61 auto surfaceDirtyManager = surfaceNodeDrawable->GetSyncDirtyManager();
62 if (!surfaceParams || !surfaceDirtyManager) {
63 RS_LOGI("RSUniDirtyComputeUtil::GetCurrentFrameVisibleDirty node(%{public}" PRIu64") params or "
64 "dirty manager is nullptr", surfaceNodeDrawable->GetId());
65 continue;
66 }
67 if (!surfaceParams->IsLeashOrMainWindow() || surfaceParams->GetDstRect().IsEmpty()) {
68 continue;
69 }
70 // for cross-screen surface, only consider the dirty region on the first display (use global dirty for others).
71 if (surfaceParams->IsFirstLevelCrossNode() &&
72 !RSUniRenderThread::Instance().GetRSRenderThreadParams()->IsFirstVisitCrossNodeDisplay()) {
73 continue;
74 }
75 auto visibleRegion = surfaceParams->GetVisibleRegion();
76 auto surfaceCurrentFrameDirtyRegion = surfaceDirtyManager->GetCurrentFrameDirtyRegion();
77 Occlusion::Region currentFrameDirtyRegion { Occlusion::Rect {
78 surfaceCurrentFrameDirtyRegion.left_, surfaceCurrentFrameDirtyRegion.top_,
79 surfaceCurrentFrameDirtyRegion.GetRight(), surfaceCurrentFrameDirtyRegion.GetBottom() } };
80 Occlusion::Region damageRegion = currentFrameDirtyRegion.And(visibleRegion);
81 damageRegions.OrSelf(damageRegion);
82 auto& surfaceFilterCollector = surfaceDirtyManager->GetFilterCollector();
83 damageRegions.OrSelf(surfaceFilterCollector.GetPureCleanFilterDirtyRegion());
84 surfaceFilterCollector.ClearPureCleanFilterDirtyRegion();
85 }
86 auto screenDirtyManager = screenNodeDrawable.GetSyncDirtyManager();
87 if (screenDirtyManager == nullptr) {
88 return {};
89 }
90 auto& screenFilterCollector = screenDirtyManager->GetFilterCollector();
91 damageRegions.OrSelf(screenFilterCollector.GetPureCleanFilterDirtyRegion());
92 screenFilterCollector.ClearPureCleanFilterDirtyRegion();
93 auto rects = RSUniDirtyComputeUtil::ScreenIntersectDirtyRects(damageRegions, screenInfo);
94 RectI rect = screenDirtyManager->GetDirtyRegionFlipWithinSurface();
95 if (!rect.IsEmpty()) {
96 rects.emplace_back(rect);
97 }
98 return rects;
99 }
100
ScreenIntersectDirtyRects(const Occlusion::Region & region,const ScreenInfo & screenInfo)101 std::vector<RectI> RSUniDirtyComputeUtil::ScreenIntersectDirtyRects(
102 const Occlusion::Region ®ion, const ScreenInfo& screenInfo)
103 {
104 const std::vector<Occlusion::Rect>& rects = region.GetRegionRects();
105 std::vector<RectI> retRects;
106 for (const Occlusion::Rect& rect : rects) {
107 // origin transformation
108 #ifdef RS_ENABLE_VK
109 if (RSSystemProperties::GetGpuApiType() == GpuApiType::VULKAN ||
110 RSSystemProperties::GetGpuApiType() == GpuApiType::DDGR) {
111 retRects.emplace_back(RectI(rect.left_, rect.top_,
112 rect.right_ - rect.left_, rect.bottom_ - rect.top_));
113 } else {
114 retRects.emplace_back(RectI(rect.left_, screenInfo.GetRotatedHeight() - rect.bottom_,
115 rect.right_ - rect.left_, rect.bottom_ - rect.top_));
116 }
117 #else
118 retRects.emplace_back(RectI(rect.left_, screenInfo.GetRotatedHeight() - rect.bottom_,
119 rect.right_ - rect.left_, rect.bottom_ - rect.top_));
120 #endif
121 }
122 RS_LOGD("ScreenIntersectDirtyRects size %{public}d %{public}s", region.GetSize(), region.GetRegionInfo().c_str());
123 return retRects;
124 }
125
GetFilpDirtyRects(const std::vector<RectI> & srcRects,const ScreenInfo & screenInfo)126 std::vector<RectI> RSUniDirtyComputeUtil::GetFilpDirtyRects(
127 const std::vector<RectI>& srcRects, const ScreenInfo& screenInfo)
128 {
129 #ifdef RS_ENABLE_VK
130 if (RSSystemProperties::GetGpuApiType() == GpuApiType::VULKAN ||
131 RSSystemProperties::GetGpuApiType() == GpuApiType::DDGR) {
132 return srcRects;
133 }
134 #endif
135
136 return FilpRects(srcRects, screenInfo);
137 }
138
FilpRects(const std::vector<RectI> & srcRects,const ScreenInfo & screenInfo)139 std::vector<RectI> RSUniDirtyComputeUtil::FilpRects(const std::vector<RectI>& srcRects, const ScreenInfo& screenInfo)
140 {
141 std::vector<RectI> retRects;
142 for (const RectI& rect : srcRects) {
143 retRects.emplace_back(RectI(rect.left_, screenInfo.GetRotatedHeight() - rect.top_ - rect.height_,
144 rect.width_, rect.height_));
145 }
146 return retRects;
147 }
148
IntersectRect(const GraphicIRect & first,const GraphicIRect & second)149 GraphicIRect RSUniDirtyComputeUtil::IntersectRect(const GraphicIRect& first, const GraphicIRect& second)
150 {
151 int left = std::max(first.x, second.x);
152 int top = std::max(first.y, second.y);
153 int right = std::min(first.x + first.w, second.x + second.w);
154 int bottom = std::min(first.y + first.h, second.y + second.h);
155 int width = right - left;
156 int height = bottom - top;
157
158 if (width <= 0 || height <= 0) {
159 return GraphicIRect { 0, 0, 0, 0 };
160 } else {
161 return GraphicIRect { left, top, width, height };
162 }
163 }
164
DealWithFilterDirtyRegion(Occlusion::Region & damageRegion,Occlusion::Region & drawRegion,DrawableV2::RSScreenRenderNodeDrawable & screenNodeDrawable,const std::optional<Drawing::Matrix> & matrix,bool dirtyAlign)165 void RSUniFilterDirtyComputeUtil::DealWithFilterDirtyRegion(Occlusion::Region& damageRegion,
166 Occlusion::Region& drawRegion, DrawableV2::RSScreenRenderNodeDrawable& screenNodeDrawable,
167 const std::optional<Drawing::Matrix>& matrix, bool dirtyAlign)
168 {
169 dirtyAlignEnabled_ = dirtyAlign;
170 if (UNLIKELY(screenNodeDrawable.GetRenderParams() == nullptr)) {
171 return;
172 }
173 auto screenParams = static_cast<RSScreenRenderParams*>(screenNodeDrawable.GetRenderParams().get());
174 // If screen zoomed, filter cache is globally disabled, thus partial render of filter cache should be disabled.
175 RSFilterDirtyCollector::SetValidCachePartialRender(!screenParams->GetZoomed());
176 auto& surfaceDrawables = screenParams->GetAllMainAndLeashSurfaceDrawables();
177 // Iteratively process filters recorded in screen manager and surface manager, until convergence.
178 bool elementChanged = false;
179 do {
180 elementChanged = false;
181 elementChanged |= DealWithFilterDirtyForScreen(damageRegion, drawRegion, screenNodeDrawable, matrix);
182 elementChanged |= DealWithFilterDirtyForSurface(damageRegion, drawRegion, surfaceDrawables, matrix);
183 } while (elementChanged);
184 ResetFilterInfoStatus(screenNodeDrawable, surfaceDrawables);
185 }
186
DealWithFilterDirtyForScreen(Occlusion::Region & damageRegion,Occlusion::Region & drawRegion,DrawableV2::RSScreenRenderNodeDrawable & screenNodeDrawable,const std::optional<Drawing::Matrix> & matrix)187 bool RSUniFilterDirtyComputeUtil::DealWithFilterDirtyForScreen(Occlusion::Region& damageRegion,
188 Occlusion::Region& drawRegion, DrawableV2::RSScreenRenderNodeDrawable& screenNodeDrawable,
189 const std::optional<Drawing::Matrix>& matrix)
190 {
191 auto screenDirtyManager = screenNodeDrawable.GetSyncDirtyManager();
192 if (UNLIKELY(screenDirtyManager == nullptr)) {
193 return false;
194 }
195 return CheckMergeFilterDirty(damageRegion, drawRegion, *screenDirtyManager, matrix, std::nullopt);
196 }
197
DealWithFilterDirtyForSurface(Occlusion::Region & damageRegion,Occlusion::Region & drawRegion,std::vector<DrawableV2::RSRenderNodeDrawableAdapter::SharedPtr> & drawables,const std::optional<Drawing::Matrix> & matrix)198 bool RSUniFilterDirtyComputeUtil::DealWithFilterDirtyForSurface(Occlusion::Region& damageRegion,
199 Occlusion::Region& drawRegion, std::vector<DrawableV2::RSRenderNodeDrawableAdapter::SharedPtr>& drawables,
200 const std::optional<Drawing::Matrix>& matrix)
201 {
202 bool elementChanged = false;
203 for (auto it = drawables.begin(); it != drawables.end(); ++it) {
204 auto surfaceNodeDrawable = std::static_pointer_cast<DrawableV2::RSSurfaceRenderNodeDrawable>(*it);
205 if (UNLIKELY(surfaceNodeDrawable == nullptr)) {
206 RS_LOGI("DealWithFilterDirtyForSurface surfaceNodeDrawable is nullptr");
207 continue;
208 }
209 auto surfaceParams = static_cast<RSSurfaceRenderParams*>(surfaceNodeDrawable->GetRenderParams().get());
210 auto surfaceDirtyManager = surfaceNodeDrawable->GetSyncDirtyManager();
211 if (UNLIKELY(surfaceParams == nullptr || surfaceDirtyManager == nullptr)) {
212 RS_LOGI("DealWithFilterDirtyForSurface surface param or dirty manager is nullptr");
213 continue;
214 }
215 bool skipComputeIfOccluded =
216 surfaceParams->GetVisibleRegion().IsEmpty() || surfaceParams->GetOccludedByFilterCache();
217 if (skipComputeIfOccluded) {
218 RS_OPTIONAL_TRACE_FMT("Skip filter dirty processing for %s occluded.", surfaceParams->GetName().c_str());
219 continue;
220 }
221 elementChanged |= CheckMergeFilterDirty(damageRegion, drawRegion, *surfaceDirtyManager,
222 matrix, surfaceParams->GetVisibleRegion());
223 }
224 return elementChanged;
225 }
226
CheckMergeFilterDirty(Occlusion::Region & damageRegion,Occlusion::Region & drawRegion,RSDirtyRegionManager & dirtyManager,const std::optional<Drawing::Matrix> & matrix,const std::optional<Occlusion::Region> & visibleRegion)227 bool RSUniFilterDirtyComputeUtil::CheckMergeFilterDirty(Occlusion::Region& damageRegion, Occlusion::Region& drawRegion,
228 RSDirtyRegionManager& dirtyManager, const std::optional<Drawing::Matrix>& matrix,
229 const std::optional<Occlusion::Region>& visibleRegion)
230 {
231 auto& collector = dirtyManager.GetFilterCollector();
232 bool filterCachePartialRender =
233 !dirtyManager.IsCurrentFrameDirty() && RSFilterDirtyCollector::GetValidCachePartialRender();
234 auto addDirtyInIntersect = [&] (FilterDirtyRegionInfo& info) {
235 // case - 0. If this filter satisfied certain partial render conditions, skip it.
236 if (filterCachePartialRender && RSFilterDirtyCollector::GetFilterCacheValidForOcclusion(info.id_)) {
237 RS_TRACE_NAME_FMT("Filter [%" PRIu64 "], partial render enabled, skip dirty expanding.", info.id_);
238 return false;
239 }
240 // case - 1. If this filter is already counted in damage region, skip it.
241 if (info.addToDirty_) {
242 return false;
243 }
244 // case - 2. If this filter is not intersected with drawRegion, skip it.
245 Occlusion::Region intersectRegion = matrix.has_value() ?
246 RSObjAbsGeometry::MapRegion(info.intersectRegion_, matrix.value()) : info.intersectRegion_;
247 intersectRegion = visibleRegion.has_value() ?
248 intersectRegion.And(visibleRegion.value()) : intersectRegion;
249 if (drawRegion.And(intersectRegion).IsEmpty()) {
250 return false;
251 }
252 // case - 3. Add this filter into both damage region (for GPU) and draw region (for RS).
253 RS_OPTIONAL_TRACE_NAME_FMT("Filter [%" PRIu64 "], intersected with draw region: %s, add %s to damage.",
254 info.id_, drawRegion.GetRegionInfo().c_str(), info.filterDirty_.GetRegionInfo().c_str());
255 Occlusion::Region dirtyRegion = matrix.has_value() ?
256 RSObjAbsGeometry::MapRegion(info.filterDirty_, matrix.value()) : info.filterDirty_;
257 Occlusion::Region alignedDirtyRegion = matrix.has_value() ?
258 RSObjAbsGeometry::MapRegion(info.alignedFilterDirty_, matrix.value()) : info.alignedFilterDirty_;
259 collector.AddPureCleanFilterDirtyRegion(dirtyRegion);
260 damageRegion.OrSelf(dirtyRegion);
261 drawRegion.OrSelf(dirtyAlignEnabled_ ? alignedDirtyRegion : dirtyRegion);
262 info.addToDirty_ = true;
263 return true;
264 };
265 // return if any filter is added to damage region and draw region.
266 auto& filterList = collector.GetFilterDirtyRegionInfoList(true);
267 return std::find_if(filterList.begin(), filterList.end(), addDirtyInIntersect) != filterList.end();
268 }
269
ResetFilterInfoStatus(DrawableV2::RSScreenRenderNodeDrawable & screenNodeDrawable,std::vector<DrawableV2::RSRenderNodeDrawableAdapter::SharedPtr> & surfaceDrawables)270 void RSUniFilterDirtyComputeUtil::ResetFilterInfoStatus(DrawableV2::RSScreenRenderNodeDrawable& screenNodeDrawable,
271 std::vector<DrawableV2::RSRenderNodeDrawableAdapter::SharedPtr>& surfaceDrawables)
272 {
273 auto resetFilterStatus = [] (FilterDirtyRegionInfo& filterInfo) { filterInfo.addToDirty_ = false; };
274 if (auto screenDirtyManager = screenNodeDrawable.GetSyncDirtyManager()) {
275 auto& screenFilterList = screenDirtyManager->GetFilterCollector().GetFilterDirtyRegionInfoList(true);
276 std::for_each(screenFilterList.begin(), screenFilterList.end(), resetFilterStatus);
277 }
278 for (auto it = surfaceDrawables.rbegin(); it != surfaceDrawables.rend(); ++it) {
279 auto surfaceNodeDrawable = std::static_pointer_cast<DrawableV2::RSSurfaceRenderNodeDrawable>(*it);
280 if (UNLIKELY(surfaceNodeDrawable == nullptr)) {
281 RS_LOGI("ResetFilterInfoStatus surfaceNodeDrawable is nullptr");
282 continue;
283 }
284 if (auto surfaceDirtyManager = surfaceNodeDrawable->GetSyncDirtyManager()) {
285 auto& surfaceFilterList = surfaceDirtyManager->GetFilterCollector().GetFilterDirtyRegionInfoList(true);
286 std::for_each(surfaceFilterList.begin(), surfaceFilterList.end(), resetFilterStatus);
287 }
288 }
289 RSFilterDirtyCollector::ResetFilterCacheValidForOcclusion();
290 }
291
GenerateFilterDirtyRegionInfo(RSRenderNode & filterNode,const std::optional<Occlusion::Region> & preDirty,bool isSurface)292 FilterDirtyRegionInfo RSUniFilterDirtyComputeUtil::GenerateFilterDirtyRegionInfo(
293 RSRenderNode& filterNode, const std::optional<Occlusion::Region>& preDirty, bool isSurface)
294 {
295 bool effectNodeExpandDirty =
296 filterNode.IsInstanceOf<RSEffectRenderNode>() && !filterNode.FirstFrameHasEffectChildren();
297 auto filterRegion = effectNodeExpandDirty ?
298 GetVisibleEffectRegion(filterNode) : Occlusion::Region(Occlusion::Rect(filterNode.GetOldDirtyInSurface()));
299 auto dirtyRegion = effectNodeExpandDirty ?
300 filterRegion.Or(Occlusion::Region(Occlusion::Rect(filterNode.GetFilterRect()))) : filterRegion;
301 if (filterNode.NeedDrawBehindWindow()) {
302 filterRegion = Occlusion::Region(Occlusion::Rect(filterNode.GetFilterRect()));
303 dirtyRegion = filterRegion;
304 }
305 // Subtree dirty region doesn't need to be considered for background filter.
306 auto& filterProperties = filterNode.GetRenderProperties();
307 FilterDirtyRegionInfo filterInfo = {
308 .id_ = filterNode.GetId(),
309 .intersectRegion_ = isSurface ? filterRegion : dirtyRegion,
310 .filterDirty_ = isSurface ? filterRegion : dirtyRegion,
311 .alignedFilterDirty_ = dirtyRegion.GetAlignedRegion(MAX_DIRTY_ALIGNMENT_SIZE),
312 .belowDirty_ = preDirty.value_or(Occlusion::Region()),
313 .isBackgroundFilterClean_ =
314 (filterProperties.GetBackgroundFilter() || filterProperties.GetNeedDrawBehindWindow()) &&
315 !filterNode.IsBackgroundInAppOrNodeSelfDirty()
316 };
317 return filterInfo;
318 }
319
GetVisibleEffectRegion(RSRenderNode & filterNode)320 Occlusion::Region RSUniFilterDirtyComputeUtil::GetVisibleEffectRegion(RSRenderNode& filterNode)
321 {
322 auto context = filterNode.GetContext().lock();
323 Occlusion::Region childEffectRegion;
324 if (!context) {
325 RS_LOGE("GetVisibleEffectRegion filter node%{public}" PRIu64 " context is nullptr", filterNode.GetId());
326 return childEffectRegion;
327 }
328 for (auto& nodeId : filterNode.GetVisibleEffectChild()) {
329 if (auto& subNode = context->GetNodeMap().GetRenderNode<RSRenderNode>(nodeId)) {
330 childEffectRegion = childEffectRegion.OrSelf(Occlusion::Region(subNode->GetOldDirtyInSurface()));
331 }
332 }
333 return childEffectRegion;
334 }
335
UpdateVirtualExpandScreenAccumulatedParams(RSScreenRenderParams & params,DrawableV2::RSScreenRenderNodeDrawable & screenNodeDrawable)336 void RSUniDirtyComputeUtil::UpdateVirtualExpandScreenAccumulatedParams(
337 RSScreenRenderParams& params, DrawableV2::RSScreenRenderNodeDrawable& screenNodeDrawable)
338 {
339 // All other factors that may prevent skipping virtual expand screen need to be considered
340 // update accumulated dirty region
341 params.SetAccumulatedDirty(params.GetAccumulatedDirty() ||
342 (screenNodeDrawable.GetSyncDirtyManager()->IsCurrentFrameDirty() || params.GetMainAndLeashSurfaceDirty()));
343
344 // update accumulated hdr status changed
345 params.SetAccumulatedHdrStatusChanged(params.GetAccumulatedHdrStatusChanged() || params.IsHDRStatusChanged());
346 }
347
CheckVirtualExpandScreenSkip(RSScreenRenderParams & params,DrawableV2::RSScreenRenderNodeDrawable & screenNodeDrawable)348 bool RSUniDirtyComputeUtil::CheckVirtualExpandScreenSkip(
349 RSScreenRenderParams& params, DrawableV2::RSScreenRenderNodeDrawable& screenNodeDrawable)
350 {
351 // Regardless of whether the current frame is skipped, the state needs to be accumulated
352 if (!RSSystemProperties::GetVirtualExpandScreenSkipEnabled()) {
353 return false;
354 }
355
356 const auto& displayDrawables = params.GetDisplayDrawables();
357 for (const auto& drawable : displayDrawables) {
358 const auto& displayDrawable = static_cast<DrawableV2::RSLogicalDisplayRenderNodeDrawable*>(drawable.get());
359 const auto& displayParams = static_cast<RSLogicalDisplayRenderParams*>(drawable->GetRenderParams().get());
360 if (UNLIKELY(displayDrawable == nullptr || displayParams == nullptr)) {
361 continue;
362 }
363 if (displayDrawable->GetSpecialLayerType(*displayParams) != NO_SPECIAL_LAYER) {
364 RS_TRACE_NAME("CheckVirtualExpandScreenSkip has special layer can not skip");
365 return false;
366 }
367 }
368 RS_TRACE_NAME_FMT("CheckVirtualExpandScreenSkip isAccumulatedDirty: %d, isAccumulatedHdrStatusChanged: %d",
369 params.GetAccumulatedDirty(), params.GetAccumulatedHdrStatusChanged());
370 return !params.GetAccumulatedDirty() && !params.GetAccumulatedHdrStatusChanged();
371 }
372 } // namespace Rosen
373 } // namespace OHOS