• 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 
18 #ifndef USE_ROSEN_DRAWING
19 #include "common/rs_optional_trace.h"
20 #include "platform/common/rs_log.h"
21 #include "platform/common/rs_system_properties.h"
22 #include "render/rs_skia_filter.h"
23 #include "src/image/SkImage_Base.h"
24 
25 #ifdef RS_ENABLE_GL
26 #include "include/gpu/GrBackendSurface.h"
27 #endif
28 
29 namespace OHOS {
30 namespace Rosen {
IsLargeArea(int width,int height)31 inline bool IsLargeArea(int width, int height)
32 {
33     // Use configurable threshold to determine if the area is large, and apply different cache policy.
34     // [PLANNING]: dynamically adjust the cache policy (e.g. update interval and cache area expansion) according to the
35     // cache size / dirty region percentage / current frame rate / filter radius.
36     static auto threshold = RSSystemProperties::GetFilterCacheSizeThreshold();
37     return width > threshold && height > threshold;
38 }
39 
UpdateCacheStateWithFilterHash(uint32_t filterHash)40 void RSFilterCacheManager::UpdateCacheStateWithFilterHash(uint32_t filterHash)
41 {
42     if (cacheType_ != CacheType::CACHE_TYPE_FILTERED_SNAPSHOT) {
43         return;
44     }
45     // If we are caching a filtered snapshot, we need to check if the filter hash matches.
46     if (filterHash == cachedFilterHash_) {
47         return;
48     }
49 
50     RS_OPTIONAL_TRACE_FUNC_BEGIN();
51     ROSEN_LOGD(
52         "RSFilterCacheManager::UpdateCacheStateWithFilterHash Cache expired. Reason: Cached filtered snapshot %X "
53         "does not match filter hash %X.",
54         cachedFilterHash_, filterHash);
55     InvalidateCache();
56     RS_OPTIONAL_TRACE_FUNC_END();
57 }
58 
UpdateCacheStateWithFilterRegion(const RectI & filterRegion)59 void RSFilterCacheManager::UpdateCacheStateWithFilterRegion(const RectI& filterRegion)
60 {
61     if (cacheType_ == CacheType::CACHE_TYPE_NONE) {
62         return;
63     }
64 
65     // Test if the filter region is contained in the cached region.
66     auto skFilterRegion = SkIRect::MakeLTRB(
67         filterRegion.GetLeft(), filterRegion.GetTop(), filterRegion.GetRight(), filterRegion.GetBottom());
68     if (cachedImageRegion_.contains(skFilterRegion)) {
69         filterRegion_ = skFilterRegion;
70         return;
71     }
72     RS_OPTIONAL_TRACE_FUNC_BEGIN();
73     ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithFilterRegion Cache expired. Reason: Filter region is not "
74                "within the cached region.");
75     InvalidateCache();
76     RS_OPTIONAL_TRACE_FUNC_END();
77 }
78 
UpdateCacheStateWithFilterRegion(bool isCachedRegionCannotCoverFilterRegion)79 void RSFilterCacheManager::UpdateCacheStateWithFilterRegion(bool isCachedRegionCannotCoverFilterRegion)
80 {
81     if (cacheType_ == CacheType::CACHE_TYPE_NONE || isCachedRegionCannotCoverFilterRegion == false) {
82         return;
83     }
84     RS_OPTIONAL_TRACE_FUNC_BEGIN();
85 
86     ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithFilterRegion Cache expired. Reason: Filter region is not "
87                "within the cached region.");
88     InvalidateCache();
89     RS_OPTIONAL_TRACE_FUNC_END();
90 }
91 
UpdateCacheStateWithDirtyRegion(const RectI & dirtyRegion)92 void RSFilterCacheManager::UpdateCacheStateWithDirtyRegion(const RectI& dirtyRegion)
93 {
94     if (cacheType_ == CacheType::CACHE_TYPE_NONE) {
95         return;
96     }
97 
98     // Use the dirty region to determine if the cache is valid.
99     auto SkDirtyRegion =
100         SkIRect::MakeLTRB(dirtyRegion.GetLeft(), dirtyRegion.GetTop(), dirtyRegion.GetRight(), dirtyRegion.GetBottom());
101     // The underlying image is not affected by the dirty region, cache is valid.
102     if (!SkDirtyRegion.intersect(filterRegion_)) {
103         return;
104     }
105 
106     RS_OPTIONAL_TRACE_FUNC_BEGIN();
107     // The underlying image is affected by the dirty region, determine if the cache should be invalidated by cache  age.
108     // [PLANNING]: also take into account the filter radius / cache size / percentage of intersected area.
109     if (cacheUpdateInterval_ > 0) {
110         ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithDirtyRegion Delaying cache invalidation for %d frames.",
111             cacheUpdateInterval_);
112     } else {
113         InvalidateCache();
114     }
115     RS_OPTIONAL_TRACE_FUNC_END();
116 }
117 
UpdateCacheStateWithDirtyRegion(bool isIntersectedWithDirtyRegion)118 void RSFilterCacheManager::UpdateCacheStateWithDirtyRegion(bool isIntersectedWithDirtyRegion)
119 {
120     if (cacheType_ == CacheType::CACHE_TYPE_NONE || isIntersectedWithDirtyRegion == false) {
121         return;
122     }
123     RS_OPTIONAL_TRACE_FUNC_BEGIN();
124 
125     // The underlying image is affected by the dirty region, determine if the cache should be invalidated by cache age.
126     // [PLANNING]: also take into account the filter radius / cache size / percentage of intersected area.
127     if (cacheUpdateInterval_ > 0) {
128         ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithDirtyRegion Delaying cache invalidation for %d frames.",
129             cacheUpdateInterval_);
130     } else {
131         InvalidateCache();
132     }
133     RS_OPTIONAL_TRACE_FUNC_END();
134 }
135 
DrawFilter(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSSkiaFilter> & filter)136 void RSFilterCacheManager::DrawFilter(RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter)
137 {
138     // Filter validation is not needed, since it's already done in RSPropertiesPainter::DrawFilter.
139     auto clipIBounds = canvas.getDeviceClipBounds();
140     if (clipIBounds.isEmpty()) {
141         // clipIBounds is empty, no need to draw filter.
142         return;
143     }
144     RS_OPTIONAL_TRACE_FUNC_BEGIN();
145 
146     SkAutoCanvasRestore autoRestore(&canvas, true);
147     canvas.resetMatrix();
148 
149     ReattachCachedImage(canvas);
150 
151     if (cacheType_ == CacheType::CACHE_TYPE_NONE) {
152         // The cache is expired, take a snapshot again.
153         TakeSnapshot(canvas, filter);
154         ClipVisibleRect(canvas);
155         DrawCachedSnapshot(canvas, filter);
156         RS_OPTIONAL_TRACE_FUNC_END();
157         return;
158     }
159 
160     // Update the cache age, this will ensure that an old cache will be invalidated immediately when intersecting with
161     // dirty region.
162     if (cacheUpdateInterval_ > 0) {
163         --cacheUpdateInterval_;
164     }
165     if (cacheType_ == CacheType::CACHE_TYPE_FILTERED_SNAPSHOT) {
166         // We are caching a filtered snapshot, draw the cached filtered image directly.
167         ClipVisibleRect(canvas);
168         DrawCachedFilteredSnapshot(canvas);
169         RS_OPTIONAL_TRACE_FUNC_END();
170         return;
171     }
172 
173     // cacheType_ == CacheType::CACHE_TYPE_SNAPSHOT
174     // We are caching a snapshot, check if we should convert it to a filtered snapshot.
175     auto filterHash = filter->Hash();
176     if (filterHash == cachedFilterHash_ && filterRegion_ == clipIBounds) {
177         // Both filter and filterRegion have not changed, increase the counter.
178         frameSinceLastFilterChange_++;
179     } else {
180         // Filter or filterRegion changed, reset the counter.
181         frameSinceLastFilterChange_ = 0;
182         filterRegion_ = clipIBounds;
183         if (filterHash != cachedFilterHash_) {
184             filter->PreProcess(cachedImage_);
185             cachedFilterHash_ = filterHash;
186         }
187     }
188     // filter has not changed for more than 3 frames, convert the cache image to a filtered image.
189     if (frameSinceLastFilterChange_ >= 3) {
190         ROSEN_LOGD(
191             "RSFilterCacheManager::DrawFilter The filter filter and region have not changed in the last 3 frames, "
192             "generating a filtered image cache.");
193         GenerateFilteredSnapshot(canvas, filter);
194         ClipVisibleRect(canvas);
195         DrawCachedFilteredSnapshot(canvas);
196         RS_OPTIONAL_TRACE_FUNC_END();
197         return;
198     }
199 
200     ClipVisibleRect(canvas);
201     DrawCachedSnapshot(canvas, filter);
202     RS_OPTIONAL_TRACE_FUNC_END();
203 }
204 
GeneratedCachedEffectData(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSSkiaFilter> & filter)205 CachedEffectData RSFilterCacheManager::GeneratedCachedEffectData(
206     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter)
207 {
208     // This function is similar to RSFilterCacheManager::DrawFilter, but does not draw anything on the canvas. Instead,
209     // it directly returns the cached image and region. Filter validation is not needed, since it's already done in
210     // RSPropertiesPainter::GenerateCachedEffectData.
211     RS_OPTIONAL_TRACE_FUNC_BEGIN();
212 
213     ReattachCachedImage(canvas);
214 
215     if (cacheType_ == CacheType::CACHE_TYPE_NONE) {
216         // The cache is expired, so take an image snapshot again and cache it.
217         ROSEN_LOGD("RSFilterCacheManager::GeneratedCachedEffectData Cache expired, taking snapshot.");
218         TakeSnapshot(canvas, filter);
219     }
220 
221     // The GeneratedCachedEffectData function generates a filtered image cache, but it does not use any cache policies
222     // like DrawFilter.
223     if (cacheType_ == CacheType::CACHE_TYPE_SNAPSHOT) {
224         ROSEN_LOGD(
225             "RSFilterCacheManager::GeneratedCachedEffectData Cache is snapshot, generating filtered image cache.");
226         filterRegion_ = cachedImageRegion_;
227         GenerateFilteredSnapshot(canvas, filter);
228     }
229 
230     if (cacheType_ != CacheType::CACHE_TYPE_FILTERED_SNAPSHOT) {
231         ROSEN_LOGE("RSFilterCacheManager::GeneratedCachedEffectData Cache generation failed.");
232         RS_OPTIONAL_TRACE_FUNC_END();
233         return {};
234     }
235 
236     RS_OPTIONAL_TRACE_FUNC_END();
237     return { cachedImage_, cachedImageRegion_ };
238 }
239 
TakeSnapshot(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSSkiaFilter> & filter)240 void RSFilterCacheManager::TakeSnapshot(RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter)
241 {
242     auto skSurface = canvas.GetSurface();
243     if (skSurface == nullptr) {
244         return;
245     }
246     RS_OPTIONAL_TRACE_FUNC_BEGIN();
247 
248     auto clipIBounds = canvas.getDeviceClipBounds();
249     auto snapshotIBounds = clipIBounds;
250     bool isLargeArea = IsLargeArea(clipIBounds.width(), clipIBounds.height());
251     if (isLargeArea) {
252         // If the filter region is smaller than the threshold, we will not increase the cache update interval.
253         // To reduce the chance of cache invalidation caused by small movements, we expand the snapshot region by 5
254         // pixels.
255         snapshotIBounds.outset(5, 5); // expand the snapshot region by 5 pixels.
256         // Make sure the clipIPadding is not larger than the canvas or screen size.
257         snapshotIBounds.intersect(SkIRect::MakeSize(canvas.getBaseLayerSize()));
258     }
259 
260     // Take a screenshot.
261     cachedImage_ = skSurface->makeImageSnapshot(snapshotIBounds);
262     if (cachedImage_ == nullptr) {
263         ROSEN_LOGE("RSFilterCacheManager::TakeSnapshot failed to make an image snapshot.");
264         RS_OPTIONAL_TRACE_FUNC_END();
265         return;
266     }
267     if (RSSystemProperties::GetImageGpuResourceCacheEnable(cachedImage_->width(), cachedImage_->height())) {
268         ROSEN_LOGD("TakeSnapshot cache image resource(width:%d, height:%d).",
269             cachedImage_->width(), cachedImage_->height());
270         as_IB(cachedImage_)->hintCacheGpuResource();
271     }
272     filter->PreProcess(cachedImage_);
273 
274     // Update the cache state.
275     cacheType_ = CacheType::CACHE_TYPE_SNAPSHOT;
276     filterRegion_ = clipIBounds;
277     cachedFilterHash_ = filter->Hash();
278     cachedImageRegion_ = snapshotIBounds;
279     frameSinceLastFilterChange_ = 0;
280 
281     // If the cached image is larger than threshold, we will increase the cache update interval, which is configurable
282     // by `hdc shell param set persist.sys.graphic.filterCacheUpdateInterval <interval>`, the default value is 1.
283     // Update: we also considered the filter parameters, only enable skip-frame if the blur radius is large enough.
284     // Note: the cache will be invalidated immediately if the cached region cannot fully cover the filter region.
285     cacheUpdateInterval_ =
286         isLargeArea && filter->CanSkipFrame() ? RSSystemProperties::GetFilterCacheUpdateInterval() : 0;
287     RS_OPTIONAL_TRACE_FUNC_END();
288 }
289 
GenerateFilteredSnapshot(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSSkiaFilter> & filter)290 void RSFilterCacheManager::GenerateFilteredSnapshot(
291     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter)
292 {
293     auto surface = canvas.GetSurface();
294     if (cacheType_ != CacheType::CACHE_TYPE_SNAPSHOT || surface == nullptr) {
295         return;
296     }
297     // The cache type has been validated, so filterRegion_ and cachedImage_ should be valid. There is no need to check
298     // them again.
299     RS_OPTIONAL_TRACE_FUNC_BEGIN();
300 
301     // Create an offscreen canvas with the same size as the filter region.
302     auto offscreenSurface = surface->makeSurface(filterRegion_.width(), filterRegion_.height());
303     RSPaintFilterCanvas offscreenCanvas(offscreenSurface.get());
304 
305     // Align the offscreen canvas coordinate system to the filter region.
306     offscreenCanvas.translate(-SkIntToScalar(filterRegion_.x()), -SkIntToScalar(filterRegion_.y()));
307 
308     // Draw the cached snapshot onto the offscreen canvas, applying the filter.
309     DrawCachedSnapshot(offscreenCanvas, filter);
310 
311     // Update the cache state with the filtered snapshot.
312     cacheType_ = CacheType::CACHE_TYPE_FILTERED_SNAPSHOT;
313     cachedImage_ = offscreenSurface->makeImageSnapshot();
314     if (RSSystemProperties::GetImageGpuResourceCacheEnable(cachedImage_->width(), cachedImage_->height())) {
315         ROSEN_LOGD("GenerateFilteredSnapshot cache image resource(width:%d, height:%d).",
316             cachedImage_->width(), cachedImage_->height());
317         as_IB(cachedImage_)->hintCacheGpuResource();
318     }
319     cachedImageRegion_ = filterRegion_;
320     RS_OPTIONAL_TRACE_FUNC_END();
321 }
322 
DrawCachedSnapshot(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSSkiaFilter> & filter) const323 void RSFilterCacheManager::DrawCachedSnapshot(
324     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSSkiaFilter>& filter) const
325 {
326     if (cacheType_ != CacheType::CACHE_TYPE_SNAPSHOT) {
327         return;
328     }
329     RS_OPTIONAL_TRACE_FUNC_BEGIN();
330 
331     // The cache type has been validated, so filterRegion_, cachedImage_, and cachedImageRegion_ should be valid. There
332     // is no need to check them again.
333     auto dstRect = SkRect::Make(filterRegion_);
334 
335     // Shrink the srcRect by 1px to avoid edge artifacts, and align it to the cachedImage_ coordinate system.
336     auto srcRect = dstRect.makeOutset(-1.0f, -1.0f);
337     srcRect.offset(-SkIntToScalar(cachedImageRegion_.x()), -SkIntToScalar(cachedImageRegion_.y()));
338 
339     filter->DrawImageRect(canvas, cachedImage_, srcRect, dstRect);
340     filter->PostProcess(canvas);
341     RS_OPTIONAL_TRACE_FUNC_END();
342 }
343 
DrawCachedFilteredSnapshot(RSPaintFilterCanvas & canvas) const344 void RSFilterCacheManager::DrawCachedFilteredSnapshot(RSPaintFilterCanvas& canvas) const
345 {
346     if (cacheType_ != CacheType::CACHE_TYPE_FILTERED_SNAPSHOT) {
347         return;
348     }
349     RS_OPTIONAL_TRACE_FUNC_BEGIN();
350 
351     // The cache type has been validated, so filterRegion_ and cachedImage_ should be valid. There is no need to check
352     // them again.
353     auto dstRect = SkRect::Make(filterRegion_);
354 
355     SkPaint paint;
356     paint.setAntiAlias(true);
357 #ifdef NEW_SKIA
358     canvas.drawImageRect(cachedImage_, dstRect, SkSamplingOptions(), &paint);
359 #endif
360     RS_OPTIONAL_TRACE_FUNC_END();
361 }
362 
InvalidateCache()363 void RSFilterCacheManager::InvalidateCache()
364 {
365     cacheType_ = CacheType::CACHE_TYPE_NONE;
366     cachedFilterHash_ = 0;
367     cachedImage_.reset();
368     cacheUpdateInterval_ = 0;
369     frameSinceLastFilterChange_ = 0;
370 }
371 
ClipVisibleRect(RSPaintFilterCanvas & canvas) const372 void RSFilterCacheManager::ClipVisibleRect(RSPaintFilterCanvas& canvas) const
373 {
374     auto visibleIRect = canvas.GetVisibleRect().round();
375     auto deviceClipRect = canvas.getDeviceClipBounds();
376     if (!visibleIRect.isEmpty() && deviceClipRect.intersect(visibleIRect)) {
377 #ifdef NEW_SKIA
378         canvas.clipIRect(visibleIRect);
379 #endif
380     }
381 }
382 
GetCachedImageRegion() const383 const SkIRect& RSFilterCacheManager::GetCachedImageRegion() const
384 {
385     return cachedImageRegion_;
386 }
387 
ReattachCachedImage(RSPaintFilterCanvas & canvas)388 void RSFilterCacheManager::ReattachCachedImage(RSPaintFilterCanvas& canvas)
389 {
390 #if defined(NEW_SKIA)
391     if (cacheType_ == CacheType::CACHE_TYPE_NONE || cachedImage_->isValid(canvas.recordingContext())) {
392 #else
393     if (cacheType_ == CacheType::CACHE_TYPE_NONE || cachedImage_->isValid(canvas.getGrContext())) {
394 #endif
395         return;
396     }
397     RS_OPTIONAL_TRACE_FUNC_BEGIN();
398 
399 #ifdef RS_ENABLE_GL
400     auto sharedBackendTexture = cachedImage_->getBackendTexture(false);
401     if (!sharedBackendTexture.isValid()) {
402         ROSEN_LOGE("RSFilterCacheManager::ReattachCachedImage failed to get backend texture.");
403         InvalidateCache();
404         RS_OPTIONAL_TRACE_FUNC_END();
405         return;
406     }
407 #if defined(NEW_SKIA)
408     auto reattachedCachedImage = SkImage::MakeFromTexture(canvas.recordingContext(), sharedBackendTexture,
409 #else
410     auto reattachedCachedImage = SkImage::MakeFromTexture(canvas.getGrContext(), sharedBackendTexture,
411 #endif
412         kBottomLeft_GrSurfaceOrigin, cachedImage_->colorType(), cachedImage_->alphaType(), nullptr);
413 #if defined(NEW_SKIA)
414     if (reattachedCachedImage == nullptr || !reattachedCachedImage->isValid(canvas.recordingContext())) {
415 #else
416     if (reattachedCachedImage == nullptr || !reattachedCachedImage->isValid(canvas.getGrContext())) {
417 #endif
418         ROSEN_LOGE("RSFilterCacheManager::ReattachCachedImage failed to create SkImage from backend texture.");
419         InvalidateCache();
420         RS_OPTIONAL_TRACE_FUNC_END();
421         return;
422     }
423     cachedImage_ = reattachedCachedImage;
424 #else
425     InvalidateCache();
426 #endif
427     RS_OPTIONAL_TRACE_FUNC_END();
428 }
429 } // namespace Rosen
430 } // namespace OHOS
431 #endif
432