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 }