1 /*
2  * Copyright (c) 2024 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 "drawable/rs_render_node_drawable.h"
17 
18 #include "common/rs_common_def.h"
19 #include "common/rs_optional_trace.h"
20 #include "luminance/rs_luminance_control.h"
21 #include "pipeline/rs_paint_filter_canvas.h"
22 #include "pipeline/rs_task_dispatcher.h"
23 #include "pipeline/rs_uni_render_thread.h"
24 #include "pipeline/rs_uni_render_util.h"
25 #include "platform/common/rs_log.h"
26 #include "rs_trace.h"
27 #include "system/rs_system_parameters.h"
28 
29 namespace OHOS::Rosen::DrawableV2 {
30 #ifdef RS_ENABLE_VK
31 #include "include/gpu/GrBackendSurface.h"
32 
33 #include "platform/ohos/backend/native_buffer_utils.h"
34 #include "platform/ohos/backend/rs_vulkan_context.h"
35 #endif
36 RSRenderNodeDrawable::Registrar RSRenderNodeDrawable::instance_;
37 thread_local bool RSRenderNodeDrawable::drawBlurForCache_ = false;
38 thread_local bool RSRenderNodeDrawable::isOpDropped_ = true;
39 thread_local bool RSRenderNodeDrawable::isOffScreenWithClipHole_ = false;
40 
41 namespace {
42 constexpr int32_t DRAWING_CACHE_MAX_UPDATE_TIME = 3;
43 constexpr float CACHE_FILL_ALPHA = 0.2f;
44 constexpr float CACHE_UPDATE_FILL_ALPHA = 0.8f;
45 }
RSRenderNodeDrawable(std::shared_ptr<const RSRenderNode> && node)46 RSRenderNodeDrawable::RSRenderNodeDrawable(std::shared_ptr<const RSRenderNode>&& node)
47     : RSRenderNodeDrawableAdapter(std::move(node))
48 {
49     auto task = [this] { this->RSRenderNodeDrawable::ClearCachedSurface(); };
50     RegisterClearSurfaceFunc(task);
51 }
52 
~RSRenderNodeDrawable()53 RSRenderNodeDrawable::~RSRenderNodeDrawable()
54 {
55     ClearCachedSurface();
56     ResetClearSurfaceFunc();
57 }
58 
OnGenerate(std::shared_ptr<const RSRenderNode> node)59 RSRenderNodeDrawable::Ptr RSRenderNodeDrawable::OnGenerate(std::shared_ptr<const RSRenderNode> node)
60 {
61     return new RSRenderNodeDrawable(std::move(node));
62 }
63 
Draw(Drawing::Canvas & canvas)64 void RSRenderNodeDrawable::Draw(Drawing::Canvas& canvas)
65 {
66     if (UNLIKELY(RSUniRenderThread::IsInCaptureProcess())) {
67         OnCapture(canvas);
68     } else {
69         OnDraw(canvas);
70     }
71 }
72 
73 /*
74  * This function will be called recursively many times, and the logic should be as concise as possible.
75  */
OnDraw(Drawing::Canvas & canvas)76 void RSRenderNodeDrawable::OnDraw(Drawing::Canvas& canvas)
77 {
78     RSRenderNodeDrawable::TotalProcessedNodeCountInc();
79     Drawing::Rect bounds = GetRenderParams() ? GetRenderParams()->GetFrameRect() : Drawing::Rect(0, 0, 0, 0);
80 
81     DrawAll(canvas, bounds);
82 }
83 
84 /*
85  * This function will be called recursively many times, and the logic should be as concise as possible.
86  */
OnCapture(Drawing::Canvas & canvas)87 void RSRenderNodeDrawable::OnCapture(Drawing::Canvas& canvas)
88 {
89     RSRenderNodeDrawable::OnDraw(canvas);
90 }
91 
GenerateCacheIfNeed(Drawing::Canvas & canvas,RSRenderParams & params)92 void RSRenderNodeDrawable::GenerateCacheIfNeed(Drawing::Canvas& canvas, RSRenderParams& params)
93 {
94     // check if drawing cache enabled
95     if (params.GetDrawingCacheType() != RSDrawingCacheType::DISABLED_CACHE) {
96         RS_OPTIONAL_TRACE_NAME_FMT("RSCanvasRenderNodeDrawable::OnDraw id:%llu cacheType:%d cacheChanged:%d"
97                                    " size:[%.2f, %.2f] ChildHasVisibleFilter:%d ChildHasVisibleEffect:%d"
98                                    " shadowRect:[%.2f, %.2f, %.2f, %.2f] HasFilterOrEffect:%d",
99             params.GetId(), params.GetDrawingCacheType(), params.GetDrawingCacheChanged(), params.GetCacheSize().x_,
100             params.GetCacheSize().y_, params.ChildHasVisibleFilter(), params.ChildHasVisibleEffect(),
101             params.GetShadowRect().GetLeft(), params.GetShadowRect().GetTop(), params.GetShadowRect().GetWidth(),
102             params.GetShadowRect().GetHeight(), HasFilterOrEffect());
103     }
104 
105     RS_LOGI_IF(DEBUG_NODE, "RSRenderNodeDrawable::GenerateCacheCondition drawingCacheType:%{public}d"
106         " RSFreezeFlag:%{public}d OpincGetCachedMark:%{public}d", params.GetDrawingCacheType(),
107         params.GetRSFreezeFlag(), OpincGetCachedMark());
108     if (params.GetRSFreezeFlag()) {
109         RS_OPTIONAL_TRACE_NAME_FMT("RSCanvasRenderNodeDrawable::GenerateCacheIfNeed id:%llu"
110                                    " GetRSFreezeFlag:%d hasFilter:%d",
111             params.GetId(), params.GetRSFreezeFlag(), params.ChildHasVisibleFilter());
112     }
113 
114     // check drawing cache type (disabled: clear cache)
115     if ((params.GetDrawingCacheType() == RSDrawingCacheType::DISABLED_CACHE && !OpincGetCachedMark()) &&
116         !params.GetRSFreezeFlag()) {
117         ClearCachedSurface();
118         {
119             std::lock_guard<std::mutex> lock(drawingCacheMapMutex_);
120             drawingCacheUpdateTimeMap_.erase(nodeId_);
121         }
122         return;
123     }
124 
125     {
126         std::scoped_lock<std::recursive_mutex> cacheLock(cacheMutex_);
127         if (cachedSurface_ == nullptr) {
128             // Remove node id in update time map to avoid update time exceeds DRAWING_CACHE_MAX_UPDATE_TIME
129             // (If cache disabled for node not on the tree, we clear cache in OnSync func, but we can't clear node
130             // id in drawingCacheUpdateTimeMap_ [drawable will not be visited in RT].
131             // If this node is marked node group by arkui again, we should first clear update time here, otherwise
132             // update time will accumulate.)
133             std::lock_guard<std::mutex> mapLock(drawingCacheMapMutex_);
134             drawingCacheUpdateTimeMap_.erase(nodeId_);
135         }
136     }
137     // generate(first time)/update cache(cache changed) [TARGET -> DISABLED if >= MAX UPDATE TIME]
138     int32_t updateTimes = 0;
139     bool needUpdateCache = CheckIfNeedUpdateCache(params, updateTimes);
140     if (needUpdateCache && params.GetDrawingCacheType() == RSDrawingCacheType::TARGETED_CACHE &&
141         updateTimes >= DRAWING_CACHE_MAX_UPDATE_TIME) {
142         RS_LOGD("RSRenderNodeDrawable::GenerateCacheCondition updateTimes:%{public}d needUpdateCache:%{public}d",
143             updateTimes, needUpdateCache);
144         RS_TRACE_NAME_FMT("DisableCache by update time > 3, id:%" PRIu64 "", params.GetId());
145         params.SetDrawingCacheType(RSDrawingCacheType::DISABLED_CACHE);
146         ClearCachedSurface();
147     }
148     // reset drawing cache changed false for render param if drawable is visited this frame
149     // if this drawble is skipped due to occlusion skip of app surface node, this flag should be kept for next frame
150     params.SetDrawingCacheChanged(false, true);
151     bool hasFilter = params.ChildHasVisibleFilter() || params.ChildHasVisibleEffect();
152     RS_LOGI_IF(DEBUG_NODE,
153         "RSRenderNodeDrawable::CheckCacheTAD hasFilter:%{public}d drawingCacheType:%{public}d",
154         hasFilter, params.GetDrawingCacheType());
155     if ((params.GetDrawingCacheType() == RSDrawingCacheType::DISABLED_CACHE || (!needUpdateCache && !hasFilter))
156         && !OpincGetCachedMark() && !params.GetRSFreezeFlag()) {
157         return;
158     }
159 
160     if (needUpdateCache) {
161         filterInfoVec_.clear();
162     }
163     bool isForegroundFilterCache = params.GetForegroundFilterCache() != nullptr;
164     // in case of no filter
165     if (needUpdateCache && (!hasFilter || isForegroundFilterCache || params.GetRSFreezeFlag())) {
166         RS_TRACE_NAME_FMT("UpdateCacheSurface id:%" PRIu64 ", isForegroundFilter:%d", nodeId_, isForegroundFilterCache);
167         RSRenderNodeDrawableAdapter* root = curDrawingCacheRoot_;
168         curDrawingCacheRoot_ = this;
169         hasSkipCacheLayer_ = false;
170         UpdateCacheSurface(canvas, params);
171         curDrawingCacheRoot_ = root;
172         return;
173     }
174 
175     // in case of with filter
176     auto curCanvas = static_cast<RSPaintFilterCanvas*>(&canvas);
177     if (needUpdateCache) {
178         // 1. update cache without filer/shadow/effect & clip hole
179         auto canvasType = curCanvas->GetCacheType();
180         // set canvas type as OFFSCREEN to not draw filter/shadow/filter
181         curCanvas->SetCacheType(RSPaintFilterCanvas::CacheType::OFFSCREEN);
182         bool isOffScreenWithClipHole = isOffScreenWithClipHole_;
183         isOffScreenWithClipHole_ = true;
184         RS_TRACE_NAME_FMT("UpdateCacheSurface with filter id:%" PRIu64 "", nodeId_);
185         RSRenderNodeDrawableAdapter* root = curDrawingCacheRoot_;
186         curDrawingCacheRoot_ = this;
187         hasSkipCacheLayer_ = false;
188         UpdateCacheSurface(canvas, params);
189         // if this NodeGroup contains other nodeGroup with filter, we should reset the isOffScreenWithClipHole_
190         isOffScreenWithClipHole_ = isOffScreenWithClipHole;
191         curCanvas->SetCacheType(canvasType);
192         curDrawingCacheRoot_ = root;
193     }
194 }
195 
TraverseSubTreeAndDrawFilterWithClip(Drawing::Canvas & canvas,const RSRenderParams & params)196 void RSRenderNodeDrawable::TraverseSubTreeAndDrawFilterWithClip(Drawing::Canvas& canvas, const RSRenderParams& params)
197 {
198     if (filterInfoVec_.empty()) {
199         return;
200     }
201     RSRenderNodeDrawableAdapter* root = curDrawingCacheRoot_;
202     curDrawingCacheRoot_ = this;
203     filterNodeSize_ = filterInfoVec_.size();
204     Drawing::AutoCanvasRestore arc(canvas, true);
205     bool isOpDropped = isOpDropped_;
206     isOpDropped_ = false;
207     drawBlurForCache_ = true; // may use in uifirst subthread
208     auto drawableCacheType = GetCacheType();
209     SetCacheType(DrawableCacheType::NONE);
210     RS_TRACE_NAME_FMT("DrawBlurForCache id:%" PRIu64 "", nodeId_);
211 
212     DrawBackground(canvas, params.GetBounds());
213     Drawing::Region filterRegion;
214     for (auto& item : filterInfoVec_) {
215         for (auto& rect: item.rectVec_) {
216             Drawing::Region region;
217             region.SetRect(rect);
218             filterRegion.Op(region, Drawing::RegionOp::UNION);
219         }
220     }
221     Drawing::Path filetrPath;
222     filterRegion.GetBoundaryPath(&filetrPath);
223     canvas.ClipPath(filetrPath);
224     DrawContent(canvas, params.GetFrameRect());
225     DrawChildren(canvas, params.GetBounds());
226     curDrawingCacheRoot_->SetLastDrawnFilterNodeId(0);
227 
228     SetCacheType(drawableCacheType);
229     isOpDropped_ = isOpDropped;
230     drawBlurForCache_ = false;
231     curDrawingCacheRoot_ = root;
232 }
233 
CheckCacheTypeAndDraw(Drawing::Canvas & canvas,const RSRenderParams & params,bool isInCapture)234 void RSRenderNodeDrawable::CheckCacheTypeAndDraw(
235     Drawing::Canvas& canvas, const RSRenderParams& params, bool isInCapture)
236 {
237     bool hasFilter = params.ChildHasVisibleFilter() || params.ChildHasVisibleEffect();
238     auto originalCacheType = GetCacheType();
239     // can not draw cache because skipCacheLayer in capture process, such as security layers...
240     if (GetCacheType() != DrawableCacheType::NONE && hasSkipCacheLayer_ && isInCapture) {
241         SetCacheType(DrawableCacheType::NONE);
242     }
243     if (hasFilter && params.GetDrawingCacheType() != RSDrawingCacheType::DISABLED_CACHE &&
244         params.GetForegroundFilterCache() == nullptr && GetCacheType() != DrawableCacheType::NONE) {
245         // traverse children to draw filter/shadow/effect
246         TraverseSubTreeAndDrawFilterWithClip(canvas, params);
247     }
248     // if children don't have any filter or effect, stop traversing
249     if (params.GetForegroundFilterCache() == nullptr && drawBlurForCache_ && curDrawingCacheRoot_ &&
250         curDrawingCacheRoot_->GetFilterNodeSize() == 0) {
251         RS_OPTIONAL_TRACE_NAME_FMT("CheckCacheTypeAndDraw id:%llu child without filter, skip", nodeId_);
252         return;
253     }
254     // in case of generating cache with filter in offscreen, clip hole for filter/shadow but drawing others
255     if (isOffScreenWithClipHole_) {
256         if (HasFilterOrEffect() && params.GetForegroundFilterCache() == nullptr) {
257             // clip hole for filter/shadow
258             DrawBackgroundWithoutFilterAndEffect(canvas, params);
259             DrawContent(canvas, params.GetFrameRect());
260             DrawChildren(canvas, params.GetBounds());
261             DrawForeground(canvas, params.GetBounds());
262             return;
263         }
264         CollectInfoForNodeWithoutFilter(canvas);
265     }
266     RS_LOGI_IF(DEBUG_NODE, "RSRenderNodeDrawable::CheckCacheTAD GetCacheType is %{public}hu", GetCacheType());
267     switch (GetCacheType()) {
268         case DrawableCacheType::NONE: {
269             DrawWithoutNodeGroupCache(canvas, params, originalCacheType);
270             break;
271         }
272         case DrawableCacheType::CONTENT: {
273             DrawWithNodeGroupCache(canvas, params);
274             break;
275         }
276         default:
277             break;
278     }
279 }
280 
DrawWithoutNodeGroupCache(Drawing::Canvas & canvas,const RSRenderParams & params,DrawableCacheType originalCacheType)281 void RSRenderNodeDrawable::DrawWithoutNodeGroupCache(
282     Drawing::Canvas& canvas, const RSRenderParams& params, DrawableCacheType originalCacheType)
283 {
284     if (drawBlurForCache_ && curDrawingCacheRoot_) {
285         auto& filterInfoVec = curDrawingCacheRoot_->GetfilterInfoVec();
286         auto begin = std::find_if(filterInfoVec.begin(), filterInfoVec.end(),
287             [nodeId = GetId()](const auto& item) -> bool { return item.nodeId_ == nodeId; });
288         if (begin == filterInfoVec.end()) {
289             CheckRegionAndDrawWithoutFilter(filterInfoVec, canvas, params);
290         } else {
291             CheckRegionAndDrawWithFilter(begin, filterInfoVec, canvas, params);
292         }
293     } else {
294         RSRenderNodeDrawable::OnDraw(canvas);
295     }
296     SetCacheType(originalCacheType);
297 }
298 
DrawWithNodeGroupCache(Drawing::Canvas & canvas,const RSRenderParams & params)299 void RSRenderNodeDrawable::DrawWithNodeGroupCache(Drawing::Canvas& canvas, const RSRenderParams& params)
300 {
301     RS_OPTIONAL_TRACE_NAME_FMT("DrawCachedImage id:%llu", nodeId_);
302     RS_LOGD("RSRenderNodeDrawable::CheckCacheTAD drawingCacheIncludeProperty is %{public}d",
303         params.GetDrawingCacheIncludeProperty());
304     if (hasSkipCacheLayer_ && curDrawingCacheRoot_) {
305         curDrawingCacheRoot_->SetSkipCacheLayer(true);
306     }
307     auto curCanvas = static_cast<RSPaintFilterCanvas*>(&canvas);
308     if (LIKELY(!params.GetDrawingCacheIncludeProperty())) {
309         DrawBackground(canvas, params.GetBounds());
310         DrawCachedImage(*curCanvas, params.GetCacheSize());
311         DrawForeground(canvas, params.GetBounds());
312     } else if (params.GetForegroundFilterCache() != nullptr) {
313         DrawBeforeCacheWithForegroundFilter(canvas, params.GetBounds());
314         DrawCachedImage(*curCanvas, params.GetCacheSize(), params.GetForegroundFilterCache());
315         DrawAfterCacheWithForegroundFilter(canvas, params.GetBounds());
316     } else {
317         DrawBeforeCacheWithProperty(canvas, params.GetBounds());
318         DrawCachedImage(*curCanvas, params.GetCacheSize());
319         DrawAfterCacheWithProperty(canvas, params.GetBounds());
320     }
321     UpdateCacheInfoForDfx(canvas, params.GetBounds(), params.GetId());
322 }
323 
CheckRegionAndDrawWithoutFilter(const std::vector<FilterNodeInfo> & filterInfoVec,Drawing::Canvas & canvas,const RSRenderParams & params)324 void RSRenderNodeDrawable::CheckRegionAndDrawWithoutFilter(
325     const std::vector<FilterNodeInfo>& filterInfoVec, Drawing::Canvas& canvas, const RSRenderParams& params)
326 {
327     if (!curDrawingCacheRoot_) {
328         return;
329     }
330     auto& withoutFilterMatrixMap = curDrawingCacheRoot_->GetWithoutFilterMatrixMap();
331     if (withoutFilterMatrixMap.find(GetId()) == withoutFilterMatrixMap.end()) {
332         RS_LOGE("RSRenderNodeDrawable::CheckRegionAndDrawWithoutFilter can not find matrix of cached node in "
333                 "withoutFilterMatrixMap");
334         return;
335     }
336     auto matrix = withoutFilterMatrixMap.at(GetId());
337     Drawing::Rect dst;
338     matrix.MapRect(dst, params.GetBounds());
339     Drawing::RectI dstRect(static_cast<int>(dst.GetLeft()), static_cast<int>(dst.GetTop()),
340         static_cast<int>(dst.GetLeft() + dst.GetWidth()), static_cast<int>(dst.GetTop() + dst.GetHeight()));
341     auto filterBegin = std::find_if(filterInfoVec.begin(), filterInfoVec.end(),
342         [nodeId = curDrawingCacheRoot_->GetLastDrawnFilterNodeId()](
343             const auto& item) -> bool { return item.nodeId_ == nodeId; });
344     if (filterBegin == filterInfoVec.end()) {
345         filterBegin = filterInfoVec.begin();
346     } else {
347         filterBegin++; // check isIntersect with undrawn filters
348     }
349     if (IsIntersectedWithFilter(filterBegin, filterInfoVec, dstRect)) {
350         RSRenderNodeDrawable::OnDraw(canvas);
351     }
352 }
353 
CheckRegionAndDrawWithFilter(std::vector<FilterNodeInfo>::const_iterator & begin,const std::vector<FilterNodeInfo> & filterInfoVec,Drawing::Canvas & canvas,const RSRenderParams & params)354 void RSRenderNodeDrawable::CheckRegionAndDrawWithFilter(std::vector<FilterNodeInfo>::const_iterator& begin,
355     const std::vector<FilterNodeInfo>& filterInfoVec, Drawing::Canvas& canvas, const RSRenderParams& params)
356 {
357     if (!curDrawingCacheRoot_ && begin == filterInfoVec.end()) {
358         return;
359     }
360     curDrawingCacheRoot_->SetLastDrawnFilterNodeId(GetId());
361     CheckShadowRectAndDrawBackground(canvas, params);
362     curDrawingCacheRoot_->ReduceFilterNodeSize();
363     Drawing::Rect dst;
364     auto matrix = begin->matrix_;
365     matrix.MapRect(dst, params.GetBounds());
366     Drawing::RectI dstRect(static_cast<int>(dst.GetLeft()), static_cast<int>(dst.GetTop()),
367         static_cast<int>(dst.GetLeft() + dst.GetWidth()), static_cast<int>(dst.GetTop() + dst.GetHeight()));
368     begin++; // check isIntersect with undrawn filters
369     if (IsIntersectedWithFilter(begin, filterInfoVec, dstRect)) {
370         DrawContent(canvas, params.GetFrameRect());
371         DrawChildren(canvas, params.GetBounds());
372         // DrawChildren may reduce filterNodeSize, if still have filter in other subtree of
373         // curDrawingCacheRoot_, we should draw foreground here
374         if (curDrawingCacheRoot_->GetFilterNodeSize() > 0) {
375             auto filterBegin = std::find_if(filterInfoVec.begin(), filterInfoVec.end(),
376                 [nodeId = curDrawingCacheRoot_->GetLastDrawnFilterNodeId()](
377                     const auto& item) -> bool { return item.nodeId_ == nodeId; });
378             if (filterBegin != filterInfoVec.end()) {
379                 filterBegin++; // check isIntersect with undrawn filters
380             }
381             if (IsIntersectedWithFilter(filterBegin, filterInfoVec, dstRect)) {
382                 DrawForeground(canvas, params.GetBounds());
383             }
384         }
385     }
386 }
387 
IsIntersectedWithFilter(std::vector<FilterNodeInfo>::const_iterator & begin,const std::vector<FilterNodeInfo> & filterInfoVec,Drawing::RectI & dstRect)388 bool RSRenderNodeDrawable::IsIntersectedWithFilter(std::vector<FilterNodeInfo>::const_iterator& begin,
389     const std::vector<FilterNodeInfo>& filterInfoVec, Drawing::RectI& dstRect)
390 {
391     bool isIntersected = false;
392     for (auto iter = begin; iter != filterInfoVec.end() && !isIntersected; ++iter) {
393         for (auto rect : iter->rectVec_) {
394             if (rect.Intersect(dstRect)) {
395                 isIntersected = true;
396                 break;
397             }
398         }
399     }
400     return isIntersected;
401 }
402 
UpdateCacheInfoForDfx(Drawing::Canvas & canvas,const Drawing::Rect & rect,NodeId id)403 void RSRenderNodeDrawable::UpdateCacheInfoForDfx(Drawing::Canvas& canvas, const Drawing::Rect& rect, NodeId id)
404 {
405     if (!isDrawingCacheDfxEnabled_) {
406         return;
407     }
408     Drawing::Rect dst;
409     canvas.GetTotalMatrix().MapRect(dst, rect);
410     RectI dfxRect(static_cast<int>(dst.GetLeft()), static_cast<int>(dst.GetTop()), static_cast<int>(dst.GetWidth()),
411         static_cast<int>(dst.GetHeight()));
412     int32_t updateTimes = 0;
413     {
414         std::lock_guard<std::mutex> lock(drawingCacheMapMutex_);
415         if (drawingCacheUpdateTimeMap_.count(nodeId_) > 0) {
416             updateTimes = drawingCacheUpdateTimeMap_.at(nodeId_);
417         }
418     }
419     {
420         std::lock_guard<std::mutex> lock(drawingCacheInfoMutex_);
421         drawingCacheInfos_[id] = std::make_pair(dfxRect, updateTimes);
422     }
423 }
424 
InitDfxForCacheInfo()425 void RSRenderNodeDrawable::InitDfxForCacheInfo()
426 {
427     isDrawingCacheEnabled_ = RSSystemParameters::GetDrawingCacheEnabled();
428     auto& uniParam = RSUniRenderThread::Instance().GetRSRenderThreadParams();
429     if (LIKELY(uniParam)) {
430         isDrawingCacheDfxEnabled_ = uniParam->IsDrawingCacheDfxEnabled();
431     }
432     if (isDrawingCacheDfxEnabled_) {
433         std::lock_guard<std::mutex> lock(drawingCacheInfoMutex_);
434         drawingCacheInfos_.clear();
435         cacheUpdatedNodeMap_.clear();
436     }
437 
438 #ifdef DDGR_ENABLE_FEATURE_OPINC
439     autoCacheEnable_ = RSSystemProperties::IsDdgrOpincEnable();
440     autoCacheDrawingEnable_ = RSSystemProperties::GetAutoCacheDebugEnabled() && autoCacheEnable_;
441     autoCacheRenderNodeInfos_.clear();
442     opincRootTotalCount_ = 0;
443     isOpincDropNodeExt_ = true;
444 #endif
445 }
446 
DrawDfxForCacheInfo(RSPaintFilterCanvas & canvas)447 void RSRenderNodeDrawable::DrawDfxForCacheInfo(RSPaintFilterCanvas& canvas)
448 {
449     if (isDrawingCacheEnabled_ && isDrawingCacheDfxEnabled_) {
450         std::lock_guard<std::mutex> lock(drawingCacheInfoMutex_);
451         for (const auto& [id, cacheInfo] : drawingCacheInfos_) {
452             std::string extraInfo = ", updateTimes:" + std::to_string(cacheInfo.second);
453             bool cacheUpdated = cacheUpdatedNodeMap_.count(id) > 0;
454             auto color = cacheUpdated ? Drawing::Color::COLOR_RED : Drawing::Color::COLOR_BLUE;
455             float alpha = cacheUpdated ? CACHE_UPDATE_FILL_ALPHA : CACHE_FILL_ALPHA;
456             RSUniRenderUtil::DrawRectForDfx(canvas, cacheInfo.first, color, alpha, extraInfo);
457         }
458     }
459 
460     if (autoCacheDrawingEnable_ && !isDrawingCacheDfxEnabled_) {
461         for (const auto& info : autoCacheRenderNodeInfos_) {
462             RSUniRenderUtil::DrawRectForDfx(
463                 canvas, info.first, Drawing::Color::COLOR_BLUE, 0.2f, info.second); // alpha 0.2 by default
464         }
465     }
466 }
467 
SetCacheType(DrawableCacheType cacheType)468 void RSRenderNodeDrawable::SetCacheType(DrawableCacheType cacheType)
469 {
470     cacheType_ = cacheType;
471 }
472 
GetCacheType() const473 DrawableCacheType RSRenderNodeDrawable::GetCacheType() const
474 {
475     return cacheType_;
476 }
477 
GetCachedSurface(pid_t threadId) const478 std::shared_ptr<Drawing::Surface> RSRenderNodeDrawable::GetCachedSurface(pid_t threadId) const
479 {
480     std::scoped_lock<std::recursive_mutex> lock(cacheMutex_);
481     return threadId == cacheThreadId_ ? cachedSurface_ : nullptr;
482 }
483 
InitCachedSurface(Drawing::GPUContext * gpuContext,const Vector2f & cacheSize,pid_t threadId,bool isHdrOn)484 void RSRenderNodeDrawable::InitCachedSurface(Drawing::GPUContext* gpuContext, const Vector2f& cacheSize,
485     pid_t threadId, bool isHdrOn)
486 {
487     std::scoped_lock<std::recursive_mutex> lock(cacheMutex_);
488 #if (defined(RS_ENABLE_GL) || defined(RS_ENABLE_VK)) && (defined RS_ENABLE_EGLIMAGE)
489     if (gpuContext == nullptr) {
490         return;
491     }
492     ClearCachedSurface();
493     cacheThreadId_ = threadId;
494     int32_t width = 0;
495     int32_t height = 0;
496     if (IsComputeDrawAreaSucc()) {
497         auto& unionRect = GetOpListUnionArea();
498         width = static_cast<int32_t>(unionRect.GetWidth());
499         height = static_cast<int32_t>(unionRect.GetHeight());
500     } else {
501         width = static_cast<int32_t>(cacheSize.x_);
502         height = static_cast<int32_t>(cacheSize.y_);
503     }
504 
505 #ifdef RS_ENABLE_GL
506     if (OHOS::Rosen::RSSystemProperties::GetGpuApiType() != OHOS::Rosen::GpuApiType::VULKAN &&
507         OHOS::Rosen::RSSystemProperties::GetGpuApiType() != OHOS::Rosen::GpuApiType::DDGR) {
508         Drawing::ImageInfo info = Drawing::ImageInfo::MakeN32Premul(width, height);
509         cachedSurface_ = Drawing::Surface::MakeRenderTarget(gpuContext, true, info);
510     }
511 #endif
512 #ifdef RS_ENABLE_VK
513     if (OHOS::Rosen::RSSystemProperties::GetGpuApiType() == OHOS::Rosen::GpuApiType::VULKAN ||
514         OHOS::Rosen::RSSystemProperties::GetGpuApiType() == OHOS::Rosen::GpuApiType::DDGR) {
515         auto colorType = Drawing::ColorType::COLORTYPE_RGBA_8888;
516         VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
517         if (isHdrOn) {
518             colorType = Drawing::ColorType::COLORTYPE_RGBA_F16;
519             format = VK_FORMAT_R16G16B16A16_SFLOAT;
520         }
521         cachedBackendTexture_ = RSUniRenderUtil::MakeBackendTexture(width, height, format);
522         auto vkTextureInfo = cachedBackendTexture_.GetTextureInfo().GetVKTextureInfo();
523         if (!cachedBackendTexture_.IsValid() || !vkTextureInfo) {
524             return;
525         }
526         vulkanCleanupHelper_ = new NativeBufferUtils::VulkanCleanupHelper(
527             RsVulkanContext::GetSingleton(), vkTextureInfo->vkImage, vkTextureInfo->vkAlloc.memory);
528         cachedSurface_ = Drawing::Surface::MakeFromBackendTexture(gpuContext, cachedBackendTexture_.GetTextureInfo(),
529             Drawing::TextureOrigin::BOTTOM_LEFT, 1, colorType, nullptr,
530             NativeBufferUtils::DeleteVkImage, vulkanCleanupHelper_);
531     }
532 #endif
533 #else
534     cachedSurface_ =
535         Drawing::Surface::MakeRasterN32Premul(static_cast<int32_t>(cacheSize.x_), static_cast<int32_t>(cacheSize.y_));
536 #endif
537 }
538 
NeedInitCachedSurface(const Vector2f & newSize)539 bool RSRenderNodeDrawable::NeedInitCachedSurface(const Vector2f& newSize)
540 {
541     auto width = static_cast<int32_t>(newSize.x_);
542     auto height = static_cast<int32_t>(newSize.y_);
543     if (IsComputeDrawAreaSucc()) {
544         auto& unionRect = GetOpListUnionArea();
545         width = static_cast<int32_t>(unionRect.GetWidth());
546         height = static_cast<int32_t>(unionRect.GetHeight());
547     }
548     std::scoped_lock<std::recursive_mutex> lock(cacheMutex_);
549     if (cachedSurface_ == nullptr) {
550         return true;
551     }
552     auto cacheCanvas = cachedSurface_->GetCanvas();
553     if (cacheCanvas == nullptr) {
554         return true;
555     }
556     return cacheCanvas->GetWidth() != width || cacheCanvas->GetHeight() != height;
557 }
558 
559 #if defined(RS_ENABLE_GL) || defined(RS_ENABLE_VK)
560 struct SharedTextureContext {
SharedTextureContextOHOS::Rosen::DrawableV2::SharedTextureContext561     SharedTextureContext(std::shared_ptr<Drawing::Image> sharedImage)
562         : sharedImage_(std::move(sharedImage)) {}
563 
564 private:
565     std::shared_ptr<Drawing::Image> sharedImage_;
566 };
567 
DeleteSharedTextureContext(void * context)568 static void DeleteSharedTextureContext(void* context)
569 {
570     SharedTextureContext* cleanupHelper = static_cast<SharedTextureContext*>(context);
571     if (cleanupHelper != nullptr) {
572         delete cleanupHelper;
573     }
574 }
575 #endif
576 
GetCachedImage(RSPaintFilterCanvas & canvas)577 std::shared_ptr<Drawing::Image> RSRenderNodeDrawable::GetCachedImage(RSPaintFilterCanvas& canvas)
578 {
579     std::scoped_lock<std::recursive_mutex> lock(cacheMutex_);
580     if (!cachedSurface_ || !cachedImage_) {
581         RS_LOGE("RSRenderNodeDrawable::GetCachedImage invalid cachedSurface_");
582         return nullptr;
583     }
584 
585     // do not use threadId to judge image grcontext change
586     if (cachedImage_->IsValid(canvas.GetGPUContext().get())) {
587         return cachedImage_;
588     }
589 #ifdef RS_ENABLE_GL
590     if (OHOS::Rosen::RSSystemProperties::GetGpuApiType() != OHOS::Rosen::GpuApiType::VULKAN &&
591         OHOS::Rosen::RSSystemProperties::GetGpuApiType() != OHOS::Rosen::GpuApiType::DDGR) {
592         if (canvas.GetGPUContext() == nullptr) {
593             return nullptr;
594         }
595         Drawing::TextureOrigin origin = Drawing::TextureOrigin::BOTTOM_LEFT;
596         Drawing::BitmapFormat info = Drawing::BitmapFormat{cachedImage_->GetColorType(), cachedImage_->GetAlphaType()};
597         SharedTextureContext* sharedContext = new SharedTextureContext(cachedImage_); // will move image
598         cachedImage_ = std::make_shared<Drawing::Image>();
599         bool ret = cachedImage_->BuildFromTexture(*canvas.GetGPUContext(), cachedBackendTexture_.GetTextureInfo(),
600             origin, info, nullptr, DeleteSharedTextureContext, sharedContext);
601         if (!ret) {
602             RS_LOGE("RSRenderNodeDrawable::GetCachedImage image BuildFromTexture failed");
603             return nullptr;
604         }
605     }
606 #endif
607 
608 #ifdef RS_ENABLE_VK
609     if (OHOS::Rosen::RSSystemProperties::GetGpuApiType() == OHOS::Rosen::GpuApiType::VULKAN ||
610         OHOS::Rosen::RSSystemProperties::GetGpuApiType() == OHOS::Rosen::GpuApiType::DDGR) {
611         if (vulkanCleanupHelper_ == nullptr || canvas.GetGPUContext() == nullptr) {
612             return nullptr;
613         }
614         Drawing::TextureOrigin origin = Drawing::TextureOrigin::BOTTOM_LEFT;
615         Drawing::BitmapFormat info = Drawing::BitmapFormat{cachedImage_->GetColorType(), cachedImage_->GetAlphaType()};
616         cachedImage_ = std::make_shared<Drawing::Image>();
617         bool ret = cachedImage_->BuildFromTexture(*canvas.GetGPUContext(), cachedBackendTexture_.GetTextureInfo(),
618             origin, info, nullptr, NativeBufferUtils::DeleteVkImage, vulkanCleanupHelper_->Ref());
619         if (!ret) {
620             RS_LOGE("RSRenderNodeDrawable::GetCachedImage image BuildFromTexture failed");
621             return nullptr;
622         }
623     }
624 #endif
625     return cachedImage_;
626 }
627 
SetCacheImageByCapture(std::shared_ptr<Drawing::Image> image)628 void RSRenderNodeDrawable::SetCacheImageByCapture(std::shared_ptr<Drawing::Image> image)
629 {
630     std::lock_guard<std::mutex> lock(freezeByCaptureMutex_);
631     cachedImageByCapture_ = image;
632 }
633 
GetCacheImageByCapture() const634 std::shared_ptr<Drawing::Image> RSRenderNodeDrawable::GetCacheImageByCapture() const
635 {
636     std::lock_guard<std::mutex> lock(freezeByCaptureMutex_);
637     return cachedImageByCapture_;
638 }
639 
DrawCachedImage(RSPaintFilterCanvas & canvas,const Vector2f & boundSize,const std::shared_ptr<RSFilter> & rsFilter)640 void RSRenderNodeDrawable::DrawCachedImage(RSPaintFilterCanvas& canvas, const Vector2f& boundSize,
641     const std::shared_ptr<RSFilter>& rsFilter)
642 {
643     auto cacheImage = GetCachedImage(canvas);
644     std::lock_guard<std::mutex> lock(freezeByCaptureMutex_);
645     if (cachedImageByCapture_) {
646         // node has freezed, and to draw surfaceCapture image
647         cacheImage = cachedImageByCapture_;
648     }
649     if (cacheImage == nullptr) {
650         RS_LOGE("RSRenderNodeDrawable::DrawCachedImage image null");
651         return;
652     }
653     if (RSSystemProperties::GetRecordingEnabled()) {
654         if (cacheImage->IsTextureBacked()) {
655             RS_LOGI("RSRenderNodeDrawable::DrawCachedImage convert cacheImage from texture to raster image");
656             cacheImage = cacheImage->MakeRasterImage();
657         }
658     }
659     if (cacheImage == nullptr || cacheImage->GetWidth() == 0 || cacheImage->GetHeight() == 0) {
660         RS_LOGE("RSRenderNodeDrawable::DrawCachedImage invalid cacheimage");
661         return;
662     }
663     float scaleX = boundSize.x_ / static_cast<float>(cacheImage->GetWidth());
664     float scaleY = boundSize.y_ / static_cast<float>(cacheImage->GetHeight());
665     if (IsComputeDrawAreaSucc()) {
666         auto& unionRect = GetOpListUnionArea();
667         scaleX = unionRect.GetWidth() / static_cast<float>(cacheImage->GetWidth());
668         scaleY = unionRect.GetHeight() / static_cast<float>(cacheImage->GetHeight());
669     }
670 
671     Drawing::AutoCanvasRestore arc(canvas, true);
672     canvas.Scale(scaleX, scaleY);
673     Drawing::Brush brush;
674     canvas.AttachBrush(brush);
675     auto samplingOptions = Drawing::SamplingOptions(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
676     if (IsComputeDrawAreaSucc() && DrawAutoCache(canvas, *cacheImage,
677         samplingOptions, Drawing::SrcRectConstraint::STRICT_SRC_RECT_CONSTRAINT)) {
678         canvas.DetachBrush();
679         DrawAutoCacheDfx(canvas, autoCacheRenderNodeInfos_);
680         return;
681     }
682     if (rsFilter != nullptr) {
683         RS_OPTIONAL_TRACE_NAME_FMT("RSRenderNodeDrawable::DrawCachedImage image width: %d, height: %d, %s",
684             cacheImage->GetWidth(), cacheImage->GetHeight(), rsFilter->GetDescription().c_str());
685         auto foregroundFilter = std::static_pointer_cast<RSDrawingFilterOriginal>(rsFilter);
686         foregroundFilter->DrawImageRect(canvas, cacheImage, Drawing::Rect(0, 0, cacheImage->GetWidth(),
687         cacheImage->GetHeight()), Drawing::Rect(0, 0, cacheImage->GetWidth(), cacheImage->GetHeight()));
688      } else {
689          canvas.DrawImage(*cacheImage, 0.0, 0.0, samplingOptions);
690      }
691     canvas.DetachBrush();
692 }
693 
ClearCachedSurface()694 void RSRenderNodeDrawable::ClearCachedSurface()
695 {
696     SetCacheType(DrawableCacheType::NONE);
697     std::scoped_lock<std::recursive_mutex> lock(cacheMutex_);
698     if (cachedSurface_ == nullptr) {
699         return;
700     }
701 
702     auto clearTask = [surface = cachedSurface_]() mutable { surface = nullptr; };
703     cachedSurface_ = nullptr;
704     cachedImage_ = nullptr;
705     RSTaskDispatcher::GetInstance().PostTask(cacheThreadId_.load(), clearTask);
706 
707 #ifdef RS_ENABLE_VK
708     if (OHOS::Rosen::RSSystemProperties::GetGpuApiType() == OHOS::Rosen::GpuApiType::VULKAN ||
709         OHOS::Rosen::RSSystemProperties::GetGpuApiType() == OHOS::Rosen::GpuApiType::DDGR) {
710         vulkanCleanupHelper_ = nullptr;
711     }
712 #endif
713 }
714 
CheckIfNeedUpdateCache(RSRenderParams & params,int32_t & updateTimes)715 bool RSRenderNodeDrawable::CheckIfNeedUpdateCache(RSRenderParams& params, int32_t& updateTimes)
716 {
717     {
718         std::lock_guard<std::mutex> lock(drawingCacheMapMutex_);
719         if (drawingCacheUpdateTimeMap_.count(nodeId_) > 0) {
720             updateTimes = drawingCacheUpdateTimeMap_.at(nodeId_);
721         }
722     }
723 
724     RS_OPTIONAL_TRACE_NAME_FMT("CheckUpdateCache id:%llu updateTimes:%d type:%d cacheChanged:%d size:[%.2f, %.2f]",
725         nodeId_, updateTimes, params.GetDrawingCacheType(), params.GetDrawingCacheChanged(),
726         params.GetCacheSize().x_, params.GetCacheSize().y_);
727 
728     // node freeze
729     if (params.GetRSFreezeFlag()) {
730         return updateTimes == 0;
731     }
732 
733     if ((params.GetDrawingCacheType() == RSDrawingCacheType::TARGETED_CACHE && params.NeedFilter() &&
734         params.GetDrawingCacheIncludeProperty()) || ROSEN_LE(params.GetCacheSize().x_, 0.f) ||
735         ROSEN_LE(params.GetCacheSize().y_, 0.f)) {
736         params.SetDrawingCacheType(RSDrawingCacheType::DISABLED_CACHE);
737         ClearCachedSurface();
738         return false;
739     }
740 
741     if (NeedInitCachedSurface(params.GetCacheSize())) {
742         ClearCachedSurface();
743         return true;
744     }
745 
746     if (updateTimes == 0 || params.GetDrawingCacheChanged()) {
747         return true;
748     }
749     return false;
750 }
751 
UpdateCacheSurface(Drawing::Canvas & canvas,const RSRenderParams & params)752 void RSRenderNodeDrawable::UpdateCacheSurface(Drawing::Canvas& canvas, const RSRenderParams& params)
753 {
754     auto curCanvas = static_cast<RSPaintFilterCanvas*>(&canvas);
755     pid_t threadId = gettid();
756     bool isHdrOn = RSLuminanceControl::Get().IsHdrOn(curCanvas->GetScreenId());
757     auto cacheSurface = GetCachedSurface(threadId);
758     if (cacheSurface == nullptr) {
759         RS_TRACE_NAME_FMT("InitCachedSurface size:[%.2f, %.2f]", params.GetCacheSize().x_, params.GetCacheSize().y_);
760         InitCachedSurface(curCanvas->GetGPUContext().get(), params.GetCacheSize(), threadId, isHdrOn);
761         cacheSurface = GetCachedSurface(threadId);
762         if (cacheSurface == nullptr) {
763             return;
764         }
765     }
766 
767     auto cacheCanvas = std::make_shared<RSPaintFilterCanvas>(cacheSurface.get());
768     if (!cacheCanvas) {
769         return;
770     }
771 
772     // copy current canvas properties into cacheCanvas
773     const auto& renderEngine = RSUniRenderThread::Instance().GetRenderEngine();
774     if (renderEngine) {
775         cacheCanvas->SetHighContrast(renderEngine->IsHighContrastEnabled());
776     }
777     cacheCanvas->CopyConfigurationToOffscreenCanvas(*curCanvas);
778     cacheCanvas->CopyHDRConfiguration(*curCanvas);
779     // Using filter cache in multi-thread environment may cause GPU memory leak or invalid textures
780     // [PLANNNING] disable it in sub-thread.
781 
782     // When drawing CacheSurface, all child node should be drawn.
783     // So set isOpDropped_ = false here.
784     bool isOpDropped = isOpDropped_;
785     isOpDropped_ = false;
786     cacheCanvas->Clear(Drawing::Color::COLOR_TRANSPARENT);
787 
788     OpincCanvasUnionTranslate(*cacheCanvas);
789     if (params.GetRSFreezeFlag()) {
790         cacheCanvas->SetDisableFilterCache(true);
791     }
792     // draw content + children
793     auto bounds = params.GetBounds();
794     ApplyForegroundColorIfNeed(*cacheCanvas, bounds);
795     if (LIKELY(!params.GetDrawingCacheIncludeProperty())) {
796         DrawContent(*cacheCanvas, params.GetFrameRect());
797         DrawChildren(*cacheCanvas, bounds);
798     } else if (params.GetForegroundFilterCache() != nullptr) {
799         DrawCacheWithForegroundFilter(*cacheCanvas, bounds);
800     } else {
801         DrawCacheWithProperty(*cacheCanvas, bounds);
802     }
803     ResumeOpincCanvasTranslate(*cacheCanvas);
804 
805     isOpDropped_ = isOpDropped;
806 
807     // get image & backend
808     {
809         std::scoped_lock<std::recursive_mutex> lock(cacheMutex_);
810         cachedImage_ = cacheSurface->GetImageSnapshot();
811         if (cachedImage_) {
812             SetCacheType(DrawableCacheType::CONTENT);
813         }
814     }
815 
816 #if RS_ENABLE_GL
817     // vk backend has been created when surface init.
818     if (OHOS::Rosen::RSSystemProperties::GetGpuApiType() != OHOS::Rosen::GpuApiType::VULKAN &&
819         OHOS::Rosen::RSSystemProperties::GetGpuApiType() != OHOS::Rosen::GpuApiType::DDGR) {
820         cachedBackendTexture_ = cacheSurface->GetBackendTexture();
821     }
822 #endif
823     // update cache updateTimes
824     {
825         std::lock_guard<std::mutex> lock(drawingCacheMapMutex_);
826         drawingCacheUpdateTimeMap_[nodeId_]++;
827     }
828     {
829         std::lock_guard<std::mutex> lock(drawingCacheInfoMutex_);
830         cacheUpdatedNodeMap_.emplace(params.GetId(), true);
831     }
832 }
833 
GetTotalProcessedNodeCount()834 int RSRenderNodeDrawable::GetTotalProcessedNodeCount()
835 {
836     return totalProcessedNodeCount_;
837 }
838 
TotalProcessedNodeCountInc()839 void RSRenderNodeDrawable::TotalProcessedNodeCountInc()
840 {
841     ++totalProcessedNodeCount_;
842 }
843 
ClearTotalProcessedNodeCount()844 void RSRenderNodeDrawable::ClearTotalProcessedNodeCount()
845 {
846     totalProcessedNodeCount_ = 0;
847 }
848 
GetProcessedNodeCount()849 int RSRenderNodeDrawable::GetProcessedNodeCount()
850 {
851     return processedNodeCount_;
852 }
853 
ProcessedNodeCountInc()854 void RSRenderNodeDrawable::ProcessedNodeCountInc()
855 {
856     ++processedNodeCount_;
857 }
858 
ClearProcessedNodeCount()859 void RSRenderNodeDrawable::ClearProcessedNodeCount()
860 {
861     processedNodeCount_ = 0;
862 }
863 } // namespace OHOS::Rosen::DrawableV2
864