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 "drawable/rs_property_drawable.h"
17
18 #include "rs_trace.h"
19
20 #include "common/rs_optional_trace.h"
21 #include "drawable/rs_property_drawable_utils.h"
22 #include "pipeline/rs_recording_canvas.h"
23 #include "pipeline/rs_render_node.h"
24 #include "pipeline/rs_surface_render_node.h"
25 #include "platform/common/rs_log.h"
26 #include "property/rs_filter_cache_manager.h"
27 #include "render/rs_drawing_filter.h"
28 #include "render/rs_linear_gradient_blur_shader_filter.h"
29
30 namespace OHOS::Rosen {
31 constexpr int AIBAR_CACHE_UPDATE_INTERVAL = 5;
32 constexpr int ROTATION_CACHE_UPDATE_INTERVAL = 1;
33 namespace DrawableV2 {
34 constexpr int TRACE_LEVEL_TWO = 2;
OnSync()35 void RSPropertyDrawable::OnSync()
36 {
37 if (!needSync_) {
38 return;
39 }
40 std::swap(drawCmdList_, stagingDrawCmdList_);
41 propertyDescription_ = stagingPropertyDescription_;
42 stagingPropertyDescription_.clear();
43 needSync_ = false;
44 }
45
OnPurge()46 void RSPropertyDrawable::OnPurge()
47 {
48 if (drawCmdList_) {
49 drawCmdList_->Purge();
50 }
51 }
52
CreateDrawFunc() const53 Drawing::RecordingCanvas::DrawFunc RSPropertyDrawable::CreateDrawFunc() const
54 {
55 auto ptr = std::static_pointer_cast<const RSPropertyDrawable>(shared_from_this());
56 return [ptr](Drawing::Canvas* canvas, const Drawing::Rect* rect) {
57 ptr->drawCmdList_->Playback(*canvas);
58 if (!ptr->propertyDescription_.empty()) {
59 RS_OPTIONAL_TRACE_NAME_FMT_LEVEL(TRACE_LEVEL_TWO, "RSPropertyDrawable:: %s, bounds:%s",
60 ptr->propertyDescription_.c_str(), rect->ToString().c_str());
61 }
62 };
63 }
64
65 // ============================================================================
66 // Updater
RSPropertyDrawCmdListUpdater(int width,int height,RSPropertyDrawable * target)67 RSPropertyDrawCmdListUpdater::RSPropertyDrawCmdListUpdater(int width, int height, RSPropertyDrawable* target)
68 : target_(target)
69 {
70 // PLANNING: use RSRenderNode to determine the correct recording canvas size
71 recordingCanvas_ = ExtendRecordingCanvas::Obtain(10, 10, false); // width 10, height 10
72 }
73
~RSPropertyDrawCmdListUpdater()74 RSPropertyDrawCmdListUpdater::~RSPropertyDrawCmdListUpdater()
75 {
76 if (recordingCanvas_ && target_) {
77 target_->stagingDrawCmdList_ = recordingCanvas_->GetDrawCmdList();
78 target_->needSync_ = true;
79 ExtendRecordingCanvas::Recycle(recordingCanvas_);
80 recordingCanvas_.reset();
81 target_ = nullptr;
82 } else {
83 ROSEN_LOGE("Update failed, recording canvas is null!");
84 }
85 }
86
GetRecordingCanvas() const87 const std::unique_ptr<ExtendRecordingCanvas>& RSPropertyDrawCmdListUpdater::GetRecordingCanvas() const
88 {
89 return recordingCanvas_;
90 }
91
92 // ============================================================================
OnGenerate(const RSRenderNode & node)93 RSDrawable::Ptr RSFrameOffsetDrawable::OnGenerate(const RSRenderNode& node)
94 {
95 if (auto ret = std::make_shared<RSFrameOffsetDrawable>(); ret->OnUpdate(node)) {
96 return std::move(ret);
97 }
98 return nullptr;
99 };
100
OnUpdate(const RSRenderNode & node)101 bool RSFrameOffsetDrawable::OnUpdate(const RSRenderNode& node)
102 {
103 const RSProperties& properties = node.GetRenderProperties();
104 auto frameOffsetX = properties.GetFrameOffsetX();
105 auto frameOffsetY = properties.GetFrameOffsetY();
106 if (frameOffsetX == 0 && frameOffsetY == 0) {
107 return false;
108 }
109
110 // regenerate stagingDrawCmdList_
111 RSPropertyDrawCmdListUpdater updater(0, 0, this);
112 updater.GetRecordingCanvas()->Translate(frameOffsetX, frameOffsetY);
113 return true;
114 }
115
116 // ============================================================================
OnGenerate(const RSRenderNode & node)117 RSDrawable::Ptr RSClipToBoundsDrawable::OnGenerate(const RSRenderNode& node)
118 {
119 auto ret = std::make_shared<RSClipToBoundsDrawable>();
120 ret->OnUpdate(node);
121 ret->OnSync();
122 return std::move(ret);
123 };
124
OnUpdate(const RSRenderNode & node)125 bool RSClipToBoundsDrawable::OnUpdate(const RSRenderNode& node)
126 {
127 const RSProperties& properties = node.GetRenderProperties();
128 RSPropertyDrawCmdListUpdater updater(0, 0, this);
129 auto& canvas = *updater.GetRecordingCanvas();
130 if (properties.GetClipBounds() != nullptr) {
131 canvas.ClipPath(properties.GetClipBounds()->GetDrawingPath(), Drawing::ClipOp::INTERSECT, true);
132 } else if (properties.GetClipToRRect()) {
133 canvas.ClipRoundRect(
134 RSPropertyDrawableUtils::RRect2DrawingRRect(properties.GetClipRRect()), Drawing::ClipOp::INTERSECT, true);
135 } else if (!properties.GetCornerRadius().IsZero()) {
136 canvas.ClipRoundRect(
137 RSPropertyDrawableUtils::RRect2DrawingRRect(properties.GetRRect()), Drawing::ClipOp::INTERSECT, true);
138 } else {
139 // Enable anti-aliasing only on surface nodes to resolve the issue of jagged edges on card compoments
140 // during dragging.
141 bool aa = node.IsInstanceOf<RSSurfaceRenderNode>();
142 canvas.ClipRect(
143 RSPropertyDrawableUtils::Rect2DrawingRect(properties.GetBoundsRect()), Drawing::ClipOp::INTERSECT, aa);
144 }
145 return true;
146 }
147
OnGenerate(const RSRenderNode & node)148 RSDrawable::Ptr RSClipToFrameDrawable::OnGenerate(const RSRenderNode& node)
149 {
150 if (auto ret = std::make_shared<RSClipToFrameDrawable>(); ret->OnUpdate(node)) {
151 return std::move(ret);
152 }
153 return nullptr;
154 }
155
OnUpdate(const RSRenderNode & node)156 bool RSClipToFrameDrawable::OnUpdate(const RSRenderNode& node)
157 {
158 const RSProperties& properties = node.GetRenderProperties();
159 if (!properties.GetClipToFrame()) {
160 return false;
161 }
162
163 RSPropertyDrawCmdListUpdater updater(0, 0, this);
164 updater.GetRecordingCanvas()->ClipRect(
165 RSPropertyDrawableUtils::Rect2DrawingRect(properties.GetFrameRect()), Drawing::ClipOp::INTERSECT, false);
166 return true;
167 }
168
RSFilterDrawable()169 RSFilterDrawable::RSFilterDrawable()
170 {
171 if (RSProperties::FilterCacheEnabled) {
172 cacheManager_ = std::make_unique<RSFilterCacheManager>();
173 }
174 }
175
OnSync()176 void RSFilterDrawable::OnSync()
177 {
178 if (needSync_) {
179 filter_ = std::move(stagingFilter_);
180 needSync_ = false;
181 }
182
183 renderFilterHashChanged_ = stagingFilterHashChanged_;
184 renderForceClearCacheForLastFrame_ = stagingForceClearCacheForLastFrame_;
185 renderIsEffectNode_ = stagingIsEffectNode_;
186 renderIsSkipFrame_ = stagingIsSkipFrame_;
187 renderNodeId_ = stagingNodeId_;
188 renderClearType_ = stagingClearType_;
189 renderIntersectWithDRM_ = stagingIntersectWithDRM_;
190 renderIsDarkColorMode_ = stagingIsDarkColorMode_;
191
192 ClearFilterCache();
193
194 stagingFilterHashChanged_ = false;
195 stagingFilterRegionChanged_ = false;
196 stagingFilterInteractWithDirty_ = false;
197 stagingRotationChanged_ = false;
198 stagingForceClearCache_ = false;
199 stagingForceUseCache_ = false;
200 stagingIsOccluded_ = false;
201 stagingForceClearCacheForLastFrame_ = false;
202 stagingIntersectWithDRM_ = false;
203 stagingIsDarkColorMode_ = false;
204
205 stagingClearType_ = FilterCacheType::BOTH;
206 stagingIsLargeArea_ = false;
207 isFilterCacheValid_ = false;
208 stagingIsEffectNode_ = false;
209 stagingIsSkipFrame_ = false;
210 needSync_ = false;
211 }
212
CreateDrawFunc() const213 Drawing::RecordingCanvas::DrawFunc RSFilterDrawable::CreateDrawFunc() const
214 {
215 auto ptr = std::static_pointer_cast<const RSFilterDrawable>(shared_from_this());
216 return [ptr](Drawing::Canvas* canvas, const Drawing::Rect* rect) {
217 if (canvas && ptr && ptr->renderIntersectWithDRM_) {
218 RS_TRACE_NAME_FMT("RSFilterDrawable::CreateDrawFunc IntersectWithDRM node[%lld] isDarkColorMode[%d]",
219 ptr->renderNodeId_, ptr->renderIsDarkColorMode_);
220 RSPropertyDrawableUtils::DrawFilterWithDRM(canvas, ptr->renderIsDarkColorMode_);
221 return;
222 }
223 if (canvas && ptr && ptr->filter_) {
224 RS_TRACE_NAME_FMT("RSFilterDrawable::CreateDrawFunc node[%llu] ", ptr->renderNodeId_);
225 if (ptr->filter_->GetFilterType() == RSFilter::LINEAR_GRADIENT_BLUR && rect != nullptr) {
226 auto filter = std::static_pointer_cast<RSDrawingFilter>(ptr->filter_);
227 std::shared_ptr<RSShaderFilter> rsShaderFilter =
228 filter->GetShaderFilterWithType(RSShaderFilter::LINEAR_GRADIENT_BLUR);
229 if (rsShaderFilter != nullptr) {
230 auto tmpFilter = std::static_pointer_cast<RSLinearGradientBlurShaderFilter>(rsShaderFilter);
231 tmpFilter->SetGeometry(*canvas, rect->GetWidth(), rect->GetHeight());
232 }
233 }
234 RSPropertyDrawableUtils::DrawFilter(canvas, ptr->filter_,
235 ptr->cacheManager_, ptr->IsForeground(), ptr->renderClearFilteredCacheAfterDrawing_);
236 }
237 };
238 }
239
GetFilterCachedRegion() const240 const RectI RSFilterDrawable::GetFilterCachedRegion() const
241 {
242 return cacheManager_ == nullptr ? RectI() : cacheManager_->GetCachedImageRegion();
243 }
244
MarkFilterRegionChanged()245 void RSFilterDrawable::MarkFilterRegionChanged()
246 {
247 stagingFilterRegionChanged_ = true;
248 }
249
MarkFilterRegionInteractWithDirty()250 void RSFilterDrawable::MarkFilterRegionInteractWithDirty()
251 {
252 stagingFilterInteractWithDirty_ = true;
253 }
254
MarkFilterRegionIsLargeArea()255 void RSFilterDrawable::MarkFilterRegionIsLargeArea()
256 {
257 stagingIsLargeArea_ = true;
258 }
259
MarkFilterForceUseCache(bool forceUseCache)260 void RSFilterDrawable::MarkFilterForceUseCache(bool forceUseCache)
261 {
262 stagingForceUseCache_ = forceUseCache;
263 }
264
MarkFilterForceClearCache()265 void RSFilterDrawable::MarkFilterForceClearCache()
266 {
267 stagingForceClearCache_ = true;
268 }
269
MarkRotationChanged()270 void RSFilterDrawable::MarkRotationChanged()
271 {
272 stagingRotationChanged_ = true;
273 }
274
MarkNodeIsOccluded(bool isOccluded)275 void RSFilterDrawable::MarkNodeIsOccluded(bool isOccluded)
276 {
277 stagingIsOccluded_ = isOccluded;
278 }
279
MarkForceClearCacheWithLastFrame()280 void RSFilterDrawable::MarkForceClearCacheWithLastFrame()
281 {
282 stagingForceClearCacheForLastFrame_ = true;
283 }
284
MarkNeedClearFilterCache()285 void RSFilterDrawable::MarkNeedClearFilterCache()
286 {
287 if (cacheManager_ == nullptr) {
288 return;
289 }
290
291 RS_TRACE_NAME_FMT("RSFilterDrawable::MarkNeedClearFilterCache nodeId[%llu], forceUseCache_:%d,"
292 "forceClearCache_:%d, hashChanged:%d, regionChanged_:%d, belowDirty_:%d,"
293 "lastCacheType:%d, cacheUpdateInterval_:%d, canSkip:%d, isLargeArea:%d, filterType_:%d, pendingPurge_:%d,"
294 "forceClearCacheWithLastFrame:%d, rotationChanged:%d",
295 stagingNodeId_, stagingForceUseCache_, stagingForceClearCache_, stagingFilterHashChanged_,
296 stagingFilterRegionChanged_, stagingFilterInteractWithDirty_,
297 lastCacheType_, cacheUpdateInterval_, canSkipFrame_, stagingIsLargeArea_,
298 filterType_, pendingPurge_, stagingForceClearCacheForLastFrame_, stagingRotationChanged_);
299
300 // if do not request NextVsync, close skip
301 if (stagingForceClearCacheForLastFrame_) {
302 cacheUpdateInterval_ = 0;
303 }
304
305 stagingIsSkipFrame_ = stagingIsLargeArea_ && canSkipFrame_ && !stagingFilterRegionChanged_;
306
307 // no valid cache
308 if (lastCacheType_ == FilterCacheType::NONE) {
309 UpdateFlags(FilterCacheType::NONE, false);
310 return;
311 }
312 // No need to invalidate cache if background image is not null or freezed
313 if (stagingForceUseCache_) {
314 UpdateFlags(FilterCacheType::NONE, true);
315 return;
316 }
317
318 // clear both two type cache: 1. force clear 2. filter region changed 3.skip-frame finished
319 // 4. background changed and effectNode rotated will enable skip-frame, the last frame need to update.
320 if (stagingForceClearCache_ || (stagingFilterRegionChanged_ && !stagingRotationChanged_) || NeedPendingPurge() ||
321 ((stagingFilterInteractWithDirty_ || stagingRotationChanged_) && cacheUpdateInterval_ <= 0)) {
322 UpdateFlags(FilterCacheType::BOTH, false);
323 return;
324 }
325
326 // clear snapshot cache last frame and clear filtered cache current frame
327 if (lastCacheType_ == FilterCacheType::FILTERED_SNAPSHOT && stagingFilterHashChanged_) {
328 UpdateFlags(FilterCacheType::FILTERED_SNAPSHOT, false);
329 return;
330 }
331
332 // when blur filter changes, we need to clear filtered cache if it valid.
333 UpdateFlags(stagingFilterHashChanged_ ?
334 FilterCacheType::FILTERED_SNAPSHOT : FilterCacheType::NONE, true);
335 }
336
337 //should be called in rs main thread
MarkBlurIntersectWithDRM(bool intersectWithDRM,bool isDark)338 void RSFilterDrawable::MarkBlurIntersectWithDRM(bool intersectWithDRM, bool isDark)
339 {
340 stagingIntersectWithDRM_ = intersectWithDRM;
341 stagingIsDarkColorMode_ = isDark;
342 }
343
IsFilterCacheValid() const344 bool RSFilterDrawable::IsFilterCacheValid() const
345 {
346 return isFilterCacheValid_;
347 }
348
IsSkippingFrame() const349 bool RSFilterDrawable::IsSkippingFrame() const
350 {
351 return (stagingFilterInteractWithDirty_ || stagingRotationChanged_) && cacheUpdateInterval_ > 0;
352 }
353
IsForceClearFilterCache() const354 bool RSFilterDrawable::IsForceClearFilterCache() const
355 {
356 return stagingForceClearCache_;
357 }
358
IsForceUseFilterCache() const359 bool RSFilterDrawable::IsForceUseFilterCache() const
360 {
361 return stagingForceUseCache_;
362 }
363
NeedPendingPurge() const364 bool RSFilterDrawable::NeedPendingPurge() const
365 {
366 return !stagingFilterInteractWithDirty_ && pendingPurge_;
367 }
368
MarkEffectNode()369 void RSFilterDrawable::MarkEffectNode()
370 {
371 stagingIsEffectNode_ = true;
372 }
373
RecordFilterInfos(const std::shared_ptr<RSFilter> & rsFilter)374 void RSFilterDrawable::RecordFilterInfos(const std::shared_ptr<RSFilter>& rsFilter)
375 {
376 auto filter = std::static_pointer_cast<RSDrawingFilter>(rsFilter);
377 if (filter == nullptr) {
378 return;
379 }
380 stagingFilterHashChanged_ = stagingCachedFilterHash_ != filter->Hash();
381 if (stagingFilterHashChanged_) {
382 stagingCachedFilterHash_ = filter->Hash();
383 }
384 filterType_ = filter->GetFilterType();
385 canSkipFrame_ = filter->CanSkipFrame();
386 }
387
ClearFilterCache()388 void RSFilterDrawable::ClearFilterCache()
389 {
390 if (!RSProperties::FilterCacheEnabled || cacheManager_ == nullptr || filter_ == nullptr) {
391 ROSEN_LOGD("Clear filter cache failed or no need to clear cache, filterCacheEnabled:%{public}d,"
392 "cacheManager:%{public}d, filter:%{public}d", RSProperties::FilterCacheEnabled,
393 cacheManager_ != nullptr, filter_ == nullptr);
394 return;
395 }
396 // 1. clear memory when region changed and is not the first time occured.
397 bool needClearMemoryForGpu = stagingFilterRegionChanged_ && cacheManager_->GetCachedType() != FilterCacheType::NONE;
398 if (filterType_ == RSFilter::AIBAR && stagingIsOccluded_) {
399 cacheManager_->InvalidateFilterCache(FilterCacheType::BOTH);
400 } else {
401 cacheManager_->InvalidateFilterCache(renderClearType_);
402 }
403 // 2. clear memory when region changed without skip frame.
404 needClearMemoryForGpu = needClearMemoryForGpu && cacheManager_->GetCachedType() == FilterCacheType::NONE;
405 if (needClearMemoryForGpu) {
406 cacheManager_->SetFilterInvalid(true);
407 }
408
409 // whether to clear blur images. true: clear blur image, false: clear snapshot
410 bool isSaveSnapshot = renderFilterHashChanged_ || cacheManager_->GetCachedType() == FilterCacheType::NONE;
411 bool isAIbarWithLastFrame = filterType_ == RSFilter::AIBAR && renderForceClearCacheForLastFrame_; // last vsync
412
413 if ((filterType_ != RSFilter::AIBAR || isAIbarWithLastFrame) && isSaveSnapshot) {
414 renderClearFilteredCacheAfterDrawing_ = true; // hold snapshot
415 } else {
416 renderClearFilteredCacheAfterDrawing_ = false; // hold blur image
417 }
418 if (renderIsEffectNode_ || renderIsSkipFrame_) { renderClearFilteredCacheAfterDrawing_ = renderFilterHashChanged_; }
419 lastCacheType_ = stagingIsOccluded_ ? cacheManager_->GetCachedType() : (renderClearFilteredCacheAfterDrawing_ ?
420 FilterCacheType::SNAPSHOT : FilterCacheType::FILTERED_SNAPSHOT);
421 RS_TRACE_NAME_FMT("RSFilterDrawable::ClearFilterCache nodeId[%llu], clearType:%d,"
422 " isOccluded_:%d, lastCacheType:%d needClearMemoryForGpu:%d ClearFilteredCacheAfterDrawing:%d",
423 renderNodeId_, renderClearType_, stagingIsOccluded_, lastCacheType_, needClearMemoryForGpu,
424 renderClearFilteredCacheAfterDrawing_);
425 }
426
427 // called after OnSync()
IsFilterCacheValidForOcclusion()428 bool RSFilterDrawable::IsFilterCacheValidForOcclusion()
429 {
430 auto cacheType = cacheManager_->GetCachedType();
431 RS_OPTIONAL_TRACE_NAME_FMT("RSFilterDrawable::IsFilterCacheValidForOcclusion cacheType:%d renderClearType_:%d",
432 cacheType, renderClearType_);
433
434 return cacheType != FilterCacheType::NONE;
435 }
436
UpdateFlags(FilterCacheType type,bool cacheValid)437 void RSFilterDrawable::UpdateFlags(FilterCacheType type, bool cacheValid)
438 {
439 stagingClearType_ = type;
440 isFilterCacheValid_ = cacheValid;
441 if (!cacheValid) {
442 cacheUpdateInterval_ = stagingRotationChanged_ ? ROTATION_CACHE_UPDATE_INTERVAL :
443 (filterType_ == RSFilter::AIBAR ? AIBAR_CACHE_UPDATE_INTERVAL :
444 (stagingIsLargeArea_ && canSkipFrame_ ? RSSystemProperties::GetFilterCacheUpdateInterval() : 0));
445 pendingPurge_ = false;
446 return;
447 }
448 if (stagingIsAIBarInteractWithHWC_) {
449 if (cacheUpdateInterval_ > 0) {
450 cacheUpdateInterval_--;
451 pendingPurge_ = true;
452 }
453 } else {
454 if ((stagingFilterInteractWithDirty_ || stagingRotationChanged_) && cacheUpdateInterval_ > 0) {
455 cacheUpdateInterval_--;
456 pendingPurge_ = true;
457 }
458 }
459 stagingIsAIBarInteractWithHWC_ = false;
460 }
461
IsAIBarCacheValid()462 bool RSFilterDrawable::IsAIBarCacheValid()
463 {
464 if (filterType_ != RSFilter::AIBAR) {
465 return false;
466 }
467 stagingIsAIBarInteractWithHWC_ = true;
468 RS_OPTIONAL_TRACE_NAME_FMT("IsAIBarCacheValid cacheUpdateInterval_:%d forceClearCacheForLastFrame_:%d",
469 cacheUpdateInterval_, stagingForceClearCacheForLastFrame_);
470 if (cacheUpdateInterval_ == 0 || stagingForceClearCacheForLastFrame_) {
471 return false;
472 } else {
473 MarkFilterForceUseCache(true);
474 return true;
475 }
476 }
477 } // namespace DrawableV2
478 } // namespace OHOS::Rosen
479