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