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