1 /*
2 * Copyright (c) 2023 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 "drawing_playback.h"
16 #include <sstream>
17 #include <fcntl.h>
18 #include <thread>
19 #include <unique_fd.h>
20
21 #include "include/core/SkStream.h"
22 #include "skia_adapter/skia_canvas.h"
23 #include "src/utils/SkMultiPictureDocumentPriv.h"
24 #include "src/utils/SkOSPath.h"
25 #include "transaction/rs_marshalling_helper.h"
26 #include "recording/draw_cmd_list.h"
27 #include "platform/common/rs_system_properties.h"
28
29 #include <fstream>
30 #include <filesystem>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include "directory_ex.h"
34
35 namespace {
IsValidFile(const std::string & realPathStr,const std::string & validFile="")36 bool IsValidFile(const std::string& realPathStr, const std::string& validFile = "/data/")
37 {
38 return realPathStr.find(validFile) == 0;
39 }
40
GetRealAndValidPath(const std::string & filePath)41 std::string GetRealAndValidPath(const std::string& filePath)
42 {
43 std::string realPathStr;
44 if (!OHOS::PathToRealPath(filePath, realPathStr)) {
45 std::cout << "FileUtils: The file path is not valid!" << std::endl;
46 return "";
47 }
48 if (IsValidFile(realPathStr)) {
49 return realPathStr;
50 } else {
51 std::cout << "FileUtils: The file path is not valid!" << std::endl;
52 return "";
53 }
54 }
55
CreateFile(const std::string & filePath)56 bool CreateFile(const std::string& filePath)
57 {
58 std::string realPath = GetRealAndValidPath(filePath);
59 if (realPath.empty()) {
60 return false;
61 }
62 std::ofstream outFile(realPath);
63 if (!outFile.is_open()) {
64 std::cout << "FileUtils: file open failed!" << std::endl;
65 return false;
66 }
67 outFile.clear();
68 outFile.close();
69 return true;
70 }
71
WriteStringToFilefd(int fd,const std::string & str)72 bool WriteStringToFilefd(int fd, const std::string& str)
73 {
74 const char* p = str.data();
75 ssize_t remaining = static_cast<ssize_t>(str.size());
76 while (remaining > 0) {
77 ssize_t n = write(fd, p, remaining);
78 if (n == -1) {
79 return false;
80 }
81 p += n;
82 remaining -= n;
83 }
84 p = nullptr;
85 return true;
86 }
87
WriteStringToFile(const std::string & str,const std::string & filePath)88 bool WriteStringToFile(const std::string& str, const std::string& filePath)
89 {
90 if (!CreateFile(filePath)) {
91 return false;
92 }
93 if (filePath.empty()) {
94 return false;
95 }
96
97 char realPath[PATH_MAX] = {0};
98 if (realpath(filePath.c_str(), realPath) == nullptr) {
99 return false;
100 }
101
102 int fd = open(realPath, O_RDWR | O_CREAT, static_cast<mode_t>(0600));
103 if (fd < 0) {
104 std::cout << "FileUtils: fd open failed!" << std::endl;
105 return false;
106 }
107 bool result = WriteStringToFilefd(fd, str);
108 close(fd);
109 return result;
110 }
111 };
112
113 namespace OHOS {
114 namespace Rosen {
115
MSKPSrc(const std::string & path)116 MSKPSrc::MSKPSrc(const std::string& path) : fPath_(path)
117 {
118 std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath_.c_str());
119
120 int count = SkMultiPictureDocumentReadPageCount(stream.get());
121 if (count > 0) {
122 auto deserialContext = std::make_unique<SkSharingDeserialContext>();
123 SkDeserialProcs procs;
124 procs.fImageProc = SkSharingDeserialContext::deserializeImage;
125 procs.fImageCtx = deserialContext.get();
126
127 fPages_.reset(count);
128 SkMultiPictureDocumentRead(stream.get(), &fPages_[0], count, &procs);
129 }
130 }
131
NextFrame()132 bool MSKPSrc::NextFrame()
133 {
134 if (++curFrameNum_ == fPages_.size()) {
135 curFrameNum_ = 0;
136 sleep(1);
137 return false;
138 }
139 return true;
140 }
141
Draw(SkCanvas * c) const142 bool MSKPSrc::Draw(SkCanvas* c) const
143 {
144 SkRect cullrect = fPages_[curFrameNum_].fPicture.get()->cullRect();
145 std::cout << "cullrect.width()=" << cullrect.width() << "cullrect.width()=" << cullrect.height() << std::endl;
146 c->drawPicture(fPages_[curFrameNum_].fPicture.get());
147 return true;
148 }
149
150
DrawingDCL(int32_t argc,char * argv[])151 DrawingDCL::DrawingDCL(int32_t argc, char* argv[])
152 {
153 DCLCommand dclCommand = DCLCommand(argc, argv);
154 UpdateParametersFromDCLCommand(dclCommand);
155 skiaRecording.InitConfigsFromParam();
156 if (iterateType_ == IterateType::REPLAY_MSKP) {
157 mskpPtr = std::make_unique<MSKPSrc>("/data/OH.mskp");
158 } else if (iterateType_ == IterateType::REPLAY_SKP) {
159 SkFILEStream input("/data/OH.skp");
160 skpPtr = SkPicture::MakeFromStream(&input);
161 }
162 }
163
~DrawingDCL()164 DrawingDCL::~DrawingDCL()
165 {
166 mskpPtr.reset();
167 std::cout << "~DrawingDCL" << std::endl;
168 }
169
PrintDurationTime(const std::string & description,std::chrono::time_point<std::chrono::system_clock> start)170 void DrawingDCL::PrintDurationTime(const std::string & description,
171 std::chrono::time_point<std::chrono::system_clock> start)
172 {
173 auto end = std::chrono::system_clock::now();
174 auto duration = std::chrono::duration_cast<std::chrono::nanoseconds> (end - start);
175 const double convertToMs = 1000000.0;
176 std::cout << description << duration.count() / convertToMs << "ms" << std::endl;
177 }
178
IterateFrame(int & curLoop,int & frame)179 bool DrawingDCL::IterateFrame(int &curLoop, int &frame)
180 {
181 ++frame;
182 if (frame > endFrame_) {
183 frame = beginFrame_;
184 if (++curLoop == loop_) {
185 return false;
186 }
187 }
188 return true;
189 }
190
ReplayMSKP(SkCanvas * skiaCanvas)191 bool DrawingDCL::ReplayMSKP(SkCanvas* skiaCanvas)
192 {
193 std::string tmp;
194 std::cout << "press any key to draw one skp in OH.(begin)" << std::endl;
195 std::string timePrint = "lkx: draw time of frame " + std::to_string(mskpPtr->GetCurFrameNum()) + " is ";
196 if (loop_ == 0) {
197 std::cin>>tmp;
198 }
199 if (beginFrame_ == 1) {
200 std::cout<<"Frame: " << std::to_string(mskpPtr->GetCurFrameNum()) <<" .sleep 4s..."<< std::endl;
201 sleep(opItemStep_);
202 }
203 auto start = std::chrono::system_clock::now();
204 mskpPtr->Draw(skiaCanvas);
205 PrintDurationTime(timePrint, start);
206 if (beginFrame_ == 1) {
207 std::cout<<"Frame: " << std::to_string(mskpPtr->GetCurFrameNum()) <<" .sleep 4s again..."<< std::endl;
208 sleep(opItemStep_);
209 }
210 std::cout << "press any key to draw one skp in OH.(end)" << std::endl;
211 if (loop_ == 0) {
212 std::cin>>tmp;
213 }
214 return mskpPtr->NextFrame();
215 }
216
ReplaySKP(SkCanvas * skiaCanvas)217 void DrawingDCL::ReplaySKP(SkCanvas *skiaCanvas)
218 {
219 if (skiaCanvas == nullptr) {
220 return;
221 }
222 static int frameNum = 0;
223 std::cout << "repaly skp. the " << frameNum << "times" << std::endl;
224 std::string tmp;
225 std::cout << "press any key to draw one frame.(begin)" << std::endl;
226 std::cin >> tmp;
227 auto start = std::chrono::system_clock::now();
228 skiaCanvas->drawPicture(skpPtr);
229 std::cout << "press any key to draw one frame.(end)" << std::endl;
230 PrintDurationTime("The frame PlayBack time is ", start);
231 std::cin >> tmp;
232 }
233
PlayBackByFrame(Drawing::Canvas * canvas,bool isDumpPictures)234 bool DrawingDCL::PlayBackByFrame(Drawing::Canvas* canvas, bool isDumpPictures)
235 {
236 //read DrawCmdList from file
237 if (canvas == nullptr) {
238 return false;
239 }
240 auto start = std::chrono::system_clock::now();
241 static int curLoop = 0;
242 std::string dclFile = inputFilePath_ + "frame" + std::to_string(curFrameNo_) + ".drawing";
243 std::cout << "PlayBackByFrame dclFile:" << dclFile << std::endl;
244
245 if (LoadDrawCmdList(dclFile) < 0) {
246 std::cout << "failed to loadDrawCmdList" << std::endl;
247 IterateFrame(curLoop, curFrameNo_);
248 return false;
249 }
250 PrintDurationTime("Load DrawCmdList file time is ", start);
251 start = std::chrono::system_clock::now();
252 dcl_->Playback(*canvas);
253 PrintDurationTime("The frame PlayBack time is ", start);
254 return IterateFrame(curLoop, curFrameNo_);
255 }
256
PlayBackByOpItem(SkCanvas * skiaCanvas,bool isMoreOps)257 bool DrawingDCL::PlayBackByOpItem(SkCanvas* skiaCanvas, bool isMoreOps)
258 {
259 if (skiaCanvas == nullptr) {
260 return false;
261 }
262 auto start = std::chrono::system_clock::now();
263 // read drawCmdList from file
264 std::string dclFile = inputFilePath_ + "frameByOpItem.drawing";
265 std::cout << "PlayBackFrame dclFile:" << dclFile << std::endl;
266
267 if (LoadDrawCmdList(dclFile) < 0) {
268 std::cout << "failed to loadDrawCmdList" << std::endl;
269 return false;
270 }
271 PrintDurationTime("Load DrawCmdList file time is ", start);
272 // Playback
273 static double opitemId = 0;
274 if (!isMoreOps) {
275 opitemId -= opItemStep_;
276 if (opitemId < 0) {
277 opitemId = 0;
278 }
279 std::cout<< "This is already the first OpItem." << std::endl;
280 } else {
281 opitemId += opItemStep_;
282 }
283 std::cout << "play back to opitemId = " << static_cast<int>(opitemId) << std::endl;
284 return true;
285 }
286
GetDirectionAndStep(std::string command,bool & isMoreOps)287 bool DrawingDCL::GetDirectionAndStep(std::string command, bool &isMoreOps)
288 {
289 if (command.empty()) {
290 return true;
291 }
292 std::vector<std::string> words;
293 std::stringstream ss(command);
294 std::string word;
295 while (ss >> word) {
296 words.emplace_back(word);
297 }
298 const size_t twoParam = 2;
299 if (words.size() != twoParam) {
300 std::cout << "Wrong Parameter!" << std::endl;
301 return false;
302 }
303 if (std::strcmp(words[0].c_str(), "l") == 0 || std::strcmp(words[0].c_str(), "L") == 0) {
304 isMoreOps = false;
305 } else if (std::strcmp(words[0].c_str(), "m") == 0 || std::strcmp(words[0].c_str(), "M") == 0) {
306 isMoreOps = true;
307 } else {
308 std::cout << "Wrong Direction!" << std::endl;
309 return false;
310 }
311 // check if the input for step is valid
312 int dotPostion = -1;
313 for (size_t i = 0; i < words[1].size(); ++i) {
314 if (words[1][i] == '.' && dotPostion == -1) {
315 dotPostion = static_cast<int>(i);
316 } else if (words[1][i] >= '0' && words[1][i] <= '9') {
317 continue;
318 } else {
319 std::cout << "Please enter right step!" << std::endl;
320 return false;
321 }
322 }
323 opItemStep_ = std::stod(words[1]);
324 return true;
325 }
326
UpdateParametersFromDCLCommand(const DCLCommand & dclCommand)327 void DrawingDCL::UpdateParametersFromDCLCommand(const DCLCommand &dclCommand)
328 {
329 iterateType_ = dclCommand.iterateType_;
330 beginFrame_ = dclCommand.beginFrame_;
331 endFrame_ = dclCommand.endFrame_;
332 loop_ = dclCommand.loop_;
333 opItemStep_ = dclCommand.opItemStep_;
334 inputFilePath_ = dclCommand.inputFilePath_;
335 outputFilePath_ = dclCommand.outputFilePath_;
336 }
337
UpdateParameters(bool notNeeded)338 void DrawingDCL::UpdateParameters(bool notNeeded)
339 {
340 if (notNeeded) {
341 return;
342 }
343 std::cout << "Please re-enter the parameters" << std::endl;
344 std::string line;
345 getline(std::cin, line);
346 if (line.empty()) {
347 return;
348 }
349 DCLCommand dclCommand = DCLCommand(line);
350 UpdateParametersFromDCLCommand(dclCommand);
351 }
352
Test(Drawing::Canvas * canvas,int width,int height)353 void DrawingDCL::Test(Drawing::Canvas* canvas, int width, int height)
354 {
355 std::cout << "DrawingDCL::Test+" << std::endl;
356 #if (defined RS_ENABLE_GL) || (defined RS_ENABLE_VK)
357 std::cout << "DrawingDCL::Test skia" << std::endl;
358 orgSkiaCanvas_ = canvas->GetImpl<Drawing::SkiaCanvas>()->ExportSkCanvas();
359 if (skiaRecording.GetCaptureEnabled()) {
360 std::cout << "DrawingDCL::Test skia begin capture+" << std::endl;
361 skiaCanvas_ = skiaRecording.BeginCapture(orgSkiaCanvas_, width, height);
362 canvas->GetImpl<Drawing::SkiaCanvas>()->ImportSkCanvas(skiaCanvas_);
363 }
364 #endif
365 auto start = std::chrono::system_clock::now();
366 switch (iterateType_) {
367 case IterateType::ITERATE_FRAME:
368 UpdateParameters(PlayBackByFrame(canvas));
369 break;
370 case IterateType::ITERATE_OPITEM:
371 break;
372 case IterateType::ITERATE_OPITEM_MANUALLY: {
373 static bool isMoreOps = true;
374 std::string opActionsStr = isMoreOps ? "more" : "less";
375 std::cout << "Do you want to execute " << opItemStep_ << " OpItems " << opActionsStr << " ?\n"
376 "To Modify, enter the action (More or less) and step size, for example, \"M 2\". \n"
377 " Press Enter to continue." << std::endl;
378 std::string line;
379 do {
380 getline(std::cin, line);
381 } while (!GetDirectionAndStep(line, isMoreOps));
382 break;
383 }
384 case IterateType::REPLAY_MSKP:
385 std::cout << "replay mskp... " << std::endl;
386 #ifdef RS_ENABLE_VK
387 UpdateParameters(ReplayMSKP(skiaCanvas_) || (beginFrame_ == 1));
388 #endif
389 break;
390 case IterateType::REPLAY_SKP:
391 std::cout << "replay skp... " << std::endl;
392 #ifdef RS_ENABLE_VK
393 ReplaySKP(skiaCanvas_);
394 #endif
395 break;
396 case IterateType::OTHER:
397 std::cout << "Unknown iteratetype, please reenter parameters!" << std::endl;
398 break;
399 default:
400 std::cout << "Wrong iteratetype!" << std::endl;
401 break;
402 }
403 PrintDurationTime("This frame draw time is: ", start);
404 #if (defined RS_ENABLE_GL) || (defined RS_ENABLE_VK)
405 if (skiaRecording.GetCaptureEnabled()) {
406 skiaRecording.EndCapture();
407 }
408 canvas->GetImpl<Drawing::SkiaCanvas>()->ImportSkCanvas(orgSkiaCanvas_);
409 #endif
410 std::cout << "DrawingDCL::Test-" << std::endl;
411 }
412
Output()413 void DrawingDCL::Output()
414 {
415 }
416
417 class MyAllocator : public DefaultAllocator {
418 public:
MyAllocator(int fd,size_t size,uint8_t * mapFile)419 MyAllocator(int fd, size_t size, uint8_t* mapFile) : fd_(fd), size_(size), mapFile_(mapFile) {}
420
~MyAllocator()421 ~MyAllocator()
422 {
423 Dealloc(mapFile_);
424 }
425
Dealloc(void * mapFile)426 void Dealloc(void *mapFile)
427 {
428 if (mapFile != mapFile_) {
429 std::cout << "MyAllocator::Dealloc data addr not match!" << std::endl;
430 }
431 if (mapFile_ != nullptr) {
432 if (::munmap(mapFile_, size_) == -1) {
433 std::cout << "munmap failed!" <<std::endl;
434 }
435 mapFile_ = nullptr;
436 }
437 if (fd_ > 0) {
438 ::close(fd_);
439 fd_ = -1;
440 }
441 }
442 private:
443 int fd_;
444 size_t size_;
445 uint8_t *mapFile_;
446 };
447
GetRealPathStr(const std::string & filePath)448 std::string DrawingDCL::GetRealPathStr(const std::string& filePath)
449 {
450 std::string realPathStr = "";
451 char actualPath[PATH_MAX + 1] = {0};
452 if (realpath(filePath.c_str(), actualPath) == nullptr) {
453 std::cout << "The path of DrawCmdList file is empty!" << std::endl;
454 return realPathStr;
455 }
456 realPathStr = actualPath;
457 return realPathStr;
458 }
459
IsValidFile(const std::string & realPathStr)460 bool DrawingDCL::IsValidFile(const std::string& realPathStr)
461 {
462 return realPathStr.find(dclFileDir_) == 0;
463 }
464
LoadDrawCmdList(const std::string & dclFile)465 int DrawingDCL::LoadDrawCmdList(const std::string& dclFile)
466 {
467 std::string realDclFilePathStr = GetRealPathStr(dclFile);
468 if (realDclFilePathStr.empty()) {
469 return -1;
470 }
471 if (!IsValidFile(realDclFilePathStr)) {
472 std::cout << "The path of DrawCmdList file is not valid!" << std::endl;
473 return -1;
474 }
475 UniqueFd fd(open(realDclFilePathStr.c_str(), O_RDONLY));
476 if (fd.Get() < 0) {
477 std::cout << "Open file failed" << dclFile.c_str() << std::endl;
478 return -1;
479 }
480 struct stat statbuf;
481 if (fstat(fd.Get(), &statbuf) < 0) {
482 return -1;
483 }
484 std::cout << "statbuf.st_size = " << statbuf.st_size << std::endl;
485
486 auto mapFile = static_cast<uint8_t *>(mmap(nullptr, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
487 if (mapFile == MAP_FAILED) {
488 return -1;
489 }
490 std::cout << "mapFile OK" << std::endl;
491
492 MessageParcel messageParcel(new MyAllocator(fd.Get(), statbuf.st_size, mapFile));
493 messageParcel.SetMaxCapacity(recordingParcelMaxCapcity_);
494 if (!messageParcel.ParseFrom(reinterpret_cast<uintptr_t>(mapFile), statbuf.st_size)) {
495 munmap(mapFile, statbuf.st_size);
496 return -1;
497 }
498 std::cout << "messageParcel GetDataSize() = " << messageParcel.GetDataSize() << std::endl;
499
500 RSMarshallingHelper::BeginNoSharedMem(std::this_thread::get_id());
501 RSMarshallingHelper::Unmarshalling(messageParcel, dcl_);
502 RSMarshallingHelper::EndNoSharedMem();
503 if (dcl_ == nullptr) {
504 std::cout << "dcl is nullptr" << std::endl;
505 munmap(mapFile, statbuf.st_size);
506 return -1;
507 }
508 std::cout << "The size of Ops is " << dcl_->GetOpItemSize() << std::endl;
509 std::string opsFile = inputFilePath_ + "ops_frame" + std::to_string(curFrameNo_) + ".txt";
510 WriteStringToFile(dcl_->GetOpsWithDesc(), opsFile);
511 munmap(mapFile, statbuf.st_size);
512 return 0;
513 }
514 }
515 }
516