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