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