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