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