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