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 #include <sstream>
22
23 #include "common/rs_common_def.h"
24 #include "draw/canvas.h"
25 #include "pipeline/rs_recording_canvas.h"
26 #include "platform/common/rs_system_properties.h"
27 #include "transaction/rs_marshalling_helper.h"
28
29 #include "rs_profiler_network.h"
30 #include "rs_profiler_packet.h"
31 #include "rs_profiler_settings.h"
32 #include "rs_profiler_utils.h"
33
34 namespace OHOS::Rosen {
35
36 bool RSCaptureRecorder::testingTriggering_ = false;
37
38 static const Int32Parameter MSKP_COUNTER("mskp.counter");
39 static const Int32Parameter MSKP_MAX("mskp.max");
40 static const StringParameter MSKP_PATH("mskp.path");
41 static const BoolParameter IS_MSKP("mskp.isMskp");
42
43 // 0 - default capturing from canvas in DisplayNode::OnDraw (which is used for imgCache via snapshot())
44 // 1 - capturing from mirroredDrawable::OnCapture() call in DisplayNode::DrawMirror + security/black screen
45 // 2 - capturing offscreen rendering in DisplayNode::OnDraw for extended screen
46 // 3 - capturing the actual drawing of imgCache as texture in DisplayNode::DrawMirrorCopy
47 static Uint32Parameter CAPTURE_TYPE("capture.type");
48
PrintMessage(std::string message,bool forcedSend)49 bool RSCaptureRecorder::PrintMessage(std::string message, bool forcedSend)
50 {
51 if (RSSystemProperties::GetInstantRecording() || forcedSend) {
52 Network::SendMessage(message);
53 return true;
54 }
55 return false;
56 }
57
RSCaptureRecorder()58 RSCaptureRecorder::RSCaptureRecorder()
59 {
60 profilerEnabled_ = RSSystemProperties::GetProfilerEnabled();
61 InvalidateDrawingCanvasNodeId();
62 }
63
64 RSCaptureRecorder::~RSCaptureRecorder() = default;
65
SetProfilerEnabled(bool val)66 void RSCaptureRecorder::SetProfilerEnabled(bool val)
67 {
68 profilerEnabled_ = val;
69 }
70
IsRecordingEnabled()71 bool RSCaptureRecorder::IsRecordingEnabled()
72 {
73 return (profilerEnabled_ && RSSystemProperties::GetInstantRecording()) || testingTriggering_;
74 }
75
InvalidateDrawingCanvasNodeId()76 void RSCaptureRecorder::InvalidateDrawingCanvasNodeId()
77 {
78 SetDrawingCanvasNodeId(INVALID_NODEID);
79 }
80
SetDrawingCanvasNodeId(uint64_t nodeId)81 void RSCaptureRecorder::SetDrawingCanvasNodeId(uint64_t nodeId)
82 {
83 drawingCanvasNodeId_ = nodeId;
84 }
85
SetComponentScreenshotFlag(bool flag)86 void RSCaptureRecorder::SetComponentScreenshotFlag(bool flag)
87 {
88 isComponentScreenshot_ = flag;
89 }
90
SetCaptureType(SkpCaptureType type)91 void RSCaptureRecorder::SetCaptureType(SkpCaptureType type)
92 {
93 CAPTURE_TYPE = static_cast<uint32_t>(type);
94 }
95
SetCaptureTypeClear(bool flag)96 void RSCaptureRecorder::SetCaptureTypeClear(bool flag)
97 {
98 captureTypeToClear_ = flag;
99 }
100
TryInstantCapture(float width,float height,SkpCaptureType type)101 Drawing::Canvas* RSCaptureRecorder::TryInstantCapture(float width, float height, SkpCaptureType type)
102 {
103 if (!IsRecordingEnabled()) {
104 return nullptr;
105 }
106 if (RSSystemProperties::GetSaveRDC()) {
107 // for saving .drawing file
108 recordingTriggeredFullFrame_ = true;
109 return TryInstantCaptureDrawing(width, height);
110 }
111 // for saving .mskp file
112 mskpMaxLocal_ = *MSKP_MAX;
113 mskpIdxNext_ = *MSKP_COUNTER;
114
115 if (*IS_MSKP) {
116 // record next frame, triggered by profiler step
117 if ((mskpIdxCurrent_ != mskpIdxNext_) && (mskpMaxLocal_ > 0)) {
118 recordingTriggeredFullFrame_ = true;
119 return TryCaptureMSKP(width, height);
120 }
121 return nullptr;
122 }
123 if (isMskpActive_) {
124 return nullptr;
125 }
126
127 if (static_cast<uint32_t>(type) != CAPTURE_TYPE.Get()) {
128 return nullptr;
129 }
130
131 // for saving .skp file
132 recordingTriggeredFullFrame_ = true;
133 return TryInstantCaptureSKP(width, height);
134 }
135
EndInstantCapture(SkpCaptureType type)136 void RSCaptureRecorder::EndInstantCapture(SkpCaptureType type)
137 {
138 if (!(IsRecordingEnabled() && recordingTriggeredFullFrame_) && !isMskpActive_) {
139 return;
140 }
141 recordingTriggeredFullFrame_ = false;
142 if (RSSystemProperties::GetSaveRDC()) {
143 // for saving .drawing file
144 EndInstantCaptureDrawing();
145 return;
146 }
147 if (((mskpMaxLocal_ > 0) && isPageActive_) || isMskpActive_) {
148 recordingTriggeredFullFrame_ = false;
149 return EndCaptureMSKP();
150 }
151
152 if (static_cast<uint32_t>(type) != CAPTURE_TYPE.Get()) {
153 return;
154 }
155
156 // for saving .skp file
157 recordingTriggeredFullFrame_ = false;
158 Network::SendMessage("Finishing .skp capturing");
159 EndInstantCaptureSKP();
160 }
161
TryDrawingCanvasCapture(float width,float height,uint64_t nodeId)162 Drawing::Canvas* RSCaptureRecorder::TryDrawingCanvasCapture(float width, float height, uint64_t nodeId)
163 {
164 if (drawingCanvasNodeId_ != nodeId) {
165 return nullptr;
166 }
167
168 recordingTriggeredDrawingCanvas_ = true;
169 return TryInstantCaptureSKP(width, height);
170 }
171
EndDrawingCanvasCapture()172 void RSCaptureRecorder::EndDrawingCanvasCapture()
173 {
174 if (!recordingTriggeredDrawingCanvas_) {
175 return;
176 }
177 recordingTriggeredDrawingCanvas_ = false;
178 EndInstantCaptureSKP();
179 }
180
TryOffscreenCanvasCapture(float width,float height)181 Drawing::Canvas* RSCaptureRecorder::TryOffscreenCanvasCapture(float width, float height)
182 {
183 recordingTriggeredOffscreenCanvas_ = true;
184 return TryInstantCaptureSKP(width, height);
185 }
186
EndOffscreenCanvasCapture()187 void RSCaptureRecorder::EndOffscreenCanvasCapture()
188 {
189 if (!recordingTriggeredOffscreenCanvas_) {
190 return;
191 }
192 recordingTriggeredOffscreenCanvas_ = false;
193 EndInstantCaptureSKP();
194 }
195
TryComponentScreenshotCapture(float width,float height)196 Drawing::Canvas* RSCaptureRecorder::TryComponentScreenshotCapture(float width, float height)
197 {
198 if (!isComponentScreenshot_) {
199 return nullptr;
200 }
201 recordingTriggeredComponentScreenshot_ = true;
202 return TryInstantCaptureSKP(width, height);
203 }
204
EndComponentScreenshotCapture()205 void RSCaptureRecorder::EndComponentScreenshotCapture()
206 {
207 if (!recordingTriggeredComponentScreenshot_) {
208 return;
209 }
210 recordingTriggeredComponentScreenshot_ = false;
211 EndInstantCaptureSKP();
212 SetComponentScreenshotFlag(false);
213 }
214
GetDirtyRect(uint32_t displayWidth,uint32_t displayHeight)215 std::pair<uint32_t, uint32_t> RSCaptureRecorder::GetDirtyRect(uint32_t displayWidth, uint32_t displayHeight)
216 {
217 if (IsRecordingEnabled()) {
218 return std::pair<uint32_t, uint32_t>(displayWidth, displayHeight);
219 }
220 return std::pair<uint32_t, uint32_t>(0, 0);
221 }
222
PullAndSendRdc()223 bool RSCaptureRecorder::PullAndSendRdc()
224 {
225 const std::string path("/data/autocaps");
226 if (!std::filesystem::exists(path)) {
227 return false;
228 }
229 std::vector<std::string> files;
230 for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(path)) {
231 const std::filesystem::path& path = entry.path();
232 if (path.extension() == ".rdc") {
233 files.emplace_back(path.generic_string());
234 }
235 }
236 const size_t filesRequired = 1;
237 if (files.size() == filesRequired) {
238 Network::SendRdcPath(files[0]);
239 return true;
240 }
241 return false;
242 }
243
InitMSKP()244 void RSCaptureRecorder::InitMSKP()
245 {
246 auto stream = std::make_unique<Drawing::FileWStream>((*MSKP_PATH).data());
247 if (!stream->IsValid()) {
248 std::cout << "Could not open " << *MSKP_PATH << " for writing." << std::endl;
249 return;
250 }
251 openMultiPicStream_ = std::move(stream);
252
253 serialContext_ = std::make_unique<Drawing::SharingSerialContext>();
254 Drawing::SerialProcs procs;
255 multiPic_ = Drawing::Document::MakeMultiPictureDocument(openMultiPicStream_.get(), &procs, serialContext_);
256 isMskpActive_ = true;
257 }
258
TryInstantCaptureDrawing(float width,float height)259 ExtendRecordingCanvas* RSCaptureRecorder::TryInstantCaptureDrawing(float width, float height)
260 {
261 recordingCanvas_ = std::make_unique<ExtendRecordingCanvas>(width, height);
262 return recordingCanvas_.get();
263 }
264
EndInstantCaptureDrawing()265 void RSCaptureRecorder::EndInstantCaptureDrawing()
266 {
267 auto drawCmdList = recordingCanvas_->GetDrawCmdList();
268
269 const size_t recordingParcelCapacity = 234 * 1000 * 1024;
270 std::shared_ptr<MessageParcel> messageParcel = std::make_shared<MessageParcel>();
271 messageParcel->SetMaxCapacity(recordingParcelCapacity);
272 RSMarshallingHelper::BeginNoSharedMem(std::this_thread::get_id());
273 bool marshallingComplete = RSMarshallingHelper::Marshalling(*messageParcel, drawCmdList);
274 RSMarshallingHelper::EndNoSharedMem();
275
276 if (!marshallingComplete) {
277 RSSystemProperties::SetInstantRecording(false);
278 return;
279 }
280
281 size_t parcelSize = messageParcel->GetDataSize();
282 uintptr_t parcelBuf = messageParcel->GetData();
283
284 // Create file and write the parcel
285 const std::string drawCmdListFilename = "/data/default.drawing";
286 FILE* f = Utils::FileOpen(drawCmdListFilename, "wbe");
287 if (f == nullptr) {
288 RSSystemProperties::SetInstantRecording(false);
289 return;
290 }
291 Utils::FileWrite(reinterpret_cast<uint8_t*>(parcelBuf), sizeof(uint8_t), parcelSize, f);
292 Utils::FileClose(f);
293
294 Network::SendDclPath(drawCmdListFilename);
295 Network::SendMessage("Saved locally");
296 RSSystemProperties::SetInstantRecording(false);
297 }
298
TryInstantCaptureSKP(float width,float height)299 Drawing::Canvas* RSCaptureRecorder::TryInstantCaptureSKP(float width, float height)
300 {
301 Network::SendMessage("Starting .skp capturing.");
302 recordingSkpCanvas_ = std::make_shared<Drawing::Canvas>(width, height);
303 recorder_ = std::make_unique<Drawing::PictureRecorder>();
304 auto canvas = recorder_->BeginRecording(width, height);
305 if (canvas == nullptr) {
306 return nullptr;
307 }
308 recordingSkpCanvas_ = canvas;
309 return recordingSkpCanvas_.get();
310 }
311
EndInstantCaptureSKP()312 void RSCaptureRecorder::EndInstantCaptureSKP()
313 {
314 if (recorder_ == nullptr) {
315 RSSystemProperties::SetInstantRecording(false);
316 return;
317 }
318 InvalidateDrawingCanvasNodeId();
319 picture_ = recorder_->FinishRecordingAsPicture();
320 Network::SendMessage("Finishing .skp capturing.");
321 if (picture_ != nullptr) {
322 if (picture_->ApproximateOpCount() > 0) {
323 Network::SendMessage("OpCount: " + std::to_string(picture_->ApproximateOpCount()));
324 Drawing::SerialProcs procs;
325 procs.SetHasTypefaceProc(true);
326 auto data = picture_->Serialize(&procs);
327 if (data && (data->GetSize() > 0)) {
328 Network::SendMessage("Sending SKP");
329 Network::SendSkp(data->GetData(), data->GetSize());
330 }
331 }
332 } else {
333 Network::SendMessage("Empty, nothing to record to .skp");
334 }
335 RSSystemProperties::SetInstantRecording(false);
336 if (captureTypeToClear_) {
337 SetCaptureType(SkpCaptureType::DEFAULT);
338 captureTypeToClear_ = false;
339 }
340 }
341
TryCaptureMSKP(float width,float height)342 Drawing::Canvas* RSCaptureRecorder::TryCaptureMSKP(float width, float height)
343 {
344 if (!isMskpActive_) {
345 Network::SendMessage("Starting .mskp capturing.");
346 SetCaptureType(SkpCaptureType::DEFAULT);
347 InitMSKP();
348 }
349 mskpIdxCurrent_ = mskpIdxNext_;
350 recordingSkpCanvas_ = std::make_shared<Drawing::Canvas>(width, height);
351 std::shared_ptr<Drawing::Canvas> canvas = multiPic_->BeginPage(width, height);
352 Network::SendMessage("Begin .mskp page: " + std::to_string(mskpIdxCurrent_));
353 if (canvas == nullptr) {
354 return nullptr;
355 }
356 recordingSkpCanvas_ = canvas;
357 isPageActive_ = true;
358 return recordingSkpCanvas_.get();
359 }
360
EndCaptureMSKP()361 void RSCaptureRecorder::EndCaptureMSKP()
362 {
363 if (isPageActive_) {
364 Network::SendMessage("Close .mskp page: " + std::to_string(mskpIdxCurrent_));
365 multiPic_->EndPage();
366 }
367 isPageActive_ = false;
368
369 if (!*IS_MSKP || (mskpIdxNext_ == -1)) {
370 Network::SendMessage("Finishing / Serializing .mskp capturing");
371 RSSystemProperties::SetInstantRecording(false);
372 // setting to default
373 mskpMaxLocal_ = 0;
374 mskpIdxCurrent_ = -1;
375 mskpIdxNext_ = -1;
376 isMskpActive_ = false;
377
378 std::thread thread([this]() mutable {
379 Drawing::FileWStream* stream = this->openMultiPicStream_.release();
380 Network::SendMessage("MSKP serialization started.");
381 this->multiPic_->Close();
382 Network::SendMessage("MSKP Serialization done.");
383 delete stream;
384 Network::SendMskpPath(*MSKP_PATH);
385 });
386 thread.detach();
387 }
388 }
389
390 } // namespace OHOS::Rosen