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(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_drawing_filter.h"
28 #include "render/rs_magnifier_shader_filter.h"
29 #include "render/rs_skia_filter.h"
30 #include "drawable/rs_property_drawable_utils.h"
31 
32 namespace OHOS {
33 namespace Rosen {
GetCacheState() const34 const char* RSFilterCacheManager::GetCacheState() const
35 {
36     if (cachedFilteredSnapshot_ != nullptr) {
37         return "Filtered image found in cache. Reusing cached result.";
38     } else if (cachedSnapshot_ != nullptr) {
39         return "Snapshot found in cache. Generating filtered image using cached data.";
40     } else {
41         return "No valid cache found.";
42     }
43 }
44 
UpdateCacheStateWithFilterHash(const std::shared_ptr<RSFilter> & filter)45 void RSFilterCacheManager::UpdateCacheStateWithFilterHash(const std::shared_ptr<RSFilter>& filter)
46 {
47     RS_OPTIONAL_TRACE_FUNC();
48     auto filterHash = filter->Hash();
49     if (cachedFilteredSnapshot_ == nullptr || cachedFilterHash_ == filterHash) {
50         return;
51     }
52     // filter changed, clear the filtered snapshot.
53     ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithFilterHash Cache expired. Reason: Cached filtered hash "
54                "%{public}X does not match new hash %{public}X.",
55         cachedFilterHash_, filterHash);
56     cachedFilteredSnapshot_.reset();
57 }
58 
UpdateCacheStateWithFilterRegion()59 void RSFilterCacheManager::UpdateCacheStateWithFilterRegion()
60 {
61     if (!IsCacheValid()) {
62         return;
63     }
64     RS_OPTIONAL_TRACE_FUNC();
65 
66     ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithFilterRegion Cache expired. Reason: Filter region is not "
67                "within the cached region.");
68     InvalidateFilterCache();
69 }
70 
UpdateCacheStateWithDirtyRegion()71 void RSFilterCacheManager::UpdateCacheStateWithDirtyRegion()
72 {
73     if (!IsCacheValid()) {
74         return;
75     }
76     RS_OPTIONAL_TRACE_FUNC();
77     if (cacheUpdateInterval_ > 0) {
78         ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithDirtyRegion Delaying cache "
79                     "invalidation for %{public}d frames.",
80             cacheUpdateInterval_);
81         pendingPurge_ = true;
82     } else {
83         InvalidateFilterCache();
84     }
85 }
86 
UpdateCacheStateWithDirtyRegion(const RSDirtyRegionManager & dirtyManager)87 bool RSFilterCacheManager::UpdateCacheStateWithDirtyRegion(const RSDirtyRegionManager& dirtyManager)
88 {
89     if (!IsCacheValid()) {
90         return false;
91     }
92     RS_OPTIONAL_TRACE_FUNC();
93     auto& cachedImageRect = GetCachedImageRegion();
94     if (dirtyManager.currentFrameDirtyRegion_.Intersect(cachedImageRect) ||
95         std::any_of(dirtyManager.visitedDirtyRegions_.begin(), dirtyManager.visitedDirtyRegions_.end(),
96             [&cachedImageRect](const RectI& rect) { return rect.Intersect(cachedImageRect); })) {
97         // The underlying image is affected by the dirty region, determine if the cache should be invalidated by cache
98         // age. [PLANNING]: also take into account the filter radius / cache size / percentage of intersected area.
99         if (cacheUpdateInterval_ > 0) {
100             ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithDirtyRegion Delaying cache "
101                        "invalidation for %{public}d frames.",
102                 cacheUpdateInterval_);
103             pendingPurge_ = true;
104         } else {
105             InvalidateFilterCache();
106         }
107         return false;
108     }
109     if (pendingPurge_) {
110         ROSEN_LOGD("RSFilterCacheManager::UpdateCacheStateWithDirtyRegion MergeDirtyRect at %{public}d frames",
111             cacheUpdateInterval_);
112         InvalidateFilterCache();
113         return true;
114     } else {
115         return false;
116     }
117 }
118 
DrawFilterWithoutSnapshot(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSDrawingFilter> & filter,const Drawing::RectI & src,const Drawing::RectI & dst,bool shouldClearFilteredCache)119 bool RSFilterCacheManager::DrawFilterWithoutSnapshot(RSPaintFilterCanvas& canvas,
120     const std::shared_ptr<RSDrawingFilter>& filter, const Drawing::RectI& src, const Drawing::RectI& dst,
121     bool shouldClearFilteredCache)
122 {
123     if (!RSSystemProperties::GetDrawFilterWithoutSnapshotEnabled() || !shouldClearFilteredCache ||
124         cachedSnapshot_ == nullptr || cachedSnapshot_->cachedImage_ == nullptr) {
125         return false;
126     }
127     RS_OPTIONAL_TRACE_FUNC();
128 
129     /* Reuse code from RSPropertiesPainter::DrawFilter() when cache manager is not available */
130     auto clipIBounds = src;
131     Drawing::AutoCanvasRestore acr(canvas, true);
132     canvas.ResetMatrix();
133     // Only draw within the visible rect.
134     ClipVisibleRect(canvas);
135     Drawing::Rect srcRect = Drawing::Rect(0, 0, cachedSnapshot_->cachedImage_->GetWidth(),
136             cachedSnapshot_->cachedImage_->GetHeight());
137     Drawing::Rect dstRect = clipIBounds;
138     filter->DrawImageRect(canvas, cachedSnapshot_->cachedImage_, srcRect, dstRect);
139     filter->PostProcess(canvas);
140     cachedFilterHash_ = filter->Hash();
141     return true;
142 }
143 
DrawFilter(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSDrawingFilter> & filter,const DrawFilterParams params,const std::optional<Drawing::RectI> & srcRect,const std::optional<Drawing::RectI> & dstRect)144 void RSFilterCacheManager::DrawFilter(RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter,
145     const DrawFilterParams params, const std::optional<Drawing::RectI>& srcRect,
146     const std::optional<Drawing::RectI>& dstRect)
147 {
148     RS_OPTIONAL_TRACE_FUNC();
149     if (canvas.GetDeviceClipBounds().IsEmpty()) {
150         return;
151     }
152     const auto& [src, dst] = ValidateParams(canvas, srcRect, dstRect);
153     if (src.IsEmpty() || dst.IsEmpty()) {
154         return;
155     }
156     RS_TRACE_NAME_FMT("RSFilterCacheManager::DrawFilter status: %s", GetCacheState());
157     if (!IsCacheValid()) {
158         TakeSnapshot(canvas, filter, src);
159     }
160 
161     if (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) {
162         if (DrawFilterWithoutSnapshot(canvas, filter, src, dst, params.shouldClearFilteredCache)) {
163             return;
164         } else {
165             GenerateFilteredSnapshot(canvas, filter, dst);
166         }
167     }
168     DrawCachedFilteredSnapshot(canvas, dst);
169 }
170 
GeneratedCachedEffectData(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSDrawingFilter> & filter,const std::optional<Drawing::RectI> & srcRect,const std::optional<Drawing::RectI> & dstRect)171 const std::shared_ptr<RSPaintFilterCanvas::CachedEffectData> RSFilterCacheManager::GeneratedCachedEffectData(
172     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter,
173     const std::optional<Drawing::RectI>& srcRect, const std::optional<Drawing::RectI>& dstRect)
174 {
175     RS_OPTIONAL_TRACE_FUNC();
176     if (canvas.GetDeviceClipBounds().IsEmpty()) {
177         return nullptr;
178     }
179     const auto& [src, dst] = ValidateParams(canvas, srcRect, dstRect);
180     if (src.IsEmpty() || dst.IsEmpty()) {
181         return nullptr;
182     }
183     RS_TRACE_NAME_FMT("RSFilterCacheManager::GeneratedCachedEffectData status: %s", GetCacheState());
184     if (!IsCacheValid()) {
185         TakeSnapshot(canvas, filter, src);
186     }
187 
188     if (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) {
189         GenerateFilteredSnapshot(canvas, filter, dst);
190     }
191     // Keep a reference to the cached image, since CompactCache may invalidate it.
192     auto cachedFilteredSnapshot = cachedFilteredSnapshot_;
193     return cachedFilteredSnapshot;
194 }
195 
TakeSnapshot(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSDrawingFilter> & filter,const Drawing::RectI & srcRect)196 void RSFilterCacheManager::TakeSnapshot(
197     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter, const Drawing::RectI& srcRect)
198 {
199     auto drawingSurface = canvas.GetSurface();
200     if (drawingSurface == nullptr) {
201         return;
202     }
203     RS_OPTIONAL_TRACE_FUNC();
204 
205     // shrink the srcRect by 1px to avoid edge artifacts.
206     Drawing::RectI snapshotIBounds = srcRect;
207 
208     std::shared_ptr<RSShaderFilter> magnifierShaderFilter = filter->GetShaderFilterWithType(RSShaderFilter::MAGNIFIER);
209     if (magnifierShaderFilter != nullptr) {
210         auto tmpFilter = std::static_pointer_cast<RSMagnifierShaderFilter>(magnifierShaderFilter);
211         snapshotIBounds.Offset(tmpFilter->GetMagnifierOffsetX(), tmpFilter->GetMagnifierOffsetY());
212     }
213 
214     // Take a screenshot.
215     auto snapshot = drawingSurface->GetImageSnapshot(snapshotIBounds, false);
216     if (snapshot == nullptr) {
217         ROSEN_LOGD("RSFilterCacheManager::TakeSnapshot failed to make an image snapshot.");
218         return;
219     }
220     if (RSSystemProperties::GetImageGpuResourceCacheEnable(snapshot->GetWidth(), snapshot->GetHeight())) {
221         ROSEN_LOGD("TakeSnapshot cache image resource(width:%{public}d, height:%{public}d).", snapshot->GetWidth(),
222             snapshot->GetHeight());
223         snapshot->HintCacheGpuResource();
224     }
225     filter->PreProcess(snapshot);
226 
227     // Update the cache state.
228     snapshotRegion_ = RectI(srcRect.GetLeft(), srcRect.GetTop(), srcRect.GetWidth(), srcRect.GetHeight());
229     cachedSnapshot_ = std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(snapshot), snapshotIBounds);
230     cachedFilterHash_ = 0;
231 }
232 
GenerateFilteredSnapshot(RSPaintFilterCanvas & canvas,const std::shared_ptr<RSDrawingFilter> & filter,const Drawing::RectI & dstRect)233 void RSFilterCacheManager::GenerateFilteredSnapshot(
234     RSPaintFilterCanvas& canvas, const std::shared_ptr<RSDrawingFilter>& filter, const Drawing::RectI& dstRect)
235 {
236     auto surface = canvas.GetSurface();
237     if (surface == nullptr || cachedSnapshot_ == nullptr || cachedSnapshot_->cachedImage_ == nullptr) {
238         return;
239     }
240     // The cache type has been validated, so filterRegion_ and cachedImage_ should be valid. There is no need to check
241     // them again.
242     RS_OPTIONAL_TRACE_FUNC();
243 
244     // planning yan for LINEAR_GRADIENT_BLUR
245 
246     // Create an offscreen canvas with the same size as the filter region.
247     auto offscreenRect = dstRect;
248     auto offscreenSurface = surface->MakeSurface(offscreenRect.GetWidth(), offscreenRect.GetHeight());
249     if (offscreenSurface == nullptr) {
250         RS_LOGD("RSFilterCacheManager::GenerateFilteredSnapshot offscreenSurface is nullptr");
251         return;
252     }
253     RSPaintFilterCanvas offscreenCanvas(offscreenSurface.get());
254 
255     // Src rect and dst rect, with origin at (0, 0).
256     auto src = Drawing::Rect(0, 0, cachedSnapshot_->cachedRect_.GetWidth(), cachedSnapshot_->cachedRect_.GetHeight());
257     auto dst = Drawing::Rect(0, 0, offscreenRect.GetWidth(), offscreenRect.GetHeight());
258 
259     // Draw the cached snapshot on the offscreen canvas, apply the filter, and post-process.
260     filter->DrawImageRect(offscreenCanvas, cachedSnapshot_->cachedImage_, src, dst);
261     filter->PostProcess(offscreenCanvas);
262 
263     // Update the cache state with the filtered snapshot.
264     auto filteredSnapshot = offscreenSurface->GetImageSnapshot();
265     if (filteredSnapshot == nullptr) {
266         ROSEN_LOGE("RSFilterCacheManager::GenerateFilteredSnapshot failed to get filteredSnapshot.");
267         return;
268     }
269     if (RSSystemProperties::GetImageGpuResourceCacheEnable(filteredSnapshot->GetWidth(),
270         filteredSnapshot->GetHeight())) {
271         ROSEN_LOGD("GenerateFilteredSnapshot cache image resource(width:%{public}d, height:%{public}d).",
272             filteredSnapshot->GetWidth(), filteredSnapshot->GetHeight());
273         filteredSnapshot->HintCacheGpuResource();
274     }
275     if (RSSystemProperties::GetRecordingEnabled()) {
276         if (filteredSnapshot->IsTextureBacked()) {
277             RS_LOGI("RSFilterCacheManager::GenerateFilteredSnapshot cachedImage from texture to raster image");
278             filteredSnapshot = filteredSnapshot->MakeRasterImage();
279         }
280     }
281     cachedFilteredSnapshot_ =
282         std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(filteredSnapshot), offscreenRect);
283 }
284 
DrawCachedFilteredSnapshot(RSPaintFilterCanvas & canvas,const Drawing::RectI & dstRect) const285 void RSFilterCacheManager::DrawCachedFilteredSnapshot(RSPaintFilterCanvas& canvas, const Drawing::RectI& dstRect) const
286 {
287     if (cachedFilteredSnapshot_ == nullptr || cachedFilteredSnapshot_->cachedImage_ == nullptr) {
288         return;
289     }
290     RS_OPTIONAL_TRACE_FUNC();
291 
292     // Draw in device coordinates.
293     Drawing::AutoCanvasRestore autoRestore(canvas, true);
294     canvas.ResetMatrix();
295 
296     // Only draw within the visible rect.
297     ClipVisibleRect(canvas);
298 
299     // The cache type and parameters has been validated, dstRect must be subset of cachedFilteredSnapshot_->cachedRect_.
300     Drawing::Rect dst = {dstRect.GetLeft(), dstRect.GetTop(), dstRect.GetRight(), dstRect.GetBottom()};
301     Drawing::Rect src = {dstRect.GetLeft(), dstRect.GetTop(), dstRect.GetRight(), dstRect.GetBottom()};
302     src.Offset(-cachedFilteredSnapshot_->cachedRect_.GetLeft(), -cachedFilteredSnapshot_->cachedRect_.GetTop());
303     RS_OPTIONAL_TRACE_NAME_FMT("DrawCachedFilteredSnapshot cachedRect_:%s, src:%s, dst:%s",
304         cachedFilteredSnapshot_->cachedRect_.ToString().c_str(), src.ToString().c_str(), dst.ToString().c_str());
305     Drawing::Brush brush;
306     brush.SetAntiAlias(true);
307     canvas.AttachBrush(brush);
308     canvas.DrawImageRect(*cachedFilteredSnapshot_->cachedImage_, src, dst, Drawing::SamplingOptions(),
309         Drawing::SrcRectConstraint::FAST_SRC_RECT_CONSTRAINT);
310     canvas.DetachBrush();
311 }
312 
InvalidateFilterCache(FilterCacheType clearType)313 void RSFilterCacheManager::InvalidateFilterCache(FilterCacheType clearType)
314 {
315     if (clearType == FilterCacheType::BOTH) {
316         cachedSnapshot_.reset();
317         cachedFilteredSnapshot_.reset();
318         RS_OPTIONAL_TRACE_NAME_FMT("RSFilterCacheManager::InvalidateFilterCache BOTH");
319         return;
320     }
321     if (clearType == FilterCacheType::SNAPSHOT) {
322         cachedSnapshot_.reset();
323         RS_OPTIONAL_TRACE_NAME_FMT("RSFilterCacheManager::InvalidateFilterCache SNAPSHOT");
324         return;
325     }
326     if (clearType == FilterCacheType::FILTERED_SNAPSHOT) {
327         RS_OPTIONAL_TRACE_NAME_FMT("RSFilterCacheManager::InvalidateFilterCache FILTERED_SNAPSHOT");
328         cachedFilteredSnapshot_.reset();
329     }
330 }
331 
GetFilterInvalid()332 bool RSFilterCacheManager::GetFilterInvalid()
333 {
334     return filterInvalid_;
335 }
336 
SetFilterInvalid(bool invalidFilter)337 void RSFilterCacheManager::SetFilterInvalid(bool invalidFilter)
338 {
339     filterInvalid_ = invalidFilter;
340 }
341 
ReleaseCacheOffTree()342 void RSFilterCacheManager::ReleaseCacheOffTree()
343 {
344     RS_OPTIONAL_TRACE_FUNC();
345     cachedSnapshot_.reset();
346     cachedFilteredSnapshot_.reset();
347 }
348 
GetCachedType() const349 FilterCacheType RSFilterCacheManager::GetCachedType() const
350 {
351     if (cachedSnapshot_ == nullptr && cachedFilteredSnapshot_ == nullptr) {
352         return FilterCacheType::NONE;
353     }
354     if (cachedSnapshot_ != nullptr) {
355         return FilterCacheType::SNAPSHOT;
356     }
357 
358     if (cachedFilteredSnapshot_ != nullptr) {
359         return FilterCacheType::FILTERED_SNAPSHOT;
360     }
361     return FilterCacheType::BOTH;
362 }
363 
ClipVisibleRect(RSPaintFilterCanvas & canvas)364 inline void RSFilterCacheManager::ClipVisibleRect(RSPaintFilterCanvas& canvas)
365 {
366     auto visibleRectF = canvas.GetVisibleRect();
367     visibleRectF.Round();
368     Drawing::RectI visibleIRect = {(int)visibleRectF.GetLeft(), (int)visibleRectF.GetTop(),
369                                    (int)visibleRectF.GetRight(), (int)visibleRectF.GetBottom()};
370     auto deviceClipRect = canvas.GetDeviceClipBounds();
371     if (!visibleIRect.IsEmpty() && deviceClipRect.Intersect(visibleIRect)) {
372         canvas.ClipIRect(visibleIRect, Drawing::ClipOp::INTERSECT);
373     }
374 }
375 
GetCachedImageRegion() const376 const RectI& RSFilterCacheManager::GetCachedImageRegion() const
377 {
378     static const auto emptyRect = RectI();
379     return IsCacheValid() ? snapshotRegion_ : emptyRect;
380 }
381 
IsCacheInvalid(const RSPaintFilterCanvas::CachedEffectData & cache,RSPaintFilterCanvas & canvas)382 inline static bool IsCacheInvalid(const RSPaintFilterCanvas::CachedEffectData& cache, RSPaintFilterCanvas& canvas)
383 {
384     return cache.cachedImage_ == nullptr || !cache.cachedImage_->IsValid(canvas.GetGPUContext().get());
385 }
386 
CheckCachedImages(RSPaintFilterCanvas & canvas)387 void RSFilterCacheManager::CheckCachedImages(RSPaintFilterCanvas& canvas)
388 {
389     if (cachedSnapshot_ != nullptr && IsCacheInvalid(*cachedSnapshot_, canvas)) {
390         ROSEN_LOGE("RSFilterCacheManager::CheckCachedImages cachedSnapshot_ is invalid");
391         cachedSnapshot_.reset();
392     }
393 
394     if (cachedFilteredSnapshot_ != nullptr && IsCacheInvalid(*cachedFilteredSnapshot_, canvas)) {
395         ROSEN_LOGE("RSFilterCacheManager::CheckCachedImages cachedFilteredSnapshot_ is invalid");
396         cachedFilteredSnapshot_.reset();
397     }
398 }
399 
ValidateParams(RSPaintFilterCanvas & canvas,const std::optional<Drawing::RectI> & srcRect,const std::optional<Drawing::RectI> & dstRect)400 std::tuple<Drawing::RectI, Drawing::RectI> RSFilterCacheManager::ValidateParams(
401     RSPaintFilterCanvas& canvas, const std::optional<Drawing::RectI>& srcRect,
402     const std::optional<Drawing::RectI>& dstRect)
403 {
404     Drawing::RectI src;
405     Drawing::RectI dst;
406     auto deviceRect = Drawing::RectI(0, 0, canvas.GetImageInfo().GetWidth(), canvas.GetImageInfo().GetHeight());
407     if (!srcRect.has_value()) {
408         src = canvas.GetRoundInDeviceClipBounds();
409     } else {
410         src = srcRect.value();
411         src.Intersect(deviceRect);
412     }
413     if (!dstRect.has_value()) {
414         dst = src;
415     } else {
416         dst = dstRect.value();
417         dst.Intersect(deviceRect);
418     }
419     RS_OPTIONAL_TRACE_NAME_FMT("RSFilterCacheManager::ValidateParams src:%s, dst:%s",
420         src.ToString().c_str(), dst.ToString().c_str());
421     return { src, dst };
422 }
423 
CompactFilterCache(bool shouldClearFilteredCache)424 void RSFilterCacheManager::CompactFilterCache(bool shouldClearFilteredCache)
425 {
426     InvalidateFilterCache(shouldClearFilteredCache ?
427         FilterCacheType::FILTERED_SNAPSHOT : FilterCacheType::SNAPSHOT);
428 }
429 } // namespace Rosen
430 } // namespace OHOS
431 #endif
432