• 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 #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 }