• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 <dlfcn.h>
17 #include <iostream>
18 #include <sstream>
19 #include <string>
20 #include <chrono>
21 #include <message_parcel.h>
22 #include <surface.h>
23 #include "transaction/rs_transaction.h"
24 #include "wm/window.h"
25 #include "ui/rs_root_node.h"
26 #include "ui/rs_surface_node.h"
27 #include "ui/rs_ui_director.h"
28 #include "utils/log.h"
29 #include "dtk_test_register.h"
30 #include "dtk_test_utils.h"
31 
32 using namespace OHOS;
33 using namespace OHOS::Rosen;
34 using namespace std;
35 
36 shared_ptr<RSNode> rootNode;
37 shared_ptr<RSCanvasNode> canvasNode;
Init(shared_ptr<RSUIDirector> rsUiDirector,int width,int height)38 void Init(shared_ptr<RSUIDirector> rsUiDirector, int width, int height)
39 {
40     cout << "[INFO] rs pixelmap demo Init Rosen Backend!" << endl;
41 
42     rootNode = RSRootNode::Create();
43     rootNode->SetBounds(0, 0, width, height);
44     rootNode->SetFrame(0, 0, width, height);
45 
46     rootNode->SetBackgroundColor(Drawing::Color::COLOR_WHITE);
47 
48     rsUiDirector->SetRoot(rootNode->GetId());
49     canvasNode = RSCanvasNode::Create();
50     canvasNode->SetFrame(0, 0, width, height);
51     rootNode->AddChild(canvasNode, -1);
52 }
53 
ExportSkImage(sk_sp<SkImage> & image,const std::string & fileName)54 void ExportSkImage(sk_sp<SkImage>& image, const std::string& fileName)
55 {
56     std::string dir("/data/local/tmp/dtk_output");
57     if (access(dir.c_str(), F_OK) < 0) {
58         return;
59     }
60     if (access(dir.c_str(), W_OK) < 0) {
61         return;
62     }
63 
64     sk_sp<SkData> snapshot = image->encodeToData();
65     std::string dumpFileName = dir + fileName + ".jpg";
66 
67     SkFILEWStream outStream(dumpFileName.c_str());
68     auto img = snapshot;
69     if (img == nullptr) {
70         return;
71     }
72     outStream.write(img->bytes(), img->size());
73     outStream.flush();
74     cout << "[INFO] pixelmap write to jpg sucess" << endl;
75 }
76 
OnPixelMapCapture(std::shared_ptr<Media::PixelMap> pixelmap,std::string fileName)77 void OnPixelMapCapture(std::shared_ptr<Media::PixelMap> pixelmap, std::string fileName)
78 {
79     if (pixelmap == nullptr) {
80         cout << "[ERROR] faild to get pixelmap, return nullptr!" << endl;
81         return;
82     }
83 
84     SkBitmap bitmap;
85     auto skii = SkImageInfo::Make(pixelmap->GetWidth(), pixelmap->GetHeight(), SkColorType::kRGBA_8888_SkColorType,
86         SkAlphaType::kPremul_SkAlphaType);
87     if (!bitmap.installPixels(skii, (void*)pixelmap->GetPixels(), skii.minRowBytes64())) {
88         std::cout << __func__ << "installPixels failed" << std::endl;
89         return;
90     }
91     sk_sp<SkImage> image = bitmap.asImage();
92     ExportSkImage(image, fileName);
93 }
94 
OnImageCapture(std::shared_ptr<Drawing::Image> image,std::string fileName)95 void OnImageCapture(std::shared_ptr<Drawing::Image> image, std::string fileName)
96 {
97     if (image == nullptr) {
98         cout << "[ERROR] failed to get image, return nullptr" << endl;
99         return;
100     }
101 
102     Drawing::BitmapFormat format = { Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_PREMUL};
103     Drawing::Bitmap bitmap;
104     bitmap.Build(image->GetWidth(), image->GetHeight(), format);
105     image->ReadPixels(bitmap, 0, 0);
106     SkBitmap skbitmap;
107     auto skii = SkImageInfo::Make(image->GetWidth(), image->GetHeight(),
108         SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType);
109     if (!skbitmap.installPixels(skii, bitmap.GetPixels(), skii.minRowBytes())) {
110         std::cout << __func__ << "installPixels failed" << std::endl;
111         return;
112     }
113     sk_sp<SkImage> skimage = skbitmap.asImage();
114     ExportSkImage(skimage, fileName);
115 }
116 
SkipInitial(const std::shared_ptr<TestBase> & initCase,const std::shared_ptr<Drawing::Canvas> & initCanvas,const std::shared_ptr<Drawing::GPUContext> & initgpuCtx,const bool & isRunCase)117 void SkipInitial(const std::shared_ptr<TestBase>& initCase, const std::shared_ptr<Drawing::Canvas>& initCanvas,
118     const std::shared_ptr<Drawing::GPUContext>& initgpuCtx, const bool& isRunCase)
119 {
120     // Render 10 frames to complete initialization before capturing the flame graph
121     for (int i = 0; i < 10; i++) {
122         initCase->SetCanvas(initCanvas.get());
123         initCase->Recording();
124         initgpuCtx->FlushAndSubmit(isRunCase);
125     }
126 }
127 
runWithWindow(std::string testCaseName,std::function<std::shared_ptr<TestBase> ()> creator,int testCount,int frame,bool noDump)128 void runWithWindow(std::string testCaseName, std::function<std::shared_ptr<TestBase>()> creator, int testCount,
129                    int frame, bool noDump)
130 {
131     string demoName = "dtk_" + testCaseName;
132     RSSystemProperties::GetUniRenderEnabled();
133     sptr<WindowOption> option = new WindowOption();
134     option->SetWindowType(WindowType::WINDOW_TYPE_POINTER);
135     option->SetWindowMode(WindowMode::WINDOW_MODE_FLOATING);
136     option->SetWindowRect({ 0, 0, 1334, 2772 });
137     auto window = Window::Create(demoName, option);
138 
139     window->Show();
140     usleep(30000); // sleep 30000 microsecond
141     auto rect = window->GetRect();
142     while (rect.width_ == 0 && rect.height_ == 0) {
143         cout << "[ERROR] dtk create window failed: " << rect.width_ << rect.height_ << endl;
144         window->Hide();
145         window->Destroy();
146         window = Window::Create(demoName, option);
147         window->Show();
148         usleep(30000); // sleep 30000 microsecond
149         rect = window->GetRect();
150     }
151     cout << "[INFO] dtk create window success: " << rect.width_ << " " << rect.height_ << endl;
152     auto surfaceNode = window->GetSurfaceNode();
153 
154     auto rsUiDirector = RSUIDirector::Create();
155     rsUiDirector->Init();
156     RSTransaction::FlushImplicitTransaction();
157     usleep(100000); // sleep 100000 microsecond
158 
159     rsUiDirector->SetRSSurfaceNode(surfaceNode);
160     Init(rsUiDirector, rect.width_, rect.height_);
161     rsUiDirector->SendMessages();
162     usleep(100000); // sleep 100000 microsecond
163     cout << "[INFO] BeginRecording" << endl;
164 
165     auto testCase = creator();
166     testCase->SetTestCount(testCount);
167     for (int i = 0; i < frame; i++) {
168         auto canvas = canvasNode->BeginRecording(rect.width_, rect.height_);
169         testCase->SetCanvas((TestPlaybackCanvas*)(canvas));
170         testCase->Recording();
171         canvasNode->FinishRecording();
172         rsUiDirector->SendMessages();
173         usleep(16666); // sleep 16666 microsecond
174     }
175     usleep(50000); // sleep 50000 microsecond
176     cout << "[INFO] FinishRecording" << endl;
177     if (!noDump) {
178         OnPixelMapCapture(window->Snapshot(), testCaseName);
179     }
180     window->Hide();
181     window->Destroy();
182 }
183 
runSingleTestCase(std::string testCaseName,std::function<std::shared_ptr<TestBase> ()> creator,int testCount,int frame,bool offscreen,bool noDump,bool skipInitial,bool runLoadCase)184 void runSingleTestCase(std::string testCaseName, std::function<std::shared_ptr<TestBase>()> creator,
185                        int testCount, int frame, bool offscreen, bool noDump, bool skipInitial, bool runLoadCase)
186 {
187     cout << "[INFO]testCase--" << testCaseName << "-- starts! " << endl;
188     runWithWindow(testCaseName, creator, testCount, frame, noDump);
189     cout << "[INFO] rs draw demo end!" << endl;
190 }
191 
getTestLevel(std::string fullTestCaseName,int & currentLevel)192 bool getTestLevel(std::string fullTestCaseName, int& currentLevel)
193 {
194     size_t pos1 = fullTestCaseName.find_last_of('_');
195     if (std::string::npos == pos1) {
196         return false;
197     }
198     size_t pos2 = fullTestCaseName.find_last_of('_', pos1 - 1);
199     if (std::string::npos == pos2) {
200         return false;
201     }
202     std::string keyLevelStr = fullTestCaseName.substr(pos2 + 2, pos1 - pos2 - 1);
203     currentLevel = std::stoi(keyLevelStr);
204     return true;
205 }
206 
printNames(bool isFullname)207 void printNames(bool isFullname)
208 {
209     std::set<std::string> names;
210     auto testCaseMap = DtkTestRegister::Instance()->getTestCaseMap();
211     for (const auto& [key, value] : testCaseMap) {
212         if (isFullname) {
213             names.insert(key);
214         } else {
215             size_t pos1 = key.find_last_of('_');
216             if (std::string::npos == pos1) {
217                 continue;
218             }
219             size_t pos2 = key.find_last_of('_', pos1 - 1);
220             if (std::string::npos == pos2) {
221                 continue;
222             }
223             names.insert(key.substr(0, pos2));
224         }
225     }
226 
227     for (auto& name : names) {
228         cout << name << endl;
229     }
230 }
231 
findIntParam(unordered_map<string,int> & params,char * argv[],string op1,string op2,int defaultValue)232 int findIntParam(unordered_map<string, int>& params, char* argv[], string op1, string op2,
233                  int defaultValue)
234 {
235     if (params.find(op1) != params.end()) {
236         int i = params.find(op1)->second + 1;
237         return stoi(argv[i]);
238     } else if (params.find(op2) != params.end()) {
239         int i = params.find(op2)->second + 1;
240         return stoi(argv[i]);
241     } else {
242         return defaultValue;
243     }
244 }
245 
dumpCovData(const std::string & soPath)246 void dumpCovData(const std::string& soPath)
247 {
248     using FuncType = void();
249     auto handle = dlopen(soPath.c_str(), RTLD_NOW);
250     void* func = nullptr;
251     if (!handle) {
252         cout << "[ERROR] Cannot find " << soPath << endl;
253     } else {
254         func = dlsym(handle, "__gcov_dump");
255     }
256     if (func) {
257         reinterpret_cast<FuncType*>(func)();
258         cout << "[INFO] Run __gcov_dump in " << soPath << "successfully" << endl;
259     } else {
260         cout << "[ERROR] Cannot find __gcov_dump in " << soPath << endl;
261     }
262 }
263 
264 const string DTK_HELP_LIST_STR = R"(
265 Usage: dtk {testCaseName} [OPTIONS...]
266 Examples:
267   dtk drawrect
268   dtk drawline -testLevel 0 -testIndex 0
269   dtk drawrect -c 1 -f 1 -l 0 -i 0
270   dtk drawrect --offscreen --noDump --delay
271 OPTIONS:
272   -l, -testLevel: Test Level. Run all test levels if it is -1 or not set.
273   -i, -testIndex: Test index. Run all test indexes if it is -1 or not set.
274   -c, -testCount: Test count in a single frame. It needs to be supported by test case itself.
275                   It will be 1 if it is not set.
276   -f, -frame:     Test frames that affact test duration. It will be 1 if it is not set.
277   -h, --help:     Give this help list)
278   names:          Print all test case name.
279   fullnames:      Print all test case name with testLevel and test indexs.
280   offscreen:      It will not be shown on the screen.
281   noDumnp:        It will not dump image(jpg) in /data/local/tmp/dtk_output/
282   skipInitial:    It will Skip Initialization to capture a flame graph, It can only take effect in off-screen mode
283   delay:          Delay 2s before running.
284   all:            Run all test cases.
285   coverage:       Manually call __gcov_dump before exit 0
286   load:           Run CPU and Gpu load cases
287 )";
288 
main(int argc,char * argv[])289 int main(int argc, char* argv[])
290 {
291     if (argc < 2) { // should input more than 2 args
292         cout << "[ERROR] args are too less, please try again!" << endl;
293         return 0;
294     }
295     unordered_map<string, int> params;
296     for (int i = 1; i < argc; ++i) {
297         params.emplace(argv[i], i);
298     }
299 
300     if (params.find("-h") != params.end() || params.find("--help") != params.end()) {
301         cout << DTK_HELP_LIST_STR << endl;
302         return 0;
303     } else if (params.find("--names") != params.end()) {
304         printNames(false);
305         return 0;
306     } else if (params.find("--fullnames") != params.end()) {
307         printNames(true);
308         return 0;
309     }
310 
311     string testCaseName = argv[1];
312     bool offscreen = params.find("--offscreen") != params.end();
313     bool noDump = params.find("--noDump") != params.end();
314     bool delay = params.find("--delay") != params.end();
315     bool runAll = params.find("--all") != params.end();
316     bool runLoadCase = params.find("--load") != params.end();
317     bool enableCoverage = params.find("--coverage") != params.end();
318     bool skipInitial = params.find("--skipInitial") != params.end();
319     int testCount = findIntParam(params, argv, "-c", "-testCount", 1);
320     int frame = findIntParam(params, argv, "-f", "-frame", 1);
321     int testLevel = findIntParam(params, argv, "-l", "-testLevel", -1);
322     int index = findIntParam(params, argv, "-i", "-testIndex", -1);
323 
324     if (delay) {
325         sleep(2); // 预留 2s
326     }
327 
328     auto testCaseMap = DtkTestRegister::Instance()->getTestCaseMap();
329     bool testCaseExits = false;
330     if (runAll) {
331         for (const auto& [key, value] : testCaseMap) {
332             testCaseExits = true;
333             if (testLevel == -1) {
334                 // run all testcases on all testlevels and all testcaseIndexes
335                 runSingleTestCase(key, value, testCount, frame, offscreen, noDump, skipInitial, runLoadCase);
336                 continue;
337             }
338             int currentLevel;
339             if (getTestLevel(key, currentLevel) && testLevel == currentLevel) {
340                 // run all testCaseIndexes of all testcases on a specific testLevel
341                 runSingleTestCase(key, value, testCount, frame, offscreen, noDump, skipInitial, runLoadCase);
342             }
343         }
344     } else if (testLevel == -1) {
345         // run all testlevels of a specific testcase
346         for (const auto& [key, value] : testCaseMap) {
347             if (key.rfind(testCaseName, 0) == 0) {
348                 testCaseExits = true;
349                 runSingleTestCase(key, value, testCount, frame, offscreen, noDump, skipInitial, runLoadCase);
350             }
351         }
352     } else if (index == -1) {
353         // run all testCaseIndexes on a specific testLevel of a specific testcase
354         for (const auto& [key, value] : testCaseMap) {
355             string prefix = testCaseName + "_L" + to_string(testLevel);
356             if (key.rfind(prefix, 0) == 0) {
357                 testCaseExits = true;
358                 runSingleTestCase(key, value, testCount, frame, offscreen, noDump, skipInitial, runLoadCase);
359             }
360         }
361     } else {
362         // run a specific testCaseIndex on a specific testLevel of a specific testcase
363         string fullTestCaseName = testCaseName + "_L" + to_string(testLevel) +
364             "_" + to_string(index);
365         if (testCaseMap.find(fullTestCaseName) != testCaseMap.end()) {
366             testCaseExits = true;
367             auto creator = testCaseMap.find(fullTestCaseName)->second;
368             runSingleTestCase(fullTestCaseName, creator, testCount, frame, offscreen, noDump, skipInitial, runLoadCase);
369         }
370     }
371     if (!testCaseExits) {
372         cout << "[ERROR] testCaseName(or testlevel or testCaseIndex) not exists" << endl;
373     }
374 
375     if (enableCoverage) {
376         // 覆盖率文件导出到此路径
377         setenv("GCOV_PREFIX", "/data/service/el0/render_services", 1);
378         dumpCovData("/system/lib64/libddgr.z.so");
379         dumpCovData("/system/lib64/libddgr_compiler.z.so");
380         dumpCovData("/system/lib64/lib2d_graphics.z.so");
381     }
382 
383     return 0;
384 }