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 "pipeline/rs_ui_capture_task_parallel.h"
17 
18 #include <memory>
19 #include <sys/mman.h>
20 
21 #include "draw/surface.h"
22 #include "draw/color.h"
23 #include "rs_trace.h"
24 
25 #include "common/rs_background_thread.h"
26 #include "common/rs_obj_abs_geometry.h"
27 #include "memory/rs_tag_tracker.h"
28 #include "params/rs_surface_render_params.h"
29 #include "pipeline/rs_base_render_node.h"
30 #include "pipeline/rs_display_render_node.h"
31 #include "pipeline/rs_main_thread.h"
32 #include "pipeline/rs_paint_filter_canvas.h"
33 #include "pipeline/rs_render_service_connection.h"
34 #include "pipeline/rs_surface_render_node.h"
35 #include "pipeline/rs_surface_capture_task_parallel.h"
36 #include "pipeline/rs_uifirst_manager.h"
37 #include "pipeline/rs_uni_render_judgement.h"
38 #include "pipeline/rs_uni_render_util.h"
39 #include "platform/common/rs_log.h"
40 #include "platform/drawing/rs_surface.h"
41 #include "render/rs_drawing_filter.h"
42 #include "render/rs_skia_filter.h"
43 #include "screen_manager/rs_screen_manager.h"
44 #include "screen_manager/rs_screen_mode_info.h"
45 #include "drawable/rs_canvas_render_node_drawable.h"
46 #include "pipeline/rs_canvas_render_node.h"
47 
48 #ifdef RS_ENABLE_VK
49 #include "platform/ohos/backend/native_buffer_utils.h"
50 #endif
51 
52 namespace OHOS {
53 namespace Rosen {
54 
DrawCapturedImg(Drawing::Image & image,Drawing::Surface & surface,const Drawing::BackendTexture & backendTexture,Drawing::TextureOrigin & textureOrigin,Drawing::BitmapFormat & bitmapFormat)55 static inline void DrawCapturedImg(Drawing::Image& image,
56     Drawing::Surface& surface, const Drawing::BackendTexture& backendTexture,
57     Drawing::TextureOrigin& textureOrigin, Drawing::BitmapFormat& bitmapFormat)
58 {
59     RSPaintFilterCanvas canvas(&surface);
60     auto gpuContext = canvas.GetGPUContext();
61     if (gpuContext == nullptr) {
62         RS_LOGE("DrawCapturedImg failed: gpuContext is nullptr");
63         return;
64     }
65     image.BuildFromTexture(*gpuContext, backendTexture.GetTextureInfo(),
66         textureOrigin, bitmapFormat, nullptr);
67     canvas.DrawImage(image, 0.f, 0.f, Drawing::SamplingOptions());
68     surface.FlushAndSubmit(true);
69 }
70 
Capture(NodeId id,sptr<RSISurfaceCaptureCallback> callback,const RSSurfaceCaptureConfig & captureConfig)71 void RSUiCaptureTaskParallel::Capture(NodeId id, sptr<RSISurfaceCaptureCallback> callback,
72     const RSSurfaceCaptureConfig& captureConfig)
73 {
74     if (callback == nullptr) {
75         RS_LOGE("RSUiCaptureTaskParallel::Capture nodeId:[%{public}" PRIu64 "], callback is nullptr", id);
76         return;
77     }
78     RS_LOGI("RSUiCaptureTaskParallel::Capture nodeId:[%{public}" PRIu64 "]", id);
79     captureCount_++;
80     std::shared_ptr<RSUiCaptureTaskParallel> captureHandle =
81         std::make_shared<RSUiCaptureTaskParallel>(id, captureConfig);
82     if (captureHandle == nullptr) {
83         RS_LOGE("RSUiCaptureTaskParallel::Capture captureHandle is nullptr!");
84         ProcessUiCaptureCallback(callback, id, nullptr);
85         return;
86     }
87     if (!captureHandle->CreateResources()) {
88         RS_LOGE("RSUiCaptureTaskParallel::Capture CreateResources failed");
89         ProcessUiCaptureCallback(callback, id, nullptr);
90         return;
91     }
92 
93     std::function<void()> captureTask = [captureHandle, id, callback]() -> void {
94         RSSystemProperties::SetForceHpsBlurDisabled(true);
95         if (!captureHandle->Run(callback)) {
96             ProcessUiCaptureCallback(callback, id, nullptr);
97         }
98         RSSystemProperties::SetForceHpsBlurDisabled(false);
99     };
100     RSUniRenderThread::Instance().PostTask(captureTask);
101 }
102 
CreateResources()103 bool RSUiCaptureTaskParallel::CreateResources()
104 {
105     if (ROSEN_EQ(captureConfig_.scaleX, 0.f) || ROSEN_EQ(captureConfig_.scaleY, 0.f) ||
106         captureConfig_.scaleX < 0.f || captureConfig_.scaleY < 0.f) {
107         RS_LOGE("RSUiCaptureTaskParallel::CreateResources: SurfaceCapture scale is invalid.");
108         return false;
109     }
110     auto node = RSMainThread::Instance()->GetContext().GetNodeMap().GetRenderNode(nodeId_);
111     if (node == nullptr) {
112         RS_LOGE("RSUiCaptureTaskParallel::CreateResources: Invalid nodeId:[%{public}" PRIu64 "]",
113             nodeId_);
114         return false;
115     }
116 
117     if (node->GetType() != RSRenderNodeType::ROOT_NODE &&
118         node->GetType() != RSRenderNodeType::CANVAS_NODE &&
119         node->GetType() != RSRenderNodeType::CANVAS_DRAWING_NODE &&
120         node->GetType() != RSRenderNodeType::SURFACE_NODE) {
121         RS_LOGE("RSUiCaptureTaskParallel::CreateResources: Invalid RSRenderNodeType!");
122         return false;
123     }
124 #ifdef RS_ENABLE_VK
125     float nodeBoundsWidth = node->GetRenderProperties().GetBoundsWidth();
126     float nodeBoundsHeight = node->GetRenderProperties().GetBoundsHeight();
127     int32_t width = ceil(nodeBoundsWidth * captureConfig_.scaleX);
128     int32_t height = ceil(nodeBoundsHeight * captureConfig_.scaleY);
129     if (width * height > static_cast<int32_t>(OHOS::Rosen::NativeBufferUtils::VKIMAGE_LIMIT_SIZE)) {
130         RS_LOGE("RSUiCaptureTaskParallel::CreateResources: image is too large, width:%{public}d, height::%{public}d",
131             width, height);
132         return false;
133     }
134 #endif
135     if (auto surfaceNode = node->ReinterpretCastTo<RSSurfaceRenderNode>()) {
136         // Determine whether cache can be used
137         auto curNode = surfaceNode;
138         auto parentNode = RSBaseRenderNode::ReinterpretCast<RSSurfaceRenderNode>(surfaceNode->GetParent().lock());
139         if (parentNode && parentNode->IsLeashWindow() && parentNode->ShouldPaint() &&
140             (parentNode->GetLastFrameUifirstFlag() == MultiThreadCacheType::LEASH_WINDOW ||
141             parentNode->GetLastFrameUifirstFlag() == MultiThreadCacheType::NONFOCUS_WINDOW)) {
142             curNode = parentNode;
143         }
144 
145         nodeDrawable_ = std::static_pointer_cast<DrawableV2::RSRenderNodeDrawable>(
146             DrawableV2::RSRenderNodeDrawableAdapter::OnGenerate(curNode));
147         pixelMap_ = CreatePixelMapByNode(curNode);
148     } else if (auto canvasNode = node->ReinterpretCastTo<RSCanvasRenderNode>()) {
149         nodeDrawable_ = std::static_pointer_cast<DrawableV2::RSRenderNodeDrawable>(
150             DrawableV2::RSRenderNodeDrawableAdapter::OnGenerate(canvasNode));
151         pixelMap_ = CreatePixelMapByNode(canvasNode);
152     } else {
153         RS_LOGE("RSUiCaptureTaskParallel::CreateResources: Invalid RSRenderNode!");
154         return false;
155     }
156     if (pixelMap_ == nullptr) {
157         RS_LOGE("RSUiCaptureTaskParallel::CreateResources: pixelMap_ is nullptr!");
158         return false;
159     }
160     return true;
161 }
162 
Run(sptr<RSISurfaceCaptureCallback> callback)163 bool RSUiCaptureTaskParallel::Run(sptr<RSISurfaceCaptureCallback> callback)
164 {
165     RS_TRACE_NAME("RSUiCaptureTaskParallel::TakeSurfaceCapture");
166 
167 #if defined(RS_ENABLE_GL) || defined(RS_ENABLE_VK)
168     auto renderContext = RSUniRenderThread::Instance().GetRenderEngine()->GetRenderContext();
169     auto grContext = renderContext != nullptr ? renderContext->GetDrGPUContext() : nullptr;
170     std::string nodeName("RSUiCaptureTaskParallel");
171     RSTagTracker tagTracker(grContext, nodeId_, RSTagTracker::TAGTYPE::TAG_CAPTURE, nodeName);
172 #endif
173     auto surface = CreateSurface(pixelMap_);
174     if (surface == nullptr) {
175         RS_LOGE("RSUiCaptureTaskParallel::Run: surface is nullptr!");
176         return false;
177     }
178     if (!nodeDrawable_) {
179         RS_LOGE("RSUiCaptureTaskParallel::Run: Invalid RSRenderNodeDrawable!");
180         return false;
181     }
182 
183     RSPaintFilterCanvas canvas(surface.get());
184     canvas.Scale(captureConfig_.scaleX, captureConfig_.scaleY);
185     canvas.SetDisableFilterCache(true);
186     canvas.SetUICapture(true);
187     const auto& nodeParams = nodeDrawable_->GetRenderParams();
188     if (nodeParams) {
189         Drawing::Matrix relativeMatrix = Drawing::Matrix();
190         relativeMatrix.Set(Drawing::Matrix::Index::SCALE_X, captureConfig_.scaleX);
191         relativeMatrix.Set(Drawing::Matrix::Index::SCALE_Y, captureConfig_.scaleY);
192         Drawing::Matrix invertMatrix;
193         if (nodeParams->GetMatrix().Invert(invertMatrix)) {
194             relativeMatrix.PreConcat(invertMatrix);
195         }
196         canvas.SetMatrix(relativeMatrix);
197     } else {
198         RS_LOGD("RSUiCaptureTaskParallel::Run: RenderParams is nullptr!");
199     }
200 
201     //make sure the previous uifirst task is completed
202     if (!RSUiFirstProcessStateCheckerHelper::CheckMatchAndWaitNotify(*nodeParams, false)) {
203         RS_LOGE("RSUiCaptureTaskParallel::Run: CheckMatchAndWaitNotify fail");
204         return false;
205     }
206     RSUiFirstProcessStateCheckerHelper stateCheckerHelper(
207         nodeParams->GetFirstLevelNodeId(), nodeParams->GetUifirstRootNodeId());
208     RSUniRenderThread::SetCaptureParam(
209         CaptureParam(true, true, false, captureConfig_.scaleX, captureConfig_.scaleY));
210     nodeDrawable_->OnCapture(canvas);
211     RSUniRenderThread::ResetCaptureParam();
212 
213 #if (defined (RS_ENABLE_GL) || defined (RS_ENABLE_VK)) && (defined RS_ENABLE_EGLIMAGE)
214 #ifdef RS_ENABLE_UNI_RENDER
215     if (RSSystemProperties::GetSnapshotWithDMAEnabled()) {
216         RSUniRenderUtil::OptimizedFlushAndSubmit(surface, grContext, !RSSystemProperties::IsPcType());
217         auto copytask =
218             RSUiCaptureTaskParallel::CreateSurfaceSyncCopyTask(surface, std::move(pixelMap_), nodeId_, callback);
219         if (!copytask) {
220             RS_LOGE("RSUiCaptureTaskParallel::Run: create capture task failed!");
221             return false;
222         }
223         RSBackgroundThread::Instance().PostTask(copytask);
224         return true;
225     } else {
226         std::shared_ptr<Drawing::Image> img(surface.get()->GetImageSnapshot());
227         if (!img) {
228             RS_LOGE("RSUiCaptureTaskParallel::Run: img is nullptr");
229             return false;
230         }
231         if (!CopyDataToPixelMap(img, pixelMap_)) {
232             RS_LOGE("RSUiCaptureTaskParallel::Run: CopyDataToPixelMap failed");
233             return false;
234         }
235     }
236 #endif
237 #endif
238     // To get dump image
239     // execute "param set rosen.dumpsurfacetype.enabled 3 && setenforce 0"
240     RSBaseRenderUtil::WritePixelMapToPng(*pixelMap_);
241     RS_LOGI("RSUiCaptureTaskParallel::Capture DMADisable capture success nodeId:[%{public}" PRIu64
242             "], pixelMap width: %{public}d, height: %{public}d",
243         nodeId_, pixelMap_->GetWidth(), pixelMap_->GetHeight());
244     ProcessUiCaptureCallback(callback, nodeId_, pixelMap_.get());
245     return true;
246 }
247 
CreatePixelMapByNode(std::shared_ptr<RSRenderNode> node) const248 std::unique_ptr<Media::PixelMap> RSUiCaptureTaskParallel::CreatePixelMapByNode(
249     std::shared_ptr<RSRenderNode> node) const
250 {
251     float pixmapWidth = node->GetRenderProperties().GetBoundsWidth();
252     float pixmapHeight = node->GetRenderProperties().GetBoundsHeight();
253     Media::InitializationOptions opts;
254     opts.size.width = ceil(pixmapWidth * captureConfig_.scaleX);
255     opts.size.height = ceil(pixmapHeight * captureConfig_.scaleY);
256     RS_LOGD("RSUiCaptureTaskParallel::CreatePixelMapByNode: NodeId:[%{public}" PRIu64 "],"
257         " origin pixelmap width is [%{public}f], height is [%{public}f],"
258         " created pixelmap width is [%{public}u], height is [%{public}u],"
259         " the scale is scaleY:[%{public}f], scaleY:[%{public}f]",
260         node->GetId(), pixmapWidth, pixmapHeight, opts.size.width, opts.size.height,
261         captureConfig_.scaleX, captureConfig_.scaleY);
262     return Media::PixelMap::Create(opts);
263 }
264 
CreateSurface(const std::unique_ptr<Media::PixelMap> & pixelmap) const265 std::shared_ptr<Drawing::Surface> RSUiCaptureTaskParallel::CreateSurface(
266     const std::unique_ptr<Media::PixelMap>& pixelmap) const
267 {
268     if (pixelmap == nullptr) {
269         RS_LOGE("RSUiCaptureTaskParallel::CreateSurface: pixelmap == nullptr");
270         return nullptr;
271     }
272     auto address = const_cast<uint32_t*>(pixelmap->GetPixel32(0, 0));
273     if (address == nullptr) {
274         RS_LOGE("RSUiCaptureTaskParallel::CreateSurface: address == nullptr");
275         return nullptr;
276     }
277     Drawing::ImageInfo info = Drawing::ImageInfo{pixelmap->GetWidth(), pixelmap->GetHeight(),
278         Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_PREMUL};
279 
280 #if (defined RS_ENABLE_GL) && (defined RS_ENABLE_EGLIMAGE)
281     if (RSSystemProperties::GetGpuApiType() == GpuApiType::OPENGL) {
282         auto renderContext = RSUniRenderThread::Instance().GetRenderEngine()->GetRenderContext();
283         if (renderContext == nullptr) {
284             RS_LOGE("RSUiCaptureTaskParallel::CreateSurface: renderContext is nullptr");
285             return nullptr;
286         }
287         renderContext->SetUpGpuContext(nullptr);
288         return Drawing::Surface::MakeRenderTarget(renderContext->GetDrGPUContext(), false, info);
289     }
290 #endif
291 #ifdef RS_ENABLE_VK
292     if (RSSystemProperties::GetGpuApiType() == GpuApiType::VULKAN ||
293         RSSystemProperties::GetGpuApiType() == GpuApiType::DDGR) {
294         return Drawing::Surface::MakeRenderTarget(
295             RSUniRenderThread::Instance().GetRenderEngine()->GetSkContext().get(), false, info);
296     }
297 #endif
298 
299     return Drawing::Surface::MakeRasterDirect(info, address, pixelmap->GetRowBytes());
300 }
301 
302 #ifdef RS_ENABLE_UNI_RENDER
CreateSurfaceSyncCopyTask(std::shared_ptr<Drawing::Surface> surface,std::unique_ptr<Media::PixelMap> pixelMap,NodeId id,sptr<RSISurfaceCaptureCallback> callback,int32_t rotation,bool useDma)303 std::function<void()> RSUiCaptureTaskParallel::CreateSurfaceSyncCopyTask(
304     std::shared_ptr<Drawing::Surface> surface, std::unique_ptr<Media::PixelMap> pixelMap,
305     NodeId id, sptr<RSISurfaceCaptureCallback> callback, int32_t rotation, bool useDma)
306 {
307     Drawing::BackendTexture backendTexture = surface->GetBackendTexture();
308     if (!backendTexture.IsValid()) {
309         RS_LOGE("RSUiCaptureTaskParallel: SkiaSurface bind Image failed: BackendTexture is invalid");
310         return {};
311     }
312     auto wrapper = std::make_shared<std::tuple<std::unique_ptr<Media::PixelMap>>>();
313     std::get<0>(*wrapper) = std::move(pixelMap);
314     auto wrapperSf = std::make_shared<std::tuple<std::shared_ptr<Drawing::Surface>>>();
315     std::get<0>(*wrapperSf) = std::move(surface);
316     std::function<void()> copytask = [wrapper, callback, backendTexture, wrapperSf, id, rotation, useDma]() -> void {
317         RS_TRACE_NAME_FMT("copy and send capture useDma:%d", useDma);
318         if (!backendTexture.IsValid()) {
319             RS_LOGE("RSUiCaptureTaskParallel: Surface bind Image failed: BackendTexture is invalid");
320             ProcessUiCaptureCallback(callback, id, nullptr);
321             RSUniRenderUtil::ClearNodeCacheSurface(
322                 std::move(std::get<0>(*wrapperSf)), nullptr, UNI_MAIN_THREAD_INDEX, 0);
323             return;
324         }
325         auto pixelmap = std::move(std::get<0>(*wrapper));
326         if (pixelmap == nullptr) {
327             RS_LOGE("RSUiCaptureTaskParallel: pixelmap == nullptr");
328             ProcessUiCaptureCallback(callback, id, nullptr);
329             RSUniRenderUtil::ClearNodeCacheSurface(
330                 std::move(std::get<0>(*wrapperSf)), nullptr, UNI_MAIN_THREAD_INDEX, 0);
331             return;
332         }
333 
334         Drawing::ImageInfo info = Drawing::ImageInfo{ pixelmap->GetWidth(), pixelmap->GetHeight(),
335             Drawing::COLORTYPE_RGBA_8888, Drawing::ALPHATYPE_PREMUL };
336         Drawing::TextureOrigin textureOrigin = Drawing::TextureOrigin::BOTTOM_LEFT;
337         Drawing::BitmapFormat bitmapFormat =
338             Drawing::BitmapFormat{ Drawing::COLORTYPE_RGBA_8888, Drawing::ALPHATYPE_PREMUL };
339         std::shared_ptr<Drawing::Surface> surface;
340         auto grContext = RSBackgroundThread::Instance().GetShareGPUContext();
341         if (!grContext) {
342             RS_LOGE("RSUiCaptureTaskParallel: SharedGPUContext get failed");
343             ProcessUiCaptureCallback(callback, id, nullptr);
344             return;
345         }
346 #if defined(ROSEN_OHOS) && defined(RS_ENABLE_VK)
347         DmaMem dmaMem;
348         if (useDma && RSMainThread::Instance()->GetDeviceType() == DeviceType::PHONE &&
349             (RSSystemProperties::GetGpuApiType() == GpuApiType::VULKAN ||
350             RSSystemProperties::GetGpuApiType() == GpuApiType::DDGR)) {
351             sptr<SurfaceBuffer> surfaceBuffer = dmaMem.DmaMemAlloc(info, pixelmap);
352             surface = dmaMem.GetSurfaceFromSurfaceBuffer(surfaceBuffer, grContext);
353             if (surface == nullptr) {
354                 RS_LOGE("RSUiCaptureTaskParallel: GetSurfaceFromSurfaceBuffer fail.");
355                 ProcessUiCaptureCallback(callback, id, nullptr);
356                 RSUniRenderUtil::ClearNodeCacheSurface(
357                     std::move(std::get<0>(*wrapperSf)), nullptr, UNI_MAIN_THREAD_INDEX, 0);
358                 return;
359             }
360             auto tmpImg = std::make_shared<Drawing::Image>();
361             DrawCapturedImg(*tmpImg, *surface, backendTexture, textureOrigin, bitmapFormat);
362         } else {
363 #else
364         {
365 #endif
366             auto tmpImg = std::make_shared<Drawing::Image>();
367             tmpImg->BuildFromTexture(*grContext, backendTexture.GetTextureInfo(),
368                 textureOrigin, bitmapFormat, nullptr);
369             if (!CopyDataToPixelMap(tmpImg, pixelmap)) {
370                 RS_LOGE("RSUiCaptureTaskParallel: CopyDataToPixelMap failed");
371                 ProcessUiCaptureCallback(callback, id, nullptr);
372                 RSUniRenderUtil::ClearNodeCacheSurface(
373                     std::move(std::get<0>(*wrapperSf)), nullptr, UNI_MAIN_THREAD_INDEX, 0);
374                 return;
375             }
376         }
377         if (rotation) {
378             pixelmap->rotate(rotation);
379         }
380         // To get dump image
381         // execute "param set rosen.dumpsurfacetype.enabled 3 && setenforce 0"
382         RSBaseRenderUtil::WritePixelMapToPng(*pixelmap);
383         RS_LOGI("RSUiCaptureTaskParallel::Capture capture success nodeId:[%{public}" PRIu64
384                 "], pixelMap width: %{public}d, height: %{public}d",
385             id, pixelmap->GetWidth(), pixelmap->GetHeight());
386         ProcessUiCaptureCallback(callback, id, pixelmap.get());
387         RSBackgroundThread::Instance().CleanGrResource();
388         RSUniRenderUtil::ClearNodeCacheSurface(
389             std::move(std::get<0>(*wrapperSf)), nullptr, UNI_MAIN_THREAD_INDEX, 0);
390     };
391     return copytask;
392 }
393 #endif
394 
395 void RSUiCaptureTaskParallel::ProcessUiCaptureCallback(
396     sptr<RSISurfaceCaptureCallback> callback, NodeId id, Media::PixelMap* pixelmap)
397 {
398     callback->OnSurfaceCapture(id, pixelmap);
399     RSUiCaptureTaskParallel::captureCount_--;
400     RSMainThread::Instance()->RequestNextVSync();
401 }
402 } // namespace Rosen
403 } // namespace OHOS
404