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 
16 #include <filesystem>
17 namespace fs = std::filesystem;
18 #include <iostream>
19 #include <string>
20 
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <unique_fd.h>
26 #include "directory_ex.h"
27 
28 #include "window.h"
29 
30 #include "drawing_sample_replayer.h"
31 
32 namespace OHOS::Rosen {
33 
~DrawingSampleReplayer()34 DrawingSampleReplayer::~DrawingSampleReplayer()
35 {
36     DestoryNativeWindow(nativeWindow_);
37     if (surfaceNode_) {
38         surfaceNode_->DetachToDisplay(defaultDisplayId_);
39     }
40 }
41 
ReadCmds(const std::string path)42 bool DrawingSampleReplayer::ReadCmds(const std::string path)
43 {
44     std::string realPath;
45     if (!PathToRealPath(path, realPath)) {
46         std::cout << "PathToRealPath fails on: " << path;
47         return false;
48     }
49     UniqueFd fd(open(realPath.c_str(), O_RDONLY));
50     if (fd <= 0) {
51         return false;
52     }
53     struct stat statbuf;
54     if (fstat(fd.Get(), &statbuf) < 0) {
55         return false;
56     }
57     auto mapFile = static_cast<uint8_t*>(mmap(nullptr, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
58     if (mapFile == MAP_FAILED) {
59         return false;
60     }
61     MessageParcel messageParcel(new EmptyAllocator());
62     if (!messageParcel.ParseFrom(reinterpret_cast<uintptr_t>(mapFile), statbuf.st_size)) {
63         munmap(mapFile, statbuf.st_size);
64         return false;
65     }
66     RSMarshallingHelper::BeginNoSharedMem(std::this_thread::get_id());
67     RSMarshallingHelper::Unmarshalling(messageParcel, dcl_);
68     RSMarshallingHelper::EndNoSharedMem();
69     if (dcl_ == nullptr) {
70         std::cout << "dcl is nullptr" << std::endl;
71         munmap(mapFile, statbuf.st_size);
72         return false;
73     }
74     munmap(mapFile, statbuf.st_size);
75     return true;
76 }
77 
InitCapturingSKP(Drawing::Canvas * canvas)78 bool DrawingSampleReplayer::InitCapturingSKP(Drawing::Canvas* canvas)
79 {
80 #if (defined RS_ENABLE_GL) || (defined RS_ENABLE_VK)
81     if (captureMode_ == CaptureMode::SKP) {
82         orgSkiaCanvas_ = canvas->GetImpl<Drawing::SkiaCanvas>()->ExportSkCanvas();
83         recorder_ = std::make_unique<SkPictureRecorder>();
84         pictureCanvas_ = recorder_->beginRecording(width_, height_);
85         if (pictureCanvas_ == nullptr) {
86             return false;
87         }
88         if (nwayCanvas_ == nullptr) {
89             nwayCanvas_ = std::make_unique<SkNWayCanvas>(width_, height_);
90         }
91         nwayCanvas_->addCanvas(orgSkiaCanvas_);
92         nwayCanvas_->addCanvas(pictureCanvas_);
93 
94         canvas->GetImpl<Drawing::SkiaCanvas>()->ImportSkCanvas(nwayCanvas_.get());
95         return true;
96     }
97 #endif
98     return false;
99 }
100 
FinilizeCapturingSKP(Drawing::Canvas * canvas)101 void DrawingSampleReplayer::FinilizeCapturingSKP(Drawing::Canvas* canvas)
102 {
103 #if (defined RS_ENABLE_GL) || (defined RS_ENABLE_VK)
104     if (captureMode_ == CaptureMode::SKP) {
105         // finishing capturing and saving skp
106         nwayCanvas_->removeAll();
107         sk_sp<SkPicture> picture = recorder_->finishRecordingAsPicture();
108         if (picture->approximateOpCount() > 0) {
109             SkSerialProcs procs;
110             procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
111                 return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
112             };
113             auto data = picture->serialize(&procs);
114             // to save locally
115             std::string outputFile = "/data/default.skp";
116             SkFILEWStream stream(outputFile.c_str());
117             if (stream.isValid()) {
118                 stream.write(data->data(), data->size());
119                 stream.flush();
120                 std::cout << "Written and flushed" << std::endl;
121             } else {
122                 std::cout << "Failed to create stream" << std::endl;
123             }
124         }
125         canvas->GetImpl<Drawing::SkiaCanvas>()->ImportSkCanvas(orgSkiaCanvas_);
126     }
127 #endif
128 }
129 
PrepareFrame(Drawing::Canvas * canvas)130 bool DrawingSampleReplayer::PrepareFrame(Drawing::Canvas* canvas)
131 {
132     if (canvas == nullptr) {
133         return false;
134     }
135     bool captureStarted = InitCapturingSKP(canvas);
136     // for now we capture single frame only
137     if (!ReadCmds("/data/default.drawing")) {
138         std::cout << "Failed to load .drawing cmds" << std::endl;
139         return false;
140     }
141     dcl_->Playback(*canvas);
142     if (captureStarted) {
143         FinilizeCapturingSKP(canvas);
144     }
145     return true;
146 }
147 
RenderLoop()148 bool DrawingSampleReplayer::RenderLoop()
149 {
150     PrepareNativeEGLSetup();
151     int maxIter = (captureMode_ == CaptureMode::RDC) ? maxRenderedFrames_ : 1;
152 
153     for (int i = 0; i < maxIter; ++i) {
154         std::shared_ptr<Drawing::Surface> drawingSurface = renderContext_->AcquireSurface(width_, height_);
155         Drawing::Canvas* canvas = drawingSurface->GetCanvas().get();
156 
157         std::cout << i << "\t >> new iteration" << std::endl;
158 
159         if (!PrepareFrame(canvas)) {
160             return false;
161         }
162 
163         renderContext_->RenderFrame(); // which involves flush()
164         renderContext_->SwapBuffers(eglSurface_);
165 
166         int rdcnum = 0;
167         std::string path = "/data/autocaps";
168         // Checks if the folder exists
169         if (!fs::exists(path)) {
170             continue;
171         }
172         for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(path)) {
173             const std::filesystem::path& path = entry.path();
174             if (path.extension() == ".rdc") {
175                 rdcnum++;
176             }
177         }
178         if (rdcnum > 0) {
179             // it means the .rdc capture is saved succesfully and further handled by the RsProfiler
180             return true;
181         }
182     }
183     return false;
184 }
185 
PrepareNativeEGLSetup()186 bool DrawingSampleReplayer::PrepareNativeEGLSetup()
187 {
188     renderContext_ = std::make_shared<RenderContext>();
189     renderContext_->InitializeEglContext();
190 
191     auto defaultDisplay = DisplayManager::GetInstance().GetDefaultDisplay();
192     width_ = static_cast<uint32_t>(defaultDisplay->GetWidth());
193     height_ = static_cast<uint32_t>(defaultDisplay->GetHeight());
194 
195     std::cout << "Width: " << width_ << "  -  Height: " << height_ << std::endl;
196 
197     RSSurfaceNodeConfig surfaceNodeConfig = {"DrawingEngineSample"};
198     RSSurfaceNodeType surfaceNodeType = RSSurfaceNodeType::SELF_DRAWING_WINDOW_NODE;
199     surfaceNode_ = RSSurfaceNode::Create(surfaceNodeConfig, surfaceNodeType);
200     if (!surfaceNode_) {
201         std::cout << "surfaceNode is nullptr" << std::endl;
202         return false;
203     }
204 
205     surfaceNode_->SetFrameGravity(Gravity::RESIZE_ASPECT_FILL);
206     surfaceNode_->SetPositionZ(RSSurfaceNode::POINTER_WINDOW_POSITION_Z);
207     surfaceNode_->SetBounds(0, 0, width_, height_);
208     surfaceNode_->AttachToDisplay(defaultDisplayId_);
209     RSTransaction::FlushImplicitTransaction();
210 
211     sptr<Surface> ohosSurface = surfaceNode_->GetSurface();
212     nativeWindow_ = CreateNativeWindowFromSurface(&ohosSurface);
213     if (nativeWindow_ == nullptr) {
214         std::cout << "CreateNativeWindowFromSurface Failed" << std::endl;
215         return false;
216     }
217 
218     eglSurface_ = renderContext_->CreateEGLSurface((EGLNativeWindowType)nativeWindow_);
219     if (eglSurface_ == EGL_NO_SURFACE) {
220         std::cout << "Invalid eglSurface, return" << std::endl;
221         DestoryNativeWindow(nativeWindow_);
222         return false;
223     }
224 
225     renderContext_->MakeCurrent(eglSurface_);
226     EGLint initRes = eglGetError();
227     if (initRes != EGL_SUCCESS) {
228         std::cout << "Fail to init egl" << std::endl;
229         DestoryNativeWindow(nativeWindow_);
230         return false;
231     }
232     NativeWindowHandleOpt(nativeWindow_, SET_BUFFER_GEOMETRY, width_, height_);
233     return true;
234 }
235 
SetCaptureMode(CaptureMode mode)236 void DrawingSampleReplayer::SetCaptureMode(CaptureMode mode)
237 {
238     captureMode_ = mode;
239 }
240 
241 } // namespace OHOS::Rosen
242 
main(int32_t argc,char * argv[])243 int32_t main(int32_t argc, char *argv[])
244 {
245     OHOS::Rosen::DrawingSampleReplayer replayer;
246     std::string replayType = "default";
247     if (argc > 1) {
248         replayType = std::string(argv[1]);
249         if (replayType == "skp") {
250             replayer.SetCaptureMode(OHOS::Rosen::CaptureMode::SKP);
251         } else if (replayType == "rdc") {
252             replayer.SetCaptureMode(OHOS::Rosen::CaptureMode::RDC);
253         }
254     }
255     std::cout << "Mode: " << replayType << std::endl;
256     if (!replayer.PrepareNativeEGLSetup()) {
257         return -1;
258     }
259     if (!replayer.RenderLoop()) {
260         return -1;
261     }
262     return 0;
263 }
264