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