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