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