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