• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 "property/rs_filter_cache_manager.h"
17 #include "rs_trace.h"
18 #include "render/rs_filter.h"
19 
20 #if defined(NEW_SKIA) && (defined(RS_ENABLE_GL) || defined(RS_ENABLE_VK))
21 #include "include/gpu/GrBackendSurface.h"
22 #include "src/image/SkImage_Base.h"
23 
24 #include "common/rs_optional_trace.h"
25 #include "platform/common/rs_log.h"
26 #include "platform/common/rs_system_properties.h"
27 #include "render/rs_skia_filter.h"
28 
29 namespace OHOS {
30 namespace Rosen {
31 constexpr float PARALLEL_FILTER_RATIO_THRESHOLD = 0.8f;
32 constexpr int AIBAR_CACHE_UPDATE_INTERVAL = 5;
GetCacheState() const33 const char* RSFilterCacheManager::GetCacheState() const
34 {
35     if (cachedFilteredSnapshot_ != nullptr) {
36         return "Filtered image found in cache. Reusing cached result.";
37     } else if (cachedSnapshot_ != nullptr) {
38         return "Snapshot found in cache. Generating filtered image using cached data.";
39     } else {
40         return "No valid cache found.";
41     }
42 }
43 
44 #define CHECK_CACHE_PROCESS_STATUS                      \
45     do {                                                \
46         if (GetStatus() != CacheProcessStatus::DOING) { \
47             return false;                               \
48         }                                               \
49     } while (false)
50 #ifndef ROSEN_ARKUI_X
51 const bool RSFilterCacheManager::RSFilterCacheTask::FilterPartialRenderEnabled =
52     RSSystemProperties::GetFilterPartialRenderEnabled() && RSUniRenderJudgement::IsUniRender();
53 #else
54 const bool RSFilterCacheManager::RSFilterCacheTask::FilterPartialRenderEnabled = false;
55 #endif
56 bool RSFilterCacheManager::SoloTaskPrepare = true;
IsLargeArea(int width,int height)57 inline static bool IsLargeArea(int width, int height)
58 {
59     // Use configurable threshold to determine if the area is large, and apply different cache policy.
60     // [PLANNING]: dynamically adjust the cache policy (e.g. update interval and cache area expansion) according to the
61     // cache size / dirty region percentage / current frame rate / filter radius.
62     static const auto threshold = RSSystemProperties::GetFilterCacheSizeThreshold();
63     return width > threshold && height > threshold;
64 }
65 
66 #ifndef USE_ROSEN_DRAWING
isEqualRect(const SkIRect & src,const RectI & dst)67 inline static bool isEqualRect(const SkIRect& src, const RectI& dst)
68 {
69     return src.x() == dst.GetLeft() && src.y() == dst.GetTop() && src.width() == dst.GetWidth() &&
70            src.height() == dst.GetHeight();
71 }
72 #else
isEqualRect(const Drawing::RectI & src,const RectI & dst)73 inline static bool isEqualRect(const Drawing::RectI& src, const RectI& dst)
74 {
75     return src.GetLeft() == dst.GetLeft() && src.GetTop() == dst.GetTop() && src.GetWidth() == dst.GetWidth() &&
76            src.GetHeight() == dst.GetHeight();
77 }
78 #endif
79 
UpdateCacheStateWithFilterHash(const std::shared_ptr<RSFilter> & filter)80 void RSFilterCacheManager::UpdateCacheStateWithFilterHash(const std::shared_ptr<RSFilter>& filter)
81 {
82     RS_OPTIONAL_TRACE_FUNC();
83     auto filterHash = filter->Hash();
84     if (cachedFilteredSnapshot_ == nullptr || cachedFilterHash_ == filterHash) {
85         return;
86     }
87     // filter changed, clear the filtered snapshot.
88     ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithFilterHash Cache expired. Reason: Cached filtered hash "
89                "%{public}X does not match new hash %{public}X.",
90         cachedFilterHash_, filterHash);
91     cachedFilteredSnapshot_.reset();
92 }
93 #ifndef USE_ROSEN_DRAWING
PostPartialFilterRenderInit(const std::shared_ptr<RSSkiaFilter> & filter,const SkIRect & dstRect,int32_t canvasWidth,int32_t canvasHeight)94 void RSFilterCacheManager::PostPartialFilterRenderInit(
95     const std::shared_ptr<RSSkiaFilter>& filter, const SkIRect& dstRect, int32_t canvasWidth, int32_t canvasHeight)
96 #else
97 void RSFilterCacheManager::PostPartialFilterRenderInit(const std::shared_ptr<RSDrawingFilter>& filter,
98     const Drawing::RectI& dstRect, int32_t canvasWidth, int32_t canvasHeight)
99 #endif
100 {
101     RS_OPTIONAL_TRACE_FUNC();
102     task_->SetCompleted(task_->GetStatus() == CacheProcessStatus::DONE);
103     if (task_->GetStatus() == CacheProcessStatus::DOING) {
104         task_->SetCompleted(!task_->isFirstInit_);
105     }
106     if (task_->GetStatus() == CacheProcessStatus::DONE) {
107         task_->Reset();
108         task_->isFirstInit_ = false;
109         task_->SwapTexture();
110         task_->SetStatus(CacheProcessStatus::WAITING);
111     }
112     if (task_->isTaskRelease_.load()) {
113         return;
114     }
115     if (task_->cachedFirstFilter_ != nullptr && task_->isFirstInit_ &&
116         task_->GetStatus() == CacheProcessStatus::DOING) {
117         cachedFilteredSnapshot_ = task_->cachedFirstFilter_;
118     } else {
119         task_->cachedFirstFilter_ = nullptr;
120     }
121     if (RSFilterCacheTask::FilterPartialRenderEnabled &&
122         (cachedSnapshot_ != nullptr && cachedSnapshot_->cachedImage_ != nullptr) &&
123         (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) &&
124 #ifndef USE_ROSEN_DRAWING
125         IsNearlyFullScreen(cachedSnapshot_->cachedRect_.size(), canvasWidth, canvasHeight)) {
126 #else
127         IsNearlyFullScreen(cachedSnapshot_->cachedRect_, canvasWidth, canvasHeight)) {
128 #endif
129         PostPartialFilterRenderTask(filter, dstRect);
130     }
131 }
132 
133 void RSFilterCacheManager::UpdateCacheStateWithFilterRegion()
134 {
135     if (!IsCacheValid()) {
136         return;
137     }
138     RS_OPTIONAL_TRACE_FUNC();
139 
140     ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithFilterRegion Cache expired. Reason: Filter region is not "
141                "within the cached region.");
142     InvalidateCache();
143 }
144 
145 bool RSFilterCacheManager::UpdateCacheStateWithDirtyRegion(const RSDirtyRegionManager& dirtyManager)
146 {
147     if (!IsCacheValid()) {
148         return false;
149     }
150     RS_OPTIONAL_TRACE_FUNC();
151     auto& cachedImageRect = GetCachedImageRegion();
152     if (dirtyManager.currentFrameDirtyRegion_.Intersect(cachedImageRect) ||
153         std::any_of(dirtyManager.visitedDirtyRegions_.begin(), dirtyManager.visitedDirtyRegions_.end(),
154             [&cachedImageRect](const RectI& rect) { return rect.Intersect(cachedImageRect); })) {
155         // The underlying image is affected by the dirty region, determine if the cache should be invalidated by cache
156         // age. [PLANNING]: also take into account the filter radius / cache size / percentage of intersected area.
157         if (cacheUpdateInterval_ > 0) {
158             ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithDirtyRegion Delaying cache "
159                        "invalidation for %{public}d frames.",
160                 cacheUpdateInterval_);
161             pendingPurge_ = true;
162         } else {
163             InvalidateCache();
164         }
165         return false;
166     }
167     if (pendingPurge_) {
168         ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithDirtyRegion MergeDirtyRect at %{public}d frames",
169             cacheUpdateInterval_);
170         InvalidateCache();
171         return true;
172     } else {
173         return false;
174     }
175 }
176 
177 #ifndef USE_ROSEN_DRAWING
178 bool RSFilterCacheManager::RSFilterCacheTask::InitSurface(GrRecordingContext* grContext)
179 #else
180 bool RSFilterCacheManager::RSFilterCacheTask::InitSurface(Drawing::GPUContext* grContext)
181 #endif
182 {
183     RS_OPTIONAL_TRACE_FUNC();
184     if (!needClearSurface_) {
185         return true;
186     }
187     cacheSurface_ = nullptr;
188 
189     auto runner = AppExecFwk::EventRunner::Current();
190     handler_ = std::make_shared<AppExecFwk::EventHandler>(runner);
191 #ifndef USE_ROSEN_DRAWING
192     SkImageInfo info = SkImageInfo::MakeN32Premul(dstRect_.width(), dstRect_.height());
193     cacheSurface_ = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info);
194 #else
195     Drawing::ImageInfo info = Drawing::ImageInfo::MakeN32Premul(dstRect_.GetWidth(), dstRect_.GetHeight());
196     cacheSurface_ = Drawing::Surface::MakeRenderTarget(grContext, true, info);
197 #endif
198     return cacheSurface_ != nullptr;
199 }
200 
201 bool RSFilterCacheManager::RSFilterCacheTask::Render()
202 {
203     RS_TRACE_NAME_FMT("RSFilterCacheManager::RSFilterCacheTask::Render:%p", this);
204     ROSEN_LOGD("RSFilterCacheManager::RSFilterCacheTask::Render:%{public}p", this);
205     if (cacheSurface_ == nullptr) {
206         SetStatus(CacheProcessStatus::WAITING);
207         ROSEN_LOGE("RSFilterCacheManager::Render: cacheSurface_ is null");
208         return false;
209     }
210     CHECK_CACHE_PROCESS_STATUS;
211     auto cacheCanvas = std::make_shared<RSPaintFilterCanvas>(cacheSurface_.get());
212     if (cacheCanvas == nullptr) {
213         SetStatus(CacheProcessStatus::WAITING);
214         ROSEN_LOGE("RSFilterCacheManager::Render: cacheCanvas is null");
215         return false;
216     }
217 #ifndef USE_ROSEN_DRAWING
218     GrSurfaceOrigin surfaceOrigin = kTopLeft_GrSurfaceOrigin;
219 #ifdef RS_ENABLE_VK
220     if (RSSystemProperties::GetGpuApiType() == GpuApiType::VULKAN ||
221         RSSystemProperties::GetGpuApiType() == GpuApiType::DDGR) {
222         surfaceOrigin = kTopLeft_GrSurfaceOrigin;
223     } else {
224         surfaceOrigin = kBottomLeft_GrSurfaceOrigin;
225     }
226 #else
227     surfaceOrigin = kBottomLeft_GrSurfaceOrigin;
228 #endif // RS_ENABLE_VK
229     auto threadImage = SkImage::MakeFromTexture(cacheCanvas->recordingContext(), cacheBackendTexture_, surfaceOrigin,
230         kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
231     if (!threadImage) {
232         SetStatus(CacheProcessStatus::WAITING);
233         ROSEN_LOGE("RSFilterCacheManager::Render: threadImage is null");
234         return false;
235     }
236     auto src = SkRect::MakeSize(SkSize::Make(snapshotSize_));
237     auto dst = SkRect::MakeSize(SkSize::Make(dstRect_.size()));
238 #else // USE_ROSEN_DRAWING
239     Drawing::TextureOrigin surfaceOrigin = Drawing::TextureOrigin::TOP_LEFT;
240 #ifdef RS_ENABLE_VK
241     if (RSSystemProperties::GetGpuApiType() == GpuApiType::VULKAN ||
242         RSSystemProperties::GetGpuApiType() == GpuApiType::DDGR) {
243         surfaceOrigin = Drawing::TextureOrigin::TOP_LEFT;
244     } else {
245         surfaceOrigin = Drawing::TextureOrigin::BOTTOM_LEFT;
246     }
247 #else
248     surfaceOrigin = Drawing::TextureOrigin::BOTTOM_LEFT;
249 #endif // RS_ENABLE_VK
250     Drawing::BitmapFormat bitmapFormat = { cacheBackendTextureColorType_.load(),
251         Drawing::AlphaType::ALPHATYPE_PREMUL };
252     auto threadImage = std::make_shared<Drawing::Image>();
253     CHECK_CACHE_PROCESS_STATUS;
254     {
255         std::unique_lock<std::mutex> lock(grBackendTextureMutex_);
256         if (cachedSnapshotInTask_ == nullptr ||
257             !threadImage->BuildFromTexture(*cacheCanvas->GetGPUContext(), cacheBackendTexture_.GetTextureInfo(),
258                 surfaceOrigin, bitmapFormat, nullptr)) {
259             SetStatus(CacheProcessStatus::WAITING);
260             ROSEN_LOGE("RSFilterCacheManager::Render: BuildFromTexture is null");
261             return false;
262         }
263     }
264     auto src = Drawing::Rect(0, 0, snapshotSize_.GetWidth(), snapshotSize_.GetHeight());
265     auto dst = Drawing::Rect(0, 0, dstRect_.GetWidth(), dstRect_.GetHeight());
266 #endif // USE_ROSEN_DRAWING
267     CHECK_CACHE_PROCESS_STATUS;
268     filter_->DrawImageRect(*cacheCanvas, threadImage, src, dst);
269     filter_->PostProcess(*cacheCanvas);
270     CHECK_CACHE_PROCESS_STATUS;
271     return true;
272 }
273 
274 bool RSFilterCacheManager::RSFilterCacheTask::SaveFilteredImage()
275 {
276     CHECK_CACHE_PROCESS_STATUS;
277 #ifndef USE_ROSEN_DRAWING
278     resultBackendTexture_ =
279         cacheSurface_->getBackendTexture(SkSurface::BackendHandleAccess::kFlushRead_BackendHandleAccess);
280 #else
281     if (cacheSurface_) {
282         resultBackendTexture_ = cacheSurface_->GetBackendTexture();
283     } else {
284         SetStatus(CacheProcessStatus::WAITING);
285         ROSEN_LOGE("RSFilterCacheManager::SaveFilteredImage: cacheSurface_ is null");
286         return false;
287     }
288 #endif
289     CHECK_CACHE_PROCESS_STATUS;
290     return true;
291 }
292 
293 void RSFilterCacheManager::RSFilterCacheTask::SwapInit()
294 {
295     RS_OPTIONAL_TRACE_FUNC();
296     std::unique_lock<std::mutex> lock(grBackendTextureMutex_);
297     std::swap(filter_, filterBefore_);
298     std::swap(cachedSnapshotInTask_, cachedSnapshotBefore_);
299     std::swap(snapshotSize_, snapshotSizeBefore_);
300     std::swap(dstRect_, dstRectBefore_);
301     if (cachedSnapshotInTask_ != nullptr) {
302 #ifndef USE_ROSEN_DRAWING
303         cacheBackendTexture_ = cachedSnapshotInTask_->cachedImage_->getBackendTexture(false);
304 #else
305         cacheBackendTexture_ = cachedSnapshotInTask_->cachedImage_->GetBackendTexture(false, nullptr);
306 #endif
307         cacheBackendTextureColorType_ = cachedSnapshotInTask_->cachedImage_->GetColorType();
308     }
309 }
310 
311 bool RSFilterCacheManager::RSFilterCacheTask::SetDone()
312 {
313     RS_OPTIONAL_TRACE_FUNC();
314     CHECK_CACHE_PROCESS_STATUS;
315     if (isTaskRelease_.load()) {
316         SetStatus(CacheProcessStatus::WAITING);
317     } else {
318         SetStatus(CacheProcessStatus::DONE);
319     }
320     return true;
321 }
322 #ifndef USE_ROSEN_DRAWING
323 bool RSFilterCacheManager::IsNearlyFullScreen(SkISize imageSize, int32_t canvasWidth, int32_t canvasHeight)
324 #else
325 bool RSFilterCacheManager::IsNearlyFullScreen(Drawing::RectI imageSize, int32_t canvasWidth, int32_t canvasHeight)
326 #endif
327 {
328     RS_OPTIONAL_TRACE_FUNC();
329     auto widthThreshold = static_cast<int32_t>(canvasWidth * PARALLEL_FILTER_RATIO_THRESHOLD);
330     auto heightThreshold = static_cast<int32_t>(canvasHeight * PARALLEL_FILTER_RATIO_THRESHOLD);
331 #ifndef USE_ROSEN_DRAWING
332     return imageSize.width() >= widthThreshold && imageSize.height() >= heightThreshold;
333 #else
334     return imageSize.GetWidth() >= widthThreshold && imageSize.GetHeight() >= heightThreshold;
335 #endif
336 }
337 
338 #ifndef USE_ROSEN_DRAWING
339 void RSFilterCacheManager::PostPartialFilterRenderTask(
340     const std::shared_ptr<RSSkiaFilter>& filter, const SkIRect& dstRect)
341 #else
342 void RSFilterCacheManager::PostPartialFilterRenderTask(
343     const std::shared_ptr<RSDrawingFilter>& filter, const Drawing::RectI& dstRect)
344 #endif
345 {
346     RS_OPTIONAL_TRACE_FUNC();
347     // Prepare a backup of common resources for threads
348     if (RSFilter::postTask != nullptr && (task_->GetStatus() == CacheProcessStatus::WAITING)) {
349         // Because the screenshot is zoomed out, here you need to zoom in
350         task_->InitTask(filter, cachedSnapshot_, dstRect);
351         task_->SetStatus(CacheProcessStatus::DOING);
352         RSFilter::postTask(task_);
353     } else {
354         ROSEN_LOGD("RSFilterCacheManager::PostPartialFilterRenderTask: postTask is null");
355     }
356 }
357 
358 #ifndef USE_ROSEN_DRAWING
359 void RSFilterCacheManager::DrawFilter(RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter,
360     const bool needSnapshotOutset, const std::optional<SkIRect>& srcRect, const std::optional<SkIRect>& dstRect)
361 #else
362 void RSFilterCacheManager::DrawFilter(RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter,
363     const bool needSnapshotOutset, const std::optional<Drawing::RectI>& srcRect,
364     const std::optional<Drawing::RectI>& dstRect)
365 #endif
366 {
367     RS_OPTIONAL_TRACE_FUNC();
368 #ifndef USE_ROSEN_DRAWING
369     if (canvas.getDeviceClipBounds().isEmpty()) {
370 #else
371     if (canvas.GetDeviceClipBounds().IsEmpty()) {
372 #endif
373         return;
374     }
375     const auto& [src, dst] = ValidateParams(canvas, srcRect, dstRect);
376 #ifndef USE_ROSEN_DRAWING
377     if (src.isEmpty() || dst.isEmpty()) {
378 #else
379     if (src.IsEmpty() || dst.IsEmpty()) {
380 #endif
381         return;
382     }
383     RS_TRACE_NAME_FMT("RSFilterCacheManager::DrawFilter status: %s", GetCacheState());
384     CheckCachedImages(canvas);
385     if (!IsCacheValid()) {
386         TakeSnapshot(canvas, filter, src, needSnapshotOutset);
387     } else {
388         --cacheUpdateInterval_;
389     }
390 #ifndef USE_ROSEN_DRAWING
391     auto surface = canvas.getSurface();
392     auto width = surface->width();
393     auto height = surface->height();
394     if (filter->GetFilterType() == RSFilter::LINEAR_GRADIENT_BLUR) {
395         SkMatrix mat = canvas.getTotalMatrix();
396         filter->SetCanvasChange(mat, width, height);
397     }
398 #else
399     auto surface = canvas.GetSurface();
400     auto width = surface->Width();
401     auto height = surface->Height();
402     if (filter->GetFilterType() == RSFilter::LINEAR_GRADIENT_BLUR) {
403         Drawing::Matrix mat = canvas.GetTotalMatrix();
404         filter->SetCanvasChange(mat, width, height);
405     }
406 #endif
407     PostPartialFilterRenderInit(filter, dst, width, height);
408     bool shouldClearFilteredCache = false;
409     if (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) {
410         auto previousFilterHash = cachedFilterHash_;
411         if (RSFilterCacheTask::FilterPartialRenderEnabled && task_->IsCompleted()) {
412             FilterPartialRender(canvas, filter, dst);
413         } else {
414             GenerateFilteredSnapshot(canvas, filter, dst);
415         }
416         newCache_ = true;
417         // If 1. the filter hash matches, 2. the filter region is whole snapshot region, we can safely clear original
418         // snapshot, else we need to clear the filtered snapshot.
419         shouldClearFilteredCache = previousFilterHash != cachedFilterHash_ || !isEqualRect(dst, snapshotRegion_);
420     } else if (RSFilterCacheTask::FilterPartialRenderEnabled && task_->IsCompleted()) {
421         FilterPartialRender(canvas, filter, dst);
422     } else {
423         newCache_ = false;
424     }
425     if (task_->GetStatus() == CacheProcessStatus::DOING && task_->isFirstInit_ &&
426         task_->cachedFirstFilter_ == nullptr) {
427         task_->cachedFirstFilter_ = cachedFilteredSnapshot_;
428     }
429     DrawCachedFilteredSnapshot(canvas, dst);
430     if (filter->GetFilterType() == RSFilter::AIBAR) {
431         shouldClearFilteredCache = false;
432     }
433     task_->SetCompleted(false);
434     // To reduce the memory consumption, we only keep either the cached snapshot or the filtered image.
435     CompactCache(shouldClearFilteredCache);
436 }
437 
438 #ifndef USE_ROSEN_DRAWING
439 const std::shared_ptr<RSPaintFilterCanvas::CachedEffectData> RSFilterCacheManager::GeneratedCachedEffectData(
440     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter, const std::optional<SkIRect>& srcRect,
441     const std::optional<SkIRect>& dstRect, const std::tuple<bool, bool>& forceCacheFlags)
442 #else
443 const std::shared_ptr<RSPaintFilterCanvas::CachedEffectData> RSFilterCacheManager::GeneratedCachedEffectData(
444     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter,
445     const std::optional<Drawing::RectI>& srcRect, const std::optional<Drawing::RectI>& dstRect,
446     const std::tuple<bool, bool>& forceCacheFlags)
447 #endif
448 {
449     RS_OPTIONAL_TRACE_FUNC();
450 #ifndef USE_ROSEN_DRAWING
451     if (canvas.getDeviceClipBounds().isEmpty()) {
452 #else
453     if (canvas.GetDeviceClipBounds().IsEmpty()) {
454 #endif
455         return nullptr;
456     }
457     const auto& [src, dst] = ValidateParams(canvas, srcRect, dstRect, forceCacheFlags);
458 #ifndef USE_ROSEN_DRAWING
459     if (src.isEmpty() || dst.isEmpty()) {
460 #else
461     if (src.IsEmpty() || dst.IsEmpty()) {
462 #endif
463         return nullptr;
464     }
465     RS_TRACE_NAME_FMT("RSFilterCacheManager::GeneratedCachedEffectData status: %s", GetCacheState());
466     CheckCachedImages(canvas);
467     if (!IsCacheValid()) {
468         TakeSnapshot(canvas, filter, src);
469     } else {
470         --cacheUpdateInterval_;
471     }
472 
473     bool shouldClearFilteredCache = false;
474     if (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) {
475         auto previousFilterHash = cachedFilterHash_;
476         GenerateFilteredSnapshot(canvas, filter, dst);
477         // If 1. the filter hash matches, 2. the filter region is whole snapshot region, we can safely clear original
478         // snapshot, else we need to clear the filtered snapshot.
479         shouldClearFilteredCache = previousFilterHash != cachedFilterHash_ || !isEqualRect(dst, snapshotRegion_);
480     }
481     // Keep a reference to the cached image, since CompactCache may invalidate it.
482     auto cachedFilteredSnapshot = cachedFilteredSnapshot_;
483     // To reduce the memory consumption, we only keep either the cached snapshot or the filtered image.
484     if (std::get<0>(forceCacheFlags) || std::get<1>(forceCacheFlags)) {
485         shouldClearFilteredCache = false;
486     }
487     CompactCache(shouldClearFilteredCache);
488     return cachedFilteredSnapshot;
489 }
490 
491 #ifndef USE_ROSEN_DRAWING
492 void RSFilterCacheManager::TakeSnapshot(RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter,
493     const SkIRect& srcRect, const bool needSnapshotOutset)
494 {
495     auto skSurface = canvas.GetSurface();
496     if (skSurface == nullptr) {
497         return;
498     }
499     RS_OPTIONAL_TRACE_FUNC();
500 
501     // shrink the srcRect by 1px to avoid edge artifacts.
502     SkIRect snapshotIBounds;
503     if (needSnapshotOutset) {
504         snapshotIBounds = srcRect.makeOutset(-1, -1);
505     } else {
506         snapshotIBounds = srcRect;
507     }
508 
509     // Take a screenshot.
510     auto snapshot = skSurface->makeImageSnapshot(snapshotIBounds);
511     if (snapshot == nullptr) {
512         ROSEN_LOGD("RSFilterCacheManager::TakeSnapshot failed to make an image snapshot.");
513         return;
514     }
515     if (RSSystemProperties::GetImageGpuResourceCacheEnable(snapshot->width(), snapshot->height())) {
516         ROSEN_LOGD("TakeSnapshot cache image resource(width:%{public}d, height:%{public}d).", snapshot->width(),
517             snapshot->height());
518         as_IB(snapshot)->hintCacheGpuResource();
519     }
520     filter->PreProcess(snapshot);
521 
522     // Update the cache state.
523     snapshotRegion_ = RectI(srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
524     cachedSnapshot_ = std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(snapshot), snapshotIBounds);
525 
526     // If the cached image is larger than threshold, we will increase the cache update interval, which is configurable
527     // by `hdc shell param set persist.sys.graphic.filterCacheUpdateInterval <interval>`, the default value is 1.
528     // Update: we also considered the filter parameters, only enable skip-frame if the blur radius is large enough.
529     // Note: the cache will be invalidated immediately if the cached region cannot fully cover the filter region.
530     if (filter->GetFilterType() == RSFilter::AIBAR) {
531         // when dirty region change, delay 5 frames to update
532         cacheUpdateInterval_ = AIBAR_CACHE_UPDATE_INTERVAL;
533     } else {
534         bool isLargeArea = IsLargeArea(srcRect.width(), srcRect.height());
535         cacheUpdateInterval_ =
536             isLargeArea && filter->CanSkipFrame() ? RSSystemProperties::GetFilterCacheUpdateInterval() : 0;
537     }
538     cachedFilterHash_ = 0;
539     pendingPurge_ = false;
540 }
541 #else
542 void RSFilterCacheManager::TakeSnapshot(RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter,
543     const Drawing::RectI& srcRect, const bool needSnapshotOutset)
544 {
545     auto drawingSurface = canvas.GetSurface();
546     if (drawingSurface == nullptr) {
547         return;
548     }
549     RS_OPTIONAL_TRACE_FUNC();
550 
551     // shrink the srcRect by 1px to avoid edge artifacts.
552     Drawing::RectI snapshotIBounds;
553     snapshotIBounds = srcRect;
554     if (needSnapshotOutset) {
555         snapshotIBounds.MakeOutset(-1, -1);
556     }
557 
558     // Take a screenshot.
559     auto snapshot = drawingSurface->GetImageSnapshot(snapshotIBounds);
560     if (snapshot == nullptr) {
561         ROSEN_LOGD("RSFilterCacheManager::TakeSnapshot failed to make an image snapshot.");
562         return;
563     }
564     if (RSSystemProperties::GetImageGpuResourceCacheEnable(snapshot->GetWidth(), snapshot->GetHeight())) {
565         ROSEN_LOGD("TakeSnapshot cache image resource(width:%{public}d, height:%{public}d).", snapshot->GetWidth(),
566             snapshot->GetHeight());
567         as_IB(snapshot->ExportSkImage().get())->hintCacheGpuResource();
568     }
569     filter->PreProcess(snapshot);
570 
571     // Update the cache state.
572     snapshotRegion_ = RectI(srcRect.GetLeft(), srcRect.GetTop(), srcRect.GetWidth(), srcRect.GetHeight());
573     cachedSnapshot_ = std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(snapshot), snapshotIBounds);
574 
575     // If the cached image is larger than threshold, we will increase the cache update interval, which is configurable
576     // by `hdc shell param set persist.sys.graphic.filterCacheUpdateInterval <interval>`, the default value is 1.
577     // Update: we also considered the filter parameters, only enable skip-frame if the blur radius is large enough.
578     // Note: the cache will be invalidated immediately if the cached region cannot fully cover the filter region.
579     if (filter->GetFilterType() == RSFilter::AIBAR) {
580         // when dirty region change, delay 5 frames to update
581         cacheUpdateInterval_ = AIBAR_CACHE_UPDATE_INTERVAL;
582     } else {
583         bool isLargeArea = IsLargeArea(srcRect.GetWidth(), srcRect.GetHeight());
584         cacheUpdateInterval_ =
585             isLargeArea && filter->CanSkipFrame() ? RSSystemProperties::GetFilterCacheUpdateInterval() : 0;
586     }
587     cachedFilterHash_ = 0;
588     pendingPurge_ = false;
589 }
590 #endif
591 
592 #ifndef USE_ROSEN_DRAWING
593 void RSFilterCacheManager::FilterPartialRender(
594     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter, const SkIRect& dstRect)
595 #else
596 void RSFilterCacheManager::FilterPartialRender(
597     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter, const Drawing::RectI& dstRect)
598 #endif
599 {
600     RS_OPTIONAL_TRACE_FUNC();
601 #ifndef USE_ROSEN_DRAWING
602     auto filteredSnapshot = SkImage::MakeFromTexture(canvas.recordingContext(), task_->GetResultTexture(),
603         kBottomLeft_GrSurfaceOrigin, COLORTYPE_N32, kPremul_SkAlphaType, nullptr);
604 #else
605     auto filteredSnapshot = std::make_shared<Drawing::Image>();
606     Drawing::BitmapFormat bitmapFormat = { Drawing::ColorType::COLORTYPE_N32, Drawing::AlphaType::ALPHATYPE_PREMUL };
607     filteredSnapshot->BuildFromTexture(*canvas.GetGPUContext(), task_->GetResultTexture().GetTextureInfo(),
608         Drawing::TextureOrigin::BOTTOM_LEFT, bitmapFormat, nullptr);
609 #endif
610     if (filteredSnapshot == nullptr) {
611         GenerateFilteredSnapshot(canvas, filter, dstRect);
612     } else {
613 #ifndef USE_ROSEN_DRAWING
614         auto filteredRect = SkIRect::MakeWH(filteredSnapshot->width(), filteredSnapshot->height());
615         if (RSSystemProperties::GetImageGpuResourceCacheEnable(filteredSnapshot->width(), filteredSnapshot->height())) {
616             ROSEN_LOGD("GenerateFilteredSnapshot cache image resource(width:%{public}d, height:%{public}d).",
617                 filteredSnapshot->width(), filteredSnapshot->height());
618             as_IB(filteredSnapshot)->hintCacheGpuResource();
619         }
620 #else
621         auto filteredRect = Drawing::RectI(0, 0, filteredSnapshot->GetWidth(), filteredSnapshot->GetHeight());
622         if (RSSystemProperties::GetImageGpuResourceCacheEnable(
623                 filteredSnapshot->GetWidth(), filteredSnapshot->GetHeight())) {
624             ROSEN_LOGD("GenerateFilteredSnapshot cache image resource(width:%{public}d, height:%{public}d).",
625                 filteredSnapshot->GetWidth(), filteredSnapshot->GetHeight());
626             as_IB(filteredSnapshot->ExportSkImage().get())->hintCacheGpuResource();
627         }
628 #endif
629         cachedFilteredSnapshot_.reset();
630         cachedFilteredSnapshot_ =
631             std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(filteredSnapshot), task_->GetDstRect());
632         cachedFilterHash_ = filter->Hash();
633     }
634 }
635 
636 #ifndef USE_ROSEN_DRAWING
637 void RSFilterCacheManager::GenerateFilteredSnapshot(
638     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter, const SkIRect& dstRect)
639 {
640     auto surface = canvas.GetSurface();
641     if (surface == nullptr || cachedSnapshot_ == nullptr || cachedSnapshot_->cachedImage_ == nullptr) {
642         return;
643     }
644     // The cache type has been validated, so filterRegion_ and cachedImage_ should be valid. There is no need to check
645     // them again.
646     RS_OPTIONAL_TRACE_FUNC();
647 
648     // Create an offscreen canvas with the same size as the filter region.
649     auto offscreenRect = dstRect;
650     auto offscreenSurface = surface->makeSurface(offscreenRect.width(), offscreenRect.height());
651     RSPaintFilterCanvas offscreenCanvas(offscreenSurface.get());
652 
653     // Src rect and dst rect, with origin at (0, 0).
654     auto src = SkRect::MakeSize(SkSize::Make(cachedSnapshot_->cachedRect_.size()));
655     auto dst = SkRect::MakeSize(SkSize::Make(offscreenRect.size()));
656 
657     // Draw the cached snapshot on the offscreen canvas, apply the filter, and post-process.
658     filter->DrawImageRect(offscreenCanvas, cachedSnapshot_->cachedImage_, src, dst);
659     filter->PostProcess(offscreenCanvas);
660 
661     // Update the cache state with the filtered snapshot.
662     auto filteredSnapshot = offscreenSurface->makeImageSnapshot();
663     if (RSSystemProperties::GetImageGpuResourceCacheEnable(filteredSnapshot->width(), filteredSnapshot->height())) {
664         ROSEN_LOGD("GenerateFilteredSnapshot cache image resource(width:%{public}d, height:%{public}d).",
665             filteredSnapshot->width(), filteredSnapshot->height());
666         as_IB(filteredSnapshot)->hintCacheGpuResource();
667     }
668     if (RSSystemProperties::GetRecordingEnabled()) {
669         if (filteredSnapshot->isTextureBacked()) {
670             RS_LOGI("RSFilterCacheManager::GenerateFilteredSnapshot cachedImage from texture to raster image");
671             filteredSnapshot = filteredSnapshot->makeRasterImage();
672         }
673     }
674     cachedFilteredSnapshot_ =
675         std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(filteredSnapshot), offscreenRect);
676     cachedFilterHash_ = filter->Hash();
677 }
678 #else
679 void RSFilterCacheManager::GenerateFilteredSnapshot(
680     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter, const Drawing::RectI& dstRect)
681 {
682     auto surface = canvas.GetSurface();
683     if (surface == nullptr || cachedSnapshot_ == nullptr || cachedSnapshot_->cachedImage_ == nullptr) {
684         return;
685     }
686     // The cache type has been validated, so filterRegion_ and cachedImage_ should be valid. There is no need to check
687     // them again.
688     RS_OPTIONAL_TRACE_FUNC();
689 
690     // Create an offscreen canvas with the same size as the filter region.
691     auto offscreenRect = dstRect;
692     auto offscreenSurface = surface->MakeSurface(offscreenRect.GetWidth(), offscreenRect.GetHeight());
693     RSPaintFilterCanvas offscreenCanvas(offscreenSurface.get());
694 
695     // Src rect and dst rect, with origin at (0, 0).
696     auto src = Drawing::Rect(0, 0, cachedSnapshot_->cachedRect_.GetWidth(), cachedSnapshot_->cachedRect_.GetHeight());
697     auto dst = Drawing::Rect(0, 0, offscreenRect.GetWidth(), offscreenRect.GetHeight());
698 
699     // Draw the cached snapshot on the offscreen canvas, apply the filter, and post-process.
700     filter->DrawImageRect(offscreenCanvas, cachedSnapshot_->cachedImage_, src, dst);
701     filter->PostProcess(offscreenCanvas);
702 
703     // Update the cache state with the filtered snapshot.
704     auto filteredSnapshot = offscreenSurface->GetImageSnapshot();
705     if (RSSystemProperties::GetImageGpuResourceCacheEnable(
706             filteredSnapshot->GetWidth(), filteredSnapshot->GetHeight())) {
707         ROSEN_LOGD("GenerateFilteredSnapshot cache image resource(width:%{public}d, height:%{public}d).",
708             filteredSnapshot->GetWidth(), filteredSnapshot->GetHeight());
709         as_IB(filteredSnapshot->ExportSkImage().get())->hintCacheGpuResource();
710     }
711     if (RSSystemProperties::GetRecordingEnabled()) {
712         if (filteredSnapshot->IsTextureBacked()) {
713             RS_LOGI("RSFilterCacheManager::GenerateFilteredSnapshot cachedImage from texture to raster image");
714             filteredSnapshot = filteredSnapshot->MakeRasterImage();
715         }
716     }
717     cachedFilteredSnapshot_ =
718         std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(filteredSnapshot), offscreenRect);
719     cachedFilterHash_ = filter->Hash();
720 }
721 #endif
722 
723 #ifndef USE_ROSEN_DRAWING
724 void RSFilterCacheManager::DrawCachedFilteredSnapshot(RSPaintFilterCanvas& canvas, const SkIRect& dstRect) const
725 {
726     if (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) {
727         return;
728     }
729     RS_OPTIONAL_TRACE_FUNC();
730 
731     // Draw in device coordinates.
732     SkAutoCanvasRestore autoRestore(&canvas, true);
733     canvas.resetMatrix();
734 
735     // Only draw within the visible rect.
736     ClipVisibleRect(canvas);
737 
738     // The cache type and parameters has been validated, dstRect must be subset of cachedFilteredSnapshot_->cachedRect_.
739     SkRect dst = SkRect::Make(dstRect);
740     SkRect src;
741     if (newCache_) {
742         src = SkRect::MakeSize(SkSize::Make(cachedFilteredSnapshot_->cachedImage_->dimensions()));
743     } else {
744         src = SkRect::Make(dstRect.makeOffset(-cachedFilteredSnapshot_->cachedRect_.topLeft()));
745         src.intersect(SkRect::Make(cachedFilteredSnapshot_->cachedImage_->bounds()));
746     }
747 
748     SkPaint paint;
749     paint.setAntiAlias(true);
750     canvas.drawImageRect(cachedFilteredSnapshot_->cachedImage_, src, dst, SkSamplingOptions(), &paint,
751         SkCanvas::kFast_SrcRectConstraint);
752 }
753 #else
754 void RSFilterCacheManager::DrawCachedFilteredSnapshot(RSPaintFilterCanvas& canvas, const Drawing::RectI& dstRect) const
755 {
756     if (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) {
757         return;
758     }
759     RS_OPTIONAL_TRACE_FUNC();
760 
761     // Draw in device coordinates.
762     Drawing::AutoCanvasRestore autoRestore(canvas, true);
763     canvas.ResetMatrix();
764 
765     // Only draw within the visible rect.
766     ClipVisibleRect(canvas);
767 
768     // The cache type and parameters has been validated, dstRect must be subset of cachedFilteredSnapshot_->cachedRect_.
769     Drawing::Rect dst(dstRect.GetLeft(), dstRect.GetTop(), dstRect.GetRight(), dstRect.GetBottom());
770     Drawing::Rect src;
771     if (newCache_) {
772         src = { 0, 0, cachedFilteredSnapshot_->cachedImage_->GetWidth(),
773             cachedFilteredSnapshot_->cachedImage_->GetHeight() };
774     } else {
775         src = { dstRect.GetLeft(), dstRect.GetTop(), dstRect.GetRight(), dstRect.GetBottom() };
776         src.Offset(-cachedFilteredSnapshot_->cachedRect_.GetLeft(), -cachedFilteredSnapshot_->cachedRect_.GetTop());
777         src.Intersect(Drawing::Rect(0, 0, cachedFilteredSnapshot_->cachedImage_->GetWidth(),
778             cachedFilteredSnapshot_->cachedImage_->GetHeight()));
779     }
780 
781     Drawing::Brush brush;
782     brush.SetAntiAlias(true);
783     canvas.AttachBrush(brush);
784     canvas.DrawImageRect(*cachedFilteredSnapshot_->cachedImage_, src, dst, Drawing::SamplingOptions(),
785         Drawing::SrcRectConstraint::FAST_SRC_RECT_CONSTRAINT);
786     canvas.DetachBrush();
787 }
788 #endif
789 
790 void RSFilterCacheManager::InvalidateCache(CacheType cacheType)
791 {
792     // bitwise test
793     if (cacheType & CacheType::CACHE_TYPE_SNAPSHOT) {
794         cachedSnapshot_.reset();
795     }
796     if (cacheType & CacheType::CACHE_TYPE_FILTERED_SNAPSHOT) {
797         cachedFilteredSnapshot_.reset();
798     }
799 }
800 
801 void RSFilterCacheManager::ReleaseCacheOffTree()
802 {
803     RS_OPTIONAL_TRACE_FUNC();
804     ROSEN_LOGD("RSFilterCacheManager::ReleaseCacheOffTree task_:%{public}p", task_.get());
805     std::unique_lock<std::mutex> lock(task_->grBackendTextureMutex_);
806     cachedSnapshot_.reset();
807     cachedFilteredSnapshot_.reset();
808     newCache_ = false;
809     task_->isTaskRelease_.store(true);
810     task_->cachedFirstFilter_ = nullptr;
811     task_->ResetInTask();
812     task_->SetCompleted(false);
813     task_->isFirstInit_ = true;
814     task_->SetStatus(CacheProcessStatus::WAITING);
815     task_->Reset();
816     if (task_->GetHandler() != nullptr) {
817         auto task_tmp = task_;
818         task_->GetHandler()->PostTask(
819             [task_tmp]() { task_tmp->ResetGrContext(); }, AppExecFwk::EventQueue::Priority::IMMEDIATE);
820     }
821 }
822 
823 #ifndef USE_ROSEN_DRAWING
824 inline void RSFilterCacheManager::ClipVisibleRect(RSPaintFilterCanvas& canvas)
825 {
826     auto visibleIRect = canvas.GetVisibleRect().round();
827     auto deviceClipRect = canvas.getDeviceClipBounds();
828     if (!visibleIRect.isEmpty() && deviceClipRect.intersect(visibleIRect)) {
829         canvas.clipIRect(visibleIRect);
830     }
831 }
832 #else
833 inline void RSFilterCacheManager::ClipVisibleRect(RSPaintFilterCanvas& canvas)
834 {
835     auto visibleRectF = canvas.GetVisibleRect();
836     visibleRectF.Round();
837     Drawing::RectI visibleIRect = { (int)visibleRectF.GetLeft(), (int)visibleRectF.GetTop(),
838         (int)visibleRectF.GetRight(), (int)visibleRectF.GetBottom() };
839     auto deviceClipRect = canvas.GetDeviceClipBounds();
840     if (!visibleIRect.IsEmpty() && deviceClipRect.Intersect(visibleIRect)) {
841         canvas.ClipIRect(visibleIRect, Drawing::ClipOp::INTERSECT);
842     }
843 }
844 #endif
845 
846 const RectI& RSFilterCacheManager::GetCachedImageRegion() const
847 {
848     static const auto emptyRect = RectI();
849     return IsCacheValid() ? snapshotRegion_ : emptyRect;
850 }
851 
852 inline static bool IsCacheInvalid(const RSPaintFilterCanvas::CachedEffectData& cache, RSPaintFilterCanvas& canvas)
853 {
854 #ifndef USE_ROSEN_DRAWING
855     return cache.cachedImage_ == nullptr || !cache.cachedImage_->isValid(canvas.recordingContext());
856 #else
857     return cache.cachedImage_ == nullptr || !cache.cachedImage_->IsValid(canvas.GetGPUContext().get());
858 #endif
859 }
860 
861 void RSFilterCacheManager::CheckCachedImages(RSPaintFilterCanvas& canvas)
862 {
863     if (cachedSnapshot_ != nullptr && IsCacheInvalid(*cachedSnapshot_, canvas)) {
864         ROSEN_LOGE("RSFilterCacheManager::CheckCachedImages cachedSnapshot_ is invalid");
865         cachedSnapshot_.reset();
866     }
867     if (cachedFilteredSnapshot_ != nullptr && IsCacheInvalid(*cachedFilteredSnapshot_, canvas)) {
868         ROSEN_LOGE("RSFilterCacheManager::CheckCachedImages cachedFilteredSnapshot_ is invalid");
869         cachedFilteredSnapshot_.reset();
870     }
871 }
872 
873 #ifndef USE_ROSEN_DRAWING
874 std::tuple<SkIRect, SkIRect> RSFilterCacheManager::ValidateParams(
875     RSPaintFilterCanvas& canvas, const std::optional<SkIRect>& srcRect,
876     const std::optional<SkIRect>& dstRect, const std::tuple<bool, bool>& forceCacheFlags)
877 {
878     SkIRect src;
879     SkIRect dst;
880     auto deviceRect = SkIRect::MakeWH(canvas.imageInfo().width(), canvas.imageInfo().height());
881     if (!srcRect.has_value()) {
882         src = canvas.getDeviceClipBounds();
883     } else {
884         src = srcRect.value();
885         src.intersect(deviceRect);
886     }
887     if (!dstRect.has_value()) {
888         dst = src;
889     } else {
890         dst = dstRect.value();
891         dst.intersect(deviceRect);
892     }
893     if (snapshotRegion_.GetLeft() > dst.x() || snapshotRegion_.GetRight() < dst.right() ||
894         snapshotRegion_.GetTop() > dst.y() || snapshotRegion_.GetBottom() < dst.bottom()) {
895         // dst region is out of snapshot region, cache is invalid.
896         // It should already be checked by UpdateCacheStateWithFilterRegion in prepare phase, we should never be here.
897         ROSEN_LOGE("RSFilterCacheManager::ValidateParams Cache expired. Reason: dst region is out of snapshot region.");
898     }
899     return { src, dst };
900 }
901 #else
902 std::tuple<Drawing::RectI, Drawing::RectI> RSFilterCacheManager::ValidateParams(RSPaintFilterCanvas& canvas,
903     const std::optional<Drawing::RectI>& srcRect, const std::optional<Drawing::RectI>& dstRect,
904     const std::tuple<bool, bool>& forceCacheFlags)
905 {
906     Drawing::RectI src;
907     Drawing::RectI dst;
908     auto deviceRect = Drawing::RectI(0, 0, canvas.GetImageInfo().GetWidth(), canvas.GetImageInfo().GetHeight());
909     if (!srcRect.has_value()) {
910         src = canvas.GetDeviceClipBounds();
911     } else {
912         src = srcRect.value();
913         src.Intersect(deviceRect);
914     }
915     if (!dstRect.has_value()) {
916         dst = src;
917     } else {
918         dst = dstRect.value();
919         dst.Intersect(deviceRect);
920     }
921     if (snapshotRegion_.GetLeft() > dst.GetLeft() || snapshotRegion_.GetRight() < dst.GetRight() ||
922         snapshotRegion_.GetTop() > dst.GetTop() || snapshotRegion_.GetBottom() < dst.GetBottom()) {
923         // dst region is out of snapshot region, cache is invalid.
924         // It should already be checked by UpdateCacheStateWithFilterRegion in prepare phase, we should never be here.
925         ROSEN_LOGD("RSFilterCacheManager::ValidateParams Cache expired. Reason: dst region is out of snapshot region.");
926         if (!std::get<0>(forceCacheFlags) || !std::get<1>(forceCacheFlags)) {
927             InvalidateCache();
928         }
929     }
930     return { src, dst };
931 }
932 #endif
933 
934 inline void RSFilterCacheManager::CompactCache(bool shouldClearFilteredCache)
935 {
936     if (pendingPurge_ && cacheUpdateInterval_ == 0) {
937         InvalidateCache();
938         return;
939     }
940     InvalidateCache(
941         shouldClearFilteredCache ? CacheType::CACHE_TYPE_FILTERED_SNAPSHOT : CacheType::CACHE_TYPE_SNAPSHOT);
942 }
943 } // namespace Rosen
944 } // namespace OHOS
945 #endif
946