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