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 #include "rs_profiler_capture_recorder.h"
16 
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <filesystem>
21 
22 #include "include/core/SkDocument.h"
23 #include "include/core/SkPicture.h"
24 #include "include/core/SkPictureRecorder.h"
25 #include "include/core/SkSerialProcs.h"
26 #include "include/core/SkStream.h"
27 #include "include/utils/SkNWayCanvas.h"
28 #include "src/utils/SkMultiPictureDocument.h"
29 #include "tools/SkSharingProc.h"
30 
31 #include "common/rs_common_def.h"
32 #include "draw/canvas.h"
33 #include "drawing/engine_adapter/skia_adapter/skia_canvas.h"
34 #include "pipeline/rs_recording_canvas.h"
35 #include "platform/common/rs_log.h"
36 #include "platform/common/rs_system_properties.h"
37 #include "transaction/rs_marshalling_helper.h"
38 
39 #include "rs_profiler_network.h"
40 #include "rs_profiler_packet.h"
41 #include "rs_profiler_settings.h"
42 #include "rs_profiler_utils.h"
43 
44 namespace OHOS::Rosen {
45 
46 static const Int32Parameter MSKP_COUNTER("mskp.counter");
47 static const Int32Parameter MSKP_MAX("mskp.max");
48 static const StringParameter MSKP_PATH("mskp.path");
49 
50 RSCaptureRecorder::RSCaptureRecorder() = default;
51 RSCaptureRecorder::~RSCaptureRecorder() = default;
52 
IsRecordingEnabled()53 bool RSCaptureRecorder::IsRecordingEnabled()
54 {
55     static const bool profilerEnabled = RSSystemProperties::GetProfilerEnabled();
56     return profilerEnabled && RSSystemProperties::GetInstantRecording();
57 }
58 
TryInstantCapture(float width,float height)59 Drawing::Canvas* RSCaptureRecorder::TryInstantCapture(float width, float height)
60 {
61     if (!IsRecordingEnabled()) {
62         return nullptr;
63     }
64     if (RSSystemProperties::GetSaveRDC()) {
65         // for saving .drawing file
66         recordingTriggered_ = true;
67         return TryInstantCaptureDrawing(width, height);
68     }
69     // for saving .mskp file
70     mskpMaxLocal_ = *MSKP_MAX;
71     mskpIdxNext_ = *MSKP_COUNTER;
72 
73     if (mskpMaxLocal_ > 0) {
74         // record next frame, triggered by profiler step
75         if (mskpIdxCurrent_ != mskpIdxNext_) {
76             recordingTriggered_ = true;
77             return TryCaptureMSKP(width, height);
78         }
79         return nullptr;
80     }
81     // for saving .skp file
82     recordingTriggered_ = true;
83     return TryInstantCaptureSKP(width, height);
84 }
85 
EndInstantCapture()86 void RSCaptureRecorder::EndInstantCapture()
87 {
88     if (!(IsRecordingEnabled() && recordingTriggered_)) {
89         return;
90     }
91     recordingTriggered_ = false;
92     if (RSSystemProperties::GetSaveRDC()) {
93         // for saving .drawing file
94         EndInstantCaptureDrawing();
95         return;
96     }
97     if (mskpMaxLocal_ > 0) {
98         if (isPageActive_) {
99             recordingTriggered_ = false;
100             return EndCaptureMSKP();
101         }
102         return;
103     }
104 
105     // for saving .skp file
106     recordingTriggered_ = false;
107     EndInstantCaptureSKP();
108 }
109 
GetDirtyRect(uint32_t displayWidth,uint32_t displayHeight)110 std::pair<uint32_t, uint32_t> RSCaptureRecorder::GetDirtyRect(uint32_t displayWidth, uint32_t displayHeight)
111 {
112     if (IsRecordingEnabled()) {
113         return std::pair<uint32_t, uint32_t>(displayWidth, displayHeight);
114     }
115     return std::pair<uint32_t, uint32_t>(0, 0);
116 }
117 
PullAndSendRdc()118 bool RSCaptureRecorder::PullAndSendRdc()
119 {
120     const std::string path("/data/autocaps");
121     if (!std::filesystem::exists(path)) {
122         return false;
123     }
124     std::vector<std::string> files;
125     for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(path)) {
126         const std::filesystem::path& path = entry.path();
127         if (path.extension() == ".rdc") {
128             files.emplace_back(path.generic_string());
129         }
130     }
131     const size_t filesRequired = 1;
132     if (files.size() == filesRequired) {
133         Network::SendRdcPath(files[0]);
134         return true;
135     }
136     return false;
137 }
138 
InitMSKP()139 void RSCaptureRecorder::InitMSKP()
140 {
141     auto stream = std::make_unique<SkFILEWStream>((*MSKP_PATH).data());
142     if (!stream->isValid()) {
143         std::cout << "Could not open " << *MSKP_PATH << " for writing." << std::endl;
144         return;
145     }
146     openMultiPicStream_ = std::move(stream);
147 
148     SkSerialProcs procs;
149     serialContext_ = std::make_unique<SkSharingSerialContext>();
150     procs.fImageProc = SkSharingSerialContext::serializeImage;
151     procs.fImageCtx = serialContext_.get();
152     procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
153         return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
154     };
155     multiPic_ = SkMakeMultiPictureDocument(
156         openMultiPicStream_.get(), &procs, [sharingCtx = serialContext_.get()](const SkPicture* pic) {
157             SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
158         });
159 }
160 
TryInstantCaptureDrawing(float width,float height)161 ExtendRecordingCanvas* RSCaptureRecorder::TryInstantCaptureDrawing(float width, float height)
162 {
163     recordingCanvas_ = std::make_unique<ExtendRecordingCanvas>(width, height);
164     return recordingCanvas_.get();
165 }
166 
EndInstantCaptureDrawing()167 void RSCaptureRecorder::EndInstantCaptureDrawing()
168 {
169     auto drawCmdList = recordingCanvas_->GetDrawCmdList();
170 
171     const size_t recordingParcelCapacity = 234 * 1000 * 1024;
172     std::shared_ptr<MessageParcel> messageParcel = std::make_shared<MessageParcel>();
173     messageParcel->SetMaxCapacity(recordingParcelCapacity);
174     RSMarshallingHelper::BeginNoSharedMem(std::this_thread::get_id());
175     bool marshallingComplete = RSMarshallingHelper::Marshalling(*messageParcel, drawCmdList);
176     RSMarshallingHelper::EndNoSharedMem();
177 
178     if (!marshallingComplete) {
179         RSSystemProperties::SetInstantRecording(false);
180         return;
181     }
182 
183     size_t parcelSize = messageParcel->GetDataSize();
184     uintptr_t parcelBuf = messageParcel->GetData();
185 
186     // Create file and write the parcel
187     const std::string drawCmdListFilename = "/data/default.drawing";
188     FILE* f = Utils::FileOpen(drawCmdListFilename, "wbe");
189     if (f == nullptr) {
190         RSSystemProperties::SetInstantRecording(false);
191         return;
192     }
193     Utils::FileWrite(reinterpret_cast<uint8_t*>(parcelBuf), sizeof(uint8_t), parcelSize, f);
194     Utils::FileClose(f);
195 
196     Network::SendDclPath(drawCmdListFilename);
197     Network::SendMessage("Saved locally");
198     RSSystemProperties::SetInstantRecording(false);
199 }
200 
TryInstantCaptureSKP(float width,float height)201 Drawing::Canvas* RSCaptureRecorder::TryInstantCaptureSKP(float width, float height)
202 {
203     Network::SendMessage("Starting .skp capturing.");
204     recordingSkpCanvas_ = std::make_shared<Drawing::Canvas>(width, height);
205     skRecorder_ = std::make_unique<SkPictureRecorder>();
206     SkCanvas* skiaCanvas = skRecorder_->beginRecording(width, height);
207     if (skiaCanvas == nullptr) {
208         return nullptr;
209     }
210     recordingSkpCanvas_->GetImpl<Drawing::SkiaCanvas>()->ImportSkCanvas(skiaCanvas);
211     return recordingSkpCanvas_.get();
212 }
213 
EndInstantCaptureSKP()214 void RSCaptureRecorder::EndInstantCaptureSKP()
215 {
216     Network::SendMessage("Finishing .skp capturing");
217     if (skRecorder_ == nullptr) {
218         RSSystemProperties::SetInstantRecording(false);
219         return;
220     }
221     picture_ = skRecorder_->finishRecordingAsPicture();
222     if (picture_ != nullptr) {
223         if (picture_->approximateOpCount() > 0) {
224             Network::SendMessage("OpCount: " + std::to_string(picture_->approximateOpCount()));
225             SkSerialProcs procs;
226             procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
227                 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
228             };
229             auto data = picture_->serialize(&procs);
230             if (data && (data->size() > 0)) {
231                 Network::SendSkp(data->data(), data->size());
232             }
233         }
234     }
235     RSSystemProperties::SetInstantRecording(false);
236 }
237 
TryCaptureMSKP(float width,float height)238 Drawing::Canvas* RSCaptureRecorder::TryCaptureMSKP(float width, float height)
239 {
240     if (mskpIdxNext_ == 0) {
241         Network::SendMessage("Starting .mskp capturing.");
242         InitMSKP();
243     }
244     mskpIdxCurrent_ = mskpIdxNext_;
245     recordingSkpCanvas_ = std::make_shared<Drawing::Canvas>(width, height);
246     SkCanvas* skiaCanvas = multiPic_->beginPage(width, height);
247     Network::SendMessage("Begin .mskp page: " + std::to_string(mskpIdxCurrent_));
248     if (skiaCanvas == nullptr) {
249         return nullptr;
250     }
251     recordingSkpCanvas_->GetImpl<Drawing::SkiaCanvas>()->ImportSkCanvas(skiaCanvas);
252     isPageActive_ = true;
253     return recordingSkpCanvas_.get();
254 }
255 
EndCaptureMSKP()256 void RSCaptureRecorder::EndCaptureMSKP()
257 {
258     Network::SendMessage("Close .mskp page: " + std::to_string(mskpIdxCurrent_));
259     multiPic_->endPage();
260     isPageActive_ = false;
261 
262     if (mskpIdxCurrent_ == mskpMaxLocal_) {
263         Network::SendMessage("Finishing / Serializing .mskp capturing");
264         RSSystemProperties::SetInstantRecording(false);
265         // setting to default
266         mskpMaxLocal_ = 0;
267         mskpIdxCurrent_ = -1;
268         mskpIdxNext_ = -1;
269 
270         std::thread thread([this]() mutable {
271             SkFILEWStream* stream = this->openMultiPicStream_.release();
272             Network::SendMessage("MSKP serialization started.");
273             this->multiPic_->close();
274             Network::SendMessage("MSKP Serialization done.");
275             delete stream;
276             Network::SendMskpPath(*MSKP_PATH);
277         });
278         thread.detach();
279     }
280 }
281 
282 } // namespace OHOS::Rosen