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 if (node.GetType() == RSRenderNodeType::SURFACE_NODE && RSSystemProperties::GetCacheEnabledForRotation() &&
139         node.ReinterpretCastTo<RSSurfaceRenderNode>()->IsAppWindow()) {
140         Drawing::Rect rect = RSPropertyDrawableUtils::Rect2DrawingRect(properties.GetBoundsRect());
141         Drawing::RectI iRect(static_cast<int>(rect.GetLeft()), static_cast<int>(rect.GetTop()),
142             static_cast<int>(rect.GetRight()), static_cast<int>(rect.GetBottom()));
143         canvas.ClipIRect(iRect, Drawing::ClipOp::INTERSECT);
144     } else {
145         canvas.ClipRect(
146             RSPropertyDrawableUtils::Rect2DrawingRect(properties.GetBoundsRect()), Drawing::ClipOp::INTERSECT, false);
147     }
148     return true;
149 }
150 
OnGenerate(const RSRenderNode & node)151 RSDrawable::Ptr RSClipToFrameDrawable::OnGenerate(const RSRenderNode& node)
152 {
153     if (auto ret = std::make_shared<RSClipToFrameDrawable>(); ret->OnUpdate(node)) {
154         return std::move(ret);
155     }
156     return nullptr;
157 }
158 
OnUpdate(const RSRenderNode & node)159 bool RSClipToFrameDrawable::OnUpdate(const RSRenderNode& node)
160 {
161     const RSProperties& properties = node.GetRenderProperties();
162     if (!properties.GetClipToFrame()) {
163         return false;
164     }
165 
166     RSPropertyDrawCmdListUpdater updater(0, 0, this);
167     updater.GetRecordingCanvas()->ClipRect(
168         RSPropertyDrawableUtils::Rect2DrawingRect(properties.GetFrameRect()), Drawing::ClipOp::INTERSECT, false);
169     return true;
170 }
171 
RSFilterDrawable()172 RSFilterDrawable::RSFilterDrawable()
173 {
174     if (RSProperties::FilterCacheEnabled) {
175         cacheManager_ = std::make_unique<RSFilterCacheManager>();
176     }
177 }
178 
OnSync()179 void RSFilterDrawable::OnSync()
180 {
181     if (needSync_) {
182         filter_ = std::move(stagingFilter_);
183         needSync_ = false;
184     }
185 
186     renderFilterHashChanged_ = stagingFilterHashChanged_;
187     renderForceClearCacheForLastFrame_ = stagingForceClearCacheForLastFrame_;
188     renderIsEffectNode_ = stagingIsEffectNode_;
189     renderIsSkipFrame_ = stagingIsSkipFrame_;
190     renderNodeId_ = stagingNodeId_;
191     renderClearType_ = stagingClearType_;
192     renderIntersectWithDRM_ = stagingIntersectWithDRM_;
193     renderIsDarkColorMode_ = stagingIsDarkColorMode_;
194 
195     ClearFilterCache();
196 
197     stagingFilterHashChanged_ = false;
198     stagingFilterRegionChanged_ = false;
199     stagingFilterInteractWithDirty_ = false;
200     stagingRotationChanged_ = false;
201     stagingForceClearCache_ = false;
202     stagingForceUseCache_ = false;
203     stagingIsOccluded_ = false;
204     stagingForceClearCacheForLastFrame_ = false;
205     stagingIntersectWithDRM_ = false;
206     stagingIsDarkColorMode_ = false;
207 
208     stagingClearType_ = FilterCacheType::BOTH;
209     stagingIsLargeArea_ = false;
210     isFilterCacheValid_ = false;
211     stagingIsEffectNode_ = false;
212     stagingIsSkipFrame_ = false;
213     needSync_ = false;
214 }
215 
CreateDrawFunc() const216 Drawing::RecordingCanvas::DrawFunc RSFilterDrawable::CreateDrawFunc() const
217 {
218     auto ptr = std::static_pointer_cast<const RSFilterDrawable>(shared_from_this());
219     return [ptr](Drawing::Canvas* canvas, const Drawing::Rect* rect) {
220         if (ptr->needDrawBehindWindow_) {
221             RS_TRACE_NAME_FMT("RSFilterDrawable::CreateDrawFunc DrawBehindWindow node[%llu] ", ptr->renderNodeId_);
222             auto paintFilterCanvas = static_cast<RSPaintFilterCanvas*>(canvas);
223             Drawing::AutoCanvasRestore acr(*canvas, true);
224             paintFilterCanvas->ClipRect(*rect);
225             Drawing::Rect absRect(0.0, 0.0, 0.0, 0.0);
226             canvas->GetTotalMatrix().MapRect(absRect, *rect);
227             Drawing::RectI bounds(std::ceil(absRect.GetLeft()), std::ceil(absRect.GetTop()),
228                 std::ceil(absRect.GetRight()), std::ceil(absRect.GetBottom()));
229             RSPropertyDrawableUtils::DrawBackgroundEffect(paintFilterCanvas, ptr->filter_, ptr->cacheManager_,
230                 ptr->renderClearFilteredCacheAfterDrawing_, bounds, true);
231             return;
232         }
233         if (canvas && ptr && ptr->renderIntersectWithDRM_) {
234             RS_TRACE_NAME_FMT("RSFilterDrawable::CreateDrawFunc IntersectWithDRM node[%lld] isDarkColorMode[%d]",
235                 ptr->renderNodeId_, ptr->renderIsDarkColorMode_);
236             RSPropertyDrawableUtils::DrawFilterWithDRM(canvas, ptr->renderIsDarkColorMode_);
237             return;
238         }
239         if (canvas && ptr && ptr->filter_) {
240             RS_TRACE_NAME_FMT("RSFilterDrawable::CreateDrawFunc node[%llu] ", ptr->renderNodeId_);
241             if (ptr->filter_->GetFilterType() == RSFilter::LINEAR_GRADIENT_BLUR && rect != nullptr) {
242                 auto filter = std::static_pointer_cast<RSDrawingFilter>(ptr->filter_);
243                 std::shared_ptr<RSShaderFilter> rsShaderFilter =
244                     filter->GetShaderFilterWithType(RSShaderFilter::LINEAR_GRADIENT_BLUR);
245                 if (rsShaderFilter != nullptr) {
246                     auto tmpFilter = std::static_pointer_cast<RSLinearGradientBlurShaderFilter>(rsShaderFilter);
247                     tmpFilter->SetGeometry(*canvas, rect->GetWidth(), rect->GetHeight());
248                 }
249             }
250             RSPropertyDrawableUtils::DrawFilter(canvas, ptr->filter_,
251                 ptr->cacheManager_, ptr->IsForeground(), ptr->renderClearFilteredCacheAfterDrawing_);
252         }
253     };
254 }
255 
GetFilterCachedRegion() const256 const RectI RSFilterDrawable::GetFilterCachedRegion() const
257 {
258     return cacheManager_ == nullptr ? RectI() : cacheManager_->GetCachedImageRegion();
259 }
260 
MarkFilterRegionChanged()261 void RSFilterDrawable::MarkFilterRegionChanged()
262 {
263     stagingFilterRegionChanged_ = true;
264 }
265 
MarkFilterRegionInteractWithDirty()266 void RSFilterDrawable::MarkFilterRegionInteractWithDirty()
267 {
268     stagingFilterInteractWithDirty_ = true;
269 }
270 
MarkFilterRegionIsLargeArea()271 void RSFilterDrawable::MarkFilterRegionIsLargeArea()
272 {
273     stagingIsLargeArea_ = true;
274 }
275 
MarkFilterForceUseCache(bool forceUseCache)276 void RSFilterDrawable::MarkFilterForceUseCache(bool forceUseCache)
277 {
278     stagingForceUseCache_ = forceUseCache;
279 }
280 
MarkFilterForceClearCache()281 void RSFilterDrawable::MarkFilterForceClearCache()
282 {
283     stagingForceClearCache_ = true;
284 }
285 
MarkRotationChanged()286 void RSFilterDrawable::MarkRotationChanged()
287 {
288     stagingRotationChanged_ = true;
289 }
290 
MarkNodeIsOccluded(bool isOccluded)291 void RSFilterDrawable::MarkNodeIsOccluded(bool isOccluded)
292 {
293     stagingIsOccluded_ = isOccluded;
294 }
295 
MarkForceClearCacheWithLastFrame()296 void RSFilterDrawable::MarkForceClearCacheWithLastFrame()
297 {
298     stagingForceClearCacheForLastFrame_ = true;
299 }
300 
MarkNeedClearFilterCache()301 void RSFilterDrawable::MarkNeedClearFilterCache()
302 {
303     if (cacheManager_ == nullptr) {
304         return;
305     }
306 
307     RS_TRACE_NAME_FMT("RSFilterDrawable::MarkNeedClearFilterCache nodeId[%llu], forceUseCache_:%d,"
308         "forceClearCache_:%d, hashChanged:%d, regionChanged_:%d, belowDirty_:%d,"
309         "lastCacheType:%d, cacheUpdateInterval_:%d, canSkip:%d, isLargeArea:%d, filterType_:%d, pendingPurge_:%d,"
310         "forceClearCacheWithLastFrame:%d, rotationChanged:%d",
311         stagingNodeId_, stagingForceUseCache_, stagingForceClearCache_, stagingFilterHashChanged_,
312         stagingFilterRegionChanged_, stagingFilterInteractWithDirty_,
313         lastCacheType_, cacheUpdateInterval_, canSkipFrame_, stagingIsLargeArea_,
314         filterType_, pendingPurge_, stagingForceClearCacheForLastFrame_, stagingRotationChanged_);
315 
316     // if do not request NextVsync, close skip
317     if (stagingForceClearCacheForLastFrame_) {
318         cacheUpdateInterval_ = 0;
319     }
320 
321     stagingIsSkipFrame_ = stagingIsLargeArea_ && canSkipFrame_ && !stagingFilterRegionChanged_;
322 
323     // no valid cache
324     if (lastCacheType_ == FilterCacheType::NONE) {
325         UpdateFlags(FilterCacheType::NONE, false);
326         return;
327     }
328     // No need to invalidate cache if background image is not null or freezed
329     if (stagingForceUseCache_) {
330         UpdateFlags(FilterCacheType::NONE, true);
331         return;
332     }
333 
334     // clear both two type cache: 1. force clear 2. filter region changed 3.skip-frame finished
335     // 4. background changed and effectNode rotated will enable skip-frame, the last frame need to update.
336     if (stagingForceClearCache_ || (stagingFilterRegionChanged_ && !stagingRotationChanged_) || NeedPendingPurge() ||
337         ((stagingFilterInteractWithDirty_ || stagingRotationChanged_) && cacheUpdateInterval_ <= 0)) {
338         UpdateFlags(FilterCacheType::BOTH, false);
339         return;
340     }
341 
342     // clear snapshot cache last frame and clear filtered cache current frame
343     if (lastCacheType_ == FilterCacheType::FILTERED_SNAPSHOT && stagingFilterHashChanged_) {
344         UpdateFlags(FilterCacheType::FILTERED_SNAPSHOT, false);
345         return;
346     }
347 
348     // when blur filter changes, we need to clear filtered cache if it valid.
349     UpdateFlags(stagingFilterHashChanged_ ?
350         FilterCacheType::FILTERED_SNAPSHOT : FilterCacheType::NONE, true);
351 }
352 
353 //should be called in rs main thread
MarkBlurIntersectWithDRM(bool intersectWithDRM,bool isDark)354 void RSFilterDrawable::MarkBlurIntersectWithDRM(bool intersectWithDRM, bool isDark)
355 {
356     stagingIntersectWithDRM_ = intersectWithDRM;
357     stagingIsDarkColorMode_ = isDark;
358 }
359 
IsFilterCacheValid() const360 bool RSFilterDrawable::IsFilterCacheValid() const
361 {
362     return isFilterCacheValid_;
363 }
364 
IsSkippingFrame() const365 bool RSFilterDrawable::IsSkippingFrame() const
366 {
367     return (stagingFilterInteractWithDirty_ || stagingRotationChanged_) && cacheUpdateInterval_ > 0;
368 }
369 
IsForceClearFilterCache() const370 bool RSFilterDrawable::IsForceClearFilterCache() const
371 {
372     return stagingForceClearCache_;
373 }
374 
IsForceUseFilterCache() const375 bool RSFilterDrawable::IsForceUseFilterCache() const
376 {
377     return stagingForceUseCache_;
378 }
379 
NeedPendingPurge() const380 bool RSFilterDrawable::NeedPendingPurge() const
381 {
382     return !stagingFilterInteractWithDirty_ && pendingPurge_;
383 }
384 
MarkEffectNode()385 void RSFilterDrawable::MarkEffectNode()
386 {
387     stagingIsEffectNode_  = true;
388 }
389 
RecordFilterInfos(const std::shared_ptr<RSFilter> & rsFilter)390 void RSFilterDrawable::RecordFilterInfos(const std::shared_ptr<RSFilter>& rsFilter)
391 {
392     auto filter = std::static_pointer_cast<RSDrawingFilter>(rsFilter);
393     if (filter == nullptr) {
394         return;
395     }
396     stagingFilterHashChanged_ = stagingCachedFilterHash_ != filter->Hash();
397     if (stagingFilterHashChanged_) {
398         stagingCachedFilterHash_ = filter->Hash();
399     }
400     filterType_ = filter->GetFilterType();
401     canSkipFrame_ = filter->CanSkipFrame();
402 }
403 
ClearFilterCache()404 void RSFilterDrawable::ClearFilterCache()
405 {
406     if (!RSProperties::FilterCacheEnabled || cacheManager_ == nullptr || filter_ == nullptr) {
407         ROSEN_LOGD("Clear filter cache failed or no need to clear cache, filterCacheEnabled:%{public}d,"
408             "cacheManager:%{public}d, filter:%{public}d", RSProperties::FilterCacheEnabled,
409             cacheManager_ != nullptr, filter_ == nullptr);
410         return;
411     }
412     // 1. clear memory when region changed and is not the first time occured.
413     bool needClearMemoryForGpu = stagingFilterRegionChanged_ && cacheManager_->GetCachedType() != FilterCacheType::NONE;
414     if (filterType_ == RSFilter::AIBAR && stagingIsOccluded_) {
415         cacheManager_->InvalidateFilterCache(FilterCacheType::BOTH);
416     } else {
417         cacheManager_->InvalidateFilterCache(renderClearType_);
418     }
419     // 2. clear memory when region changed without skip frame.
420     needClearMemoryForGpu = needClearMemoryForGpu && cacheManager_->GetCachedType() == FilterCacheType::NONE;
421     if (needClearMemoryForGpu) {
422         cacheManager_->SetFilterInvalid(true);
423     }
424 
425     // whether to clear blur images. true: clear blur image, false: clear snapshot
426     bool isSaveSnapshot = renderFilterHashChanged_ || cacheManager_->GetCachedType() == FilterCacheType::NONE;
427     bool isAIbarWithLastFrame = filterType_ == RSFilter::AIBAR && renderForceClearCacheForLastFrame_; // last vsync
428 
429     if ((filterType_ != RSFilter::AIBAR || isAIbarWithLastFrame) && isSaveSnapshot) {
430         renderClearFilteredCacheAfterDrawing_ = true;      // hold snapshot
431     } else {
432         renderClearFilteredCacheAfterDrawing_ = false;     // hold blur image
433     }
434     if (renderIsEffectNode_ || renderIsSkipFrame_) { renderClearFilteredCacheAfterDrawing_ = renderFilterHashChanged_; }
435     lastCacheType_ = stagingIsOccluded_ ? cacheManager_->GetCachedType() : (renderClearFilteredCacheAfterDrawing_ ?
436         FilterCacheType::SNAPSHOT : FilterCacheType::FILTERED_SNAPSHOT);
437     RS_TRACE_NAME_FMT("RSFilterDrawable::ClearFilterCache nodeId[%llu], clearType:%d,"
438         " isOccluded_:%d, lastCacheType:%d needClearMemoryForGpu:%d ClearFilteredCacheAfterDrawing:%d",
439         renderNodeId_, renderClearType_, stagingIsOccluded_, lastCacheType_, needClearMemoryForGpu,
440         renderClearFilteredCacheAfterDrawing_);
441 }
442 
443 // called after OnSync()
IsFilterCacheValidForOcclusion()444 bool RSFilterDrawable::IsFilterCacheValidForOcclusion()
445 {
446     auto cacheType = cacheManager_->GetCachedType();
447     RS_OPTIONAL_TRACE_NAME_FMT("RSFilterDrawable::IsFilterCacheValidForOcclusion cacheType:%d renderClearType_:%d",
448         cacheType, renderClearType_);
449 
450     return cacheType != FilterCacheType::NONE;
451 }
452 
UpdateFlags(FilterCacheType type,bool cacheValid)453 void RSFilterDrawable::UpdateFlags(FilterCacheType type, bool cacheValid)
454 {
455     stagingClearType_ = type;
456     isFilterCacheValid_ = cacheValid;
457     if (!cacheValid) {
458         cacheUpdateInterval_ = stagingRotationChanged_ ? ROTATION_CACHE_UPDATE_INTERVAL :
459             (filterType_ == RSFilter::AIBAR ? AIBAR_CACHE_UPDATE_INTERVAL :
460             (stagingIsLargeArea_ && canSkipFrame_ ? RSSystemProperties::GetFilterCacheUpdateInterval() : 0));
461         pendingPurge_ = false;
462         return;
463     }
464     if (stagingIsAIBarInteractWithHWC_) {
465         if (cacheUpdateInterval_ > 0) {
466             cacheUpdateInterval_--;
467             pendingPurge_ = true;
468         }
469     } else {
470         if ((stagingFilterInteractWithDirty_ || stagingRotationChanged_) && cacheUpdateInterval_ > 0) {
471             cacheUpdateInterval_--;
472             pendingPurge_ = true;
473         }
474     }
475     stagingIsAIBarInteractWithHWC_ = false;
476 }
477 
IsAIBarCacheValid()478 bool RSFilterDrawable::IsAIBarCacheValid()
479 {
480     if (filterType_ != RSFilter::AIBAR) {
481         return false;
482     }
483     stagingIsAIBarInteractWithHWC_ = true;
484     RS_OPTIONAL_TRACE_NAME_FMT("IsAIBarCacheValid cacheUpdateInterval_:%d forceClearCacheForLastFrame_:%d",
485         cacheUpdateInterval_, stagingForceClearCacheForLastFrame_);
486     if (cacheUpdateInterval_ == 0 || stagingForceClearCacheForLastFrame_) {
487         return false;
488     } else {
489         MarkFilterForceUseCache(true);
490         return true;
491     }
492 }
493 } // namespace DrawableV2
494 } // namespace OHOS::Rosen
495