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