733523eb81d735741bba2f2179b7e555cafdccf4
[android/external-libkmsxx.git] / utils / kmstest.cpp
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 #include <regex>
5 #include <set>
6 #include <chrono>
8 #include <sys/select.h>
10 #include <kms++/kms++.h>
11 #include <kms++/modedb.h>
12 #include <kms++/mode_cvt.h>
14 #include <kms++util/kms++util.h>
16 using namespace std;
17 using namespace kms;
19 struct PlaneInfo
20 {
21         Plane* plane;
23         unsigned x;
24         unsigned y;
25         unsigned w;
26         unsigned h;
28         unsigned view_x;
29         unsigned view_y;
30         unsigned view_w;
31         unsigned view_h;
33         vector<MappedFramebuffer*> fbs;
34 };
36 struct OutputInfo
37 {
38         Connector* connector;
40         Crtc* crtc;
41         Plane* primary_plane;
42         Videomode mode;
43         bool user_set_crtc;
44         vector<MappedFramebuffer*> fbs;
46         vector<PlaneInfo> planes;
47 };
49 static bool s_use_dmt;
50 static bool s_use_cea;
51 static unsigned s_num_buffers = 1;
52 static bool s_flip_mode;
53 static bool s_flip_sync;
54 static bool s_cvt;
55 static bool s_cvt_v2;
56 static bool s_cvt_vid_opt;
58 static set<Crtc*> s_used_crtcs;
59 static set<Plane*> s_used_planes;
61 __attribute__ ((unused))
62 static void print_regex_match(smatch sm)
63 {
64         for (unsigned i = 0; i < sm.size(); ++i) {
65                 string str = sm[i].str();
66                 printf("%u: %s\n", i, str.c_str());
67         }
68 }
70 static void get_default_connector(Card& card, OutputInfo& output)
71 {
72         output.connector = card.get_first_connected_connector();
73         output.mode = output.connector->get_default_mode();
74 }
76 static void parse_connector(Card& card, const string& str, OutputInfo& output)
77 {
78         Connector* conn = resolve_connector(card, str);
80         if (!conn)
81                 EXIT("No connector '%s'", str.c_str());
83         if (!conn->connected())
84                 EXIT("Connector '%s' not connected", conn->fullname().c_str());
86         output.connector = conn;
87         output.mode = output.connector->get_default_mode();
88 }
90 static void get_default_crtc(Card& card, OutputInfo& output)
91 {
92         Crtc* crtc = output.connector->get_current_crtc();
94         if (crtc && s_used_crtcs.find(crtc) == s_used_crtcs.end()) {
95                 s_used_crtcs.insert(crtc);
96                 output.crtc = crtc;
97                 return;
98         }
100         for (const auto& possible : output.connector->get_possible_crtcs()) {
101                 if (s_used_crtcs.find(possible) == s_used_crtcs.end()) {
102                         s_used_crtcs.insert(possible);
103                         output.crtc = possible;
104                         return;
105                 }
106         }
108         EXIT("Could not find available crtc");
111 static void parse_crtc(Card& card, const string& crtc_str, OutputInfo& output)
113         // @12:1920x1200i@60
114         // @12:33000000,800/210/30/16/-,480/22/13/10/-,i
116         const regex modename_re("(?:(@?)(\\d+):)?"      // @12:
117                                 "(?:(\\d+)x(\\d+)(i)?)" // 1920x1200i
118                                 "(?:@([\\d\\.]+))?");   // @60
120         const regex modeline_re("(?:(@?)(\\d+):)?"                      // @12:
121                                 "(\\d+),"                               // 33000000,
122                                 "(\\d+)/(\\d+)/(\\d+)/(\\d+)/([+-]),"   // 800/210/30/16/-,
123                                 "(\\d+)/(\\d+)/(\\d+)/(\\d+)/([+-])"    // 480/22/13/10/-
124                                 "(?:,([i]+))?"                          // ,i
125                                 );
127         smatch sm;
128         if (regex_match(crtc_str, sm, modename_re)) {
129                 if (sm[2].matched) {
130                         bool use_id = sm[1].length() == 1;
131                         unsigned num = stoul(sm[2].str());
133                         if (use_id) {
134                                 Crtc* c = card.get_crtc(num);
135                                 if (!c)
136                                         EXIT("Bad crtc id '%u'", num);
138                                 output.crtc = c;
139                         } else {
140                                 auto crtcs = card.get_crtcs();
142                                 if (num >= crtcs.size())
143                                         EXIT("Bad crtc number '%u'", num);
145                                 output.crtc = crtcs[num];
146                         }
147                 } else {
148                         output.crtc = output.connector->get_current_crtc();
149                 }
151                 unsigned w = stoul(sm[3]);
152                 unsigned h = stoul(sm[4]);
153                 bool ilace = sm[5].matched ? true : false;
154                 float refresh = sm[6].matched ? stof(sm[6]) : 0;
156                 if (s_cvt) {
157                         output.mode = videomode_from_cvt(w, h, refresh, ilace, s_cvt_v2, s_cvt_vid_opt);
158                 } else if (s_use_dmt) {
159                         try {
160                                 output.mode = find_dmt(w, h, refresh, ilace);
161                         } catch (exception& e) {
162                                 EXIT("Mode not found from DMT tables\n");
163                         }
164                 } else if (s_use_cea) {
165                         try {
166                                 output.mode = find_cea(w, h, refresh, ilace);
167                         } catch (exception& e) {
168                                 EXIT("Mode not found from CEA tables\n");
169                         }
170                 } else {
171                         try {
172                                 output.mode = output.connector->get_mode(w, h, refresh, ilace);
173                         } catch (exception& e) {
174                                 EXIT("Mode not found from the connector\n");
175                         }
176                 }
177         } else if (regex_match(crtc_str, sm, modeline_re)) {
178                 if (sm[2].matched) {
179                         bool use_id = sm[1].length() == 1;
180                         unsigned num = stoul(sm[2].str());
182                         if (use_id) {
183                                 Crtc* c = card.get_crtc(num);
184                                 if (!c)
185                                         EXIT("Bad crtc id '%u'", num);
187                                 output.crtc = c;
188                         } else {
189                                 auto crtcs = card.get_crtcs();
191                                 if (num >= crtcs.size())
192                                         EXIT("Bad crtc number '%u'", num);
194                                 output.crtc = crtcs[num];
195                         }
196                 } else {
197                         output.crtc = output.connector->get_current_crtc();
198                 }
200                 unsigned clock = stoul(sm[3]);
202                 unsigned hact = stoul(sm[4]);
203                 unsigned hfp = stoul(sm[5]);
204                 unsigned hsw = stoul(sm[6]);
205                 unsigned hbp = stoul(sm[7]);
206                 bool h_pos_sync = sm[8] == "+" ? true : false;
208                 unsigned vact = stoul(sm[9]);
209                 unsigned vfp = stoul(sm[10]);
210                 unsigned vsw = stoul(sm[11]);
211                 unsigned vbp = stoul(sm[12]);
212                 bool v_pos_sync = sm[13] == "+" ? true : false;
214                 output.mode = videomode_from_timings(clock / 1000, hact, hfp, hsw, hbp, vact, vfp, vsw, vbp);
215                 output.mode.set_hsync(h_pos_sync ? SyncPolarity::Positive : SyncPolarity::Negative);
216                 output.mode.set_vsync(v_pos_sync ? SyncPolarity::Positive : SyncPolarity::Negative);
218                 if (sm[14].matched) {
219                         for (int i = 0; i < sm[14].length(); ++i) {
220                                 char f = string(sm[14])[i];
222                                 switch (f) {
223                                 case 'i':
224                                         output.mode.set_interlace(true);
225                                         break;
226                                 default:
227                                         EXIT("Bad mode flag %c", f);
228                                 }
229                         }
230                 }
231         } else {
232                 EXIT("Failed to parse crtc option '%s'", crtc_str.c_str());
233         }
236 static void parse_plane(Card& card, const string& plane_str, const OutputInfo& output, PlaneInfo& pinfo)
238         // 3:400,400-400x400
239         const regex plane_re("(?:(@?)(\\d+):)?"         // 3:
240                              "(?:(\\d+),(\\d+)-)?"      // 400,400-
241                              "(\\d+)x(\\d+)");          // 400x400
243         smatch sm;
244         if (!regex_match(plane_str, sm, plane_re))
245                 EXIT("Failed to parse plane option '%s'", plane_str.c_str());
247         if (sm[2].matched) {
248                 bool use_id = sm[1].length() == 1;
249                 unsigned num = stoul(sm[2].str());
251                 if (use_id) {
252                         Plane* p = card.get_plane(num);
253                         if (!p)
254                                 EXIT("Bad plane id '%u'", num);
256                         pinfo.plane = p;
257                 } else {
258                         auto planes = card.get_planes();
260                         if (num >= planes.size())
261                                 EXIT("Bad plane number '%u'", num);
263                         pinfo.plane = planes[num];
264                 }
265         } else {
266                 for (Plane* p : output.crtc->get_possible_planes()) {
267                         if (s_used_planes.find(p) != s_used_planes.end())
268                                 continue;
270                         if (p->plane_type() != PlaneType::Overlay)
271                                 continue;
273                         pinfo.plane = p;
274                 }
276                 if (!pinfo.plane)
277                         EXIT("Failed to find available plane");
278         }
280         s_used_planes.insert(pinfo.plane);
282         pinfo.w = stoul(sm[5]);
283         pinfo.h = stoul(sm[6]);
285         if (sm[3].matched)
286                 pinfo.x = stoul(sm[3]);
287         else
288                 pinfo.x = output.mode.hdisplay / 2 - pinfo.w / 2;
290         if (sm[4].matched)
291                 pinfo.y = stoul(sm[4]);
292         else
293                 pinfo.y = output.mode.vdisplay / 2 - pinfo.h / 2;
296 static vector<MappedFramebuffer*> get_default_fb(Card& card, unsigned width, unsigned height)
298         vector<MappedFramebuffer*> v;
300         for (unsigned i = 0; i < s_num_buffers; ++i)
301                 v.push_back(new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888));
303         return v;
306 static vector<MappedFramebuffer*> parse_fb(Card& card, const string& fb_str, unsigned def_w, unsigned def_h)
308         unsigned w = def_w;
309         unsigned h = def_h;
310         PixelFormat format = PixelFormat::XRGB8888;
312         if (!fb_str.empty()) {
313                 // XXX the regexp is not quite correct
314                 // 400x400-NV12
315                 const regex fb_re("(?:(\\d+)x(\\d+))?"          // 400x400
316                                   "(?:-)?"                      // -
317                                   "(\\w\\w\\w\\w)?");           // NV12
319                 smatch sm;
320                 if (!regex_match(fb_str, sm, fb_re))
321                         EXIT("Failed to parse fb option '%s'", fb_str.c_str());
323                 if (sm[1].matched)
324                         w = stoul(sm[1]);
325                 if (sm[2].matched)
326                         h = stoul(sm[2]);
327                 if (sm[3].matched)
328                         format = FourCCToPixelFormat(sm[3]);
329         }
331         vector<MappedFramebuffer*> v;
333         for (unsigned i = 0; i < s_num_buffers; ++i)
334                 v.push_back(new DumbFramebuffer(card, w, h, format));
336         return v;
339 static void parse_view(const string& view_str, PlaneInfo& pinfo)
341         const regex view_re("(\\d+),(\\d+)-(\\d+)x(\\d+)");             // 400,400-400x400
343         smatch sm;
344         if (!regex_match(view_str, sm, view_re))
345                 EXIT("Failed to parse view option '%s'", view_str.c_str());
347         pinfo.view_x = stoul(sm[1]);
348         pinfo.view_y = stoul(sm[2]);
349         pinfo.view_w = stoul(sm[3]);
350         pinfo.view_h = stoul(sm[4]);
353 static const char* usage_str =
354                 "Usage: kmstest [OPTION]...\n\n"
355                 "Show a test pattern on a display or plane\n\n"
356                 "Options:\n"
357                 "      --device=DEVICE       DEVICE is the path to DRM card to open\n"
358                 "  -c, --connector=CONN      CONN is <connector>\n"
359                 "  -r, --crtc=CRTC           CRTC is [<crtc>:]<w>x<h>[@<Hz>]\n"
360                 "                            or\n"
361                 "                            [<crtc>:]<pclk>,<hact>/<hfp>/<hsw>/<hbp>/<hsp>,<vact>/<vfp>/<vsw>/<vbp>/<vsp>[,i]\n"
362                 "  -p, --plane=PLANE         PLANE is [<plane>:][<x>,<y>-]<w>x<h>\n"
363                 "  -f, --fb=FB               FB is [<w>x<h>][-][<4cc>]\n"
364                 "  -v, --view=VIEW           VIEW is <x>,<y>-<w>x<h>\n"
365                 "      --dmt                 Search for the given mode from DMT tables\n"
366                 "      --cea                 Search for the given mode from CEA tables\n"
367                 "      --cvt=CVT             Create videomode with CVT. CVT is 'v1', 'v2' or 'v2o'\n"
368                 "      --flip                Do page flipping for each output\n"
369                 "      --sync                Synchronize page flipping\n"
370                 "\n"
371                 "<connector>, <crtc> and <plane> can be given by index (<idx>) or id (<id>).\n"
372                 "<connector> can also be given by name.\n"
373                 "\n"
374                 "Options can be given multiple times to set up multiple displays or planes.\n"
375                 "Options may apply to previous options, e.g. a plane will be set on a crtc set in\n"
376                 "an earlier option.\n"
377                 "If you omit parameters, kmstest tries to guess what you mean\n"
378                 "\n"
379                 "Examples:\n"
380                 "\n"
381                 "Set eDP-1 mode to 1920x1080@60, show XR24 framebuffer on the crtc, and a 400x400 XB24 plane:\n"
382                 "    kmstest -c eDP-1 -r 1920x1080@60 -f XR24 -p 400x400 -f XB24\n\n"
383                 "XR24 framebuffer on first connected connector in the default mode:\n"
384                 "    kmstest -f XR24\n\n"
385                 "XR24 framebuffer on a 400x400 plane on the first connected connector in the default mode:\n"
386                 "    kmstest -p 400x400 -f XR24\n\n"
387                 "Test pattern on the second connector with default mode:\n"
388                 "    kmstest -c 1\n"
389                 ;
391 static void usage()
393         puts(usage_str);
396 enum class ObjectType
398         Connector,
399         Crtc,
400         Plane,
401         Framebuffer,
402         View,
403 };
405 struct Arg
407         ObjectType type;
408         string arg;
409 };
411 static string s_device_path = "/dev/dri/card0";
413 static vector<Arg> parse_cmdline(int argc, char **argv)
415         vector<Arg> args;
417         OptionSet optionset = {
418                 Option("|device=",
419                 [&](string s)
420                 {
421                         s_device_path = s;
422                 }),
423                 Option("c|connector=",
424                 [&](string s)
425                 {
426                         args.push_back(Arg { ObjectType::Connector, s });
427                 }),
428                 Option("r|crtc=", [&](string s)
429                 {
430                         args.push_back(Arg { ObjectType::Crtc, s });
431                 }),
432                 Option("p|plane=", [&](string s)
433                 {
434                         args.push_back(Arg { ObjectType::Plane, s });
435                 }),
436                 Option("f|fb=", [&](string s)
437                 {
438                         args.push_back(Arg { ObjectType::Framebuffer, s });
439                 }),
440                 Option("v|view=", [&](string s)
441                 {
442                         args.push_back(Arg { ObjectType::View, s });
443                 }),
444                 Option("|dmt", []()
445                 {
446                         s_use_dmt = true;
447                 }),
448                 Option("|cea", []()
449                 {
450                         s_use_cea = true;
451                 }),
452                 Option("|flip", []()
453                 {
454                         s_flip_mode = true;
455                         s_num_buffers = 2;
456                 }),
457                 Option("|sync", []()
458                 {
459                         s_flip_sync = true;
460                 }),
461                 Option("|cvt=", [&](string s)
462                 {
463                         if (s == "v1")
464                                 s_cvt = true;
465                         else if (s == "v2")
466                                 s_cvt = s_cvt_v2 = true;
467                         else if (s == "v2o")
468                                 s_cvt = s_cvt_v2 = s_cvt_vid_opt = true;
469                         else {
470                                 usage();
471                                 exit(-1);
472                         }
473                 }),
474                 Option("h|help", [&]()
475                 {
476                         usage();
477                         exit(-1);
478                 }),
479         };
481         optionset.parse(argc, argv);
483         if (optionset.params().size() > 0) {
484                 usage();
485                 exit(-1);
486         }
488         return args;
491 static vector<OutputInfo> setups_to_outputs(Card& card, const vector<Arg>& output_args)
493         vector<OutputInfo> outputs;
495         if (output_args.size() == 0) {
496                 // no output args, show a pattern on all screens
497                 for (auto& pipe : card.get_connected_pipelines()) {
498                         OutputInfo output = { };
499                         output.connector = pipe.connector;
500                         output.crtc = pipe.crtc;
501                         output.mode = output.connector->get_default_mode();
503                         output.fbs = get_default_fb(card, output.mode.hdisplay, output.mode.vdisplay);
505                         outputs.push_back(output);
506                 }
508                 return outputs;
509         }
511         OutputInfo* current_output = 0;
512         PlaneInfo* current_plane = 0;
514         for (auto& arg : output_args) {
515                 switch (arg.type) {
516                 case ObjectType::Connector:
517                 {
518                         outputs.push_back(OutputInfo { });
519                         current_output = &outputs.back();
521                         parse_connector(card, arg.arg, *current_output);
522                         current_plane = 0;
524                         break;
525                 }
527                 case ObjectType::Crtc:
528                 {
529                         if (!current_output) {
530                                 outputs.push_back(OutputInfo { });
531                                 current_output = &outputs.back();
532                         }
534                         if (!current_output->connector)
535                                 get_default_connector(card, *current_output);
537                         parse_crtc(card, arg.arg, *current_output);
539                         current_output->user_set_crtc = true;
541                         current_plane = 0;
543                         break;
544                 }
546                 case ObjectType::Plane:
547                 {
548                         if (!current_output) {
549                                 outputs.push_back(OutputInfo { });
550                                 current_output = &outputs.back();
551                         }
553                         if (!current_output->connector)
554                                 get_default_connector(card, *current_output);
556                         if (!current_output->crtc)
557                                 get_default_crtc(card, *current_output);
559                         current_output->planes.push_back(PlaneInfo { });
560                         current_plane = &current_output->planes.back();
562                         parse_plane(card, arg.arg, *current_output, *current_plane);
564                         break;
565                 }
567                 case ObjectType::Framebuffer:
568                 {
569                         if (!current_output) {
570                                 outputs.push_back(OutputInfo { });
571                                 current_output = &outputs.back();
572                         }
574                         if (!current_output->connector)
575                                 get_default_connector(card, *current_output);
577                         if (!current_output->crtc)
578                                 get_default_crtc(card, *current_output);
580                         int def_w, def_h;
582                         if (current_plane) {
583                                 def_w = current_plane->w;
584                                 def_h = current_plane->h;
585                         } else {
586                                 def_w = current_output->mode.hdisplay;
587                                 def_h = current_output->mode.vdisplay;
588                         }
590                         auto fbs = parse_fb(card, arg.arg, def_w, def_h);
592                         if (current_plane)
593                                 current_plane->fbs = fbs;
594                         else
595                                 current_output->fbs = fbs;
597                         break;
598                 }
600                 case ObjectType::View:
601                 {
602                         if (!current_plane || current_plane->fbs.empty())
603                                 EXIT("'view' parameter requires a plane and a fb");
605                         parse_view(arg.arg, *current_plane);
606                         break;
607                 }
608                 }
609         }
611         // create default framebuffers if needed
612         for (OutputInfo& o : outputs) {
613                 if (!o.crtc) {
614                         get_default_crtc(card, o);
615                         o.user_set_crtc = true;
616                 }
618                 if (o.fbs.empty() && o.user_set_crtc)
619                         o.fbs = get_default_fb(card, o.mode.hdisplay, o.mode.vdisplay);
621                 for (PlaneInfo &p : o.planes) {
622                         if (p.fbs.empty())
623                                 p.fbs = get_default_fb(card, p.w, p.h);
624                 }
625         }
627         return outputs;
630 static std::string videomode_to_string(const Videomode& m)
632         string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp());
633         string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp());
635         return sformat("%s %.3f %s %s %u (%.2f) %#x %#x",
636                        m.name.c_str(),
637                        m.clock / 1000.0,
638                        h.c_str(), v.c_str(),
639                        m.vrefresh, m.calculated_vrefresh(),
640                        m.flags,
641                        m.type);
644 static void print_outputs(const vector<OutputInfo>& outputs)
646         for (unsigned i = 0; i < outputs.size(); ++i) {
647                 const OutputInfo& o = outputs[i];
649                 printf("Connector %u/@%u: %s\n", o.connector->idx(), o.connector->id(),
650                        o.connector->fullname().c_str());
651                 printf("  Crtc %u/@%u", o.crtc->idx(), o.crtc->id());
652                 if (o.primary_plane)
653                         printf(" (plane %u/@%u)", o.primary_plane->idx(), o.primary_plane->id());
654                 printf(": %s\n", videomode_to_string(o.mode).c_str());
655                 if (!o.fbs.empty()) {
656                         auto fb = o.fbs[0];
657                         printf("    Fb %u %ux%u-%s\n", fb->id(), fb->width(), fb->height(),
658                                PixelFormatToFourCC(fb->format()).c_str());
659                 }
661                 for (unsigned j = 0; j < o.planes.size(); ++j) {
662                         const PlaneInfo& p = o.planes[j];
663                         auto fb = p.fbs[0];
664                         printf("  Plane %u/@%u: %u,%u-%ux%u\n", p.plane->idx(), p.plane->id(),
665                                p.x, p.y, p.w, p.h);
666                         printf("    Fb %u %ux%u-%s\n", fb->id(), fb->width(), fb->height(),
667                                PixelFormatToFourCC(fb->format()).c_str());
668                 }
669         }
672 static void draw_test_patterns(const vector<OutputInfo>& outputs)
674         for (const OutputInfo& o : outputs) {
675                 for (auto fb : o.fbs)
676                         draw_test_pattern(*fb);
678                 for (const PlaneInfo& p : o.planes)
679                         for (auto fb : p.fbs)
680                                 draw_test_pattern(*fb);
681         }
684 static void set_crtcs_n_planes_legacy(Card& card, const vector<OutputInfo>& outputs)
686         // Disable unused crtcs
687         for (Crtc* crtc : card.get_crtcs()) {
688                 if (find_if(outputs.begin(), outputs.end(), [crtc](const OutputInfo& o) { return o.crtc == crtc; }) != outputs.end())
689                         continue;
691                 crtc->disable_mode();
692         }
694         for (const OutputInfo& o : outputs) {
695                 auto conn = o.connector;
696                 auto crtc = o.crtc;
698                 if (!o.fbs.empty()) {
699                         auto fb = o.fbs[0];
700                         int r = crtc->set_mode(conn, *fb, o.mode);
701                         if (r)
702                                 printf("crtc->set_mode() failed for crtc %u: %s\n",
703                                        crtc->id(), strerror(-r));
704                 }
706                 for (const PlaneInfo& p : o.planes) {
707                         auto fb = p.fbs[0];
708                         int r = crtc->set_plane(p.plane, *fb,
709                                                 p.x, p.y, p.w, p.h,
710                                                 0, 0, fb->width(), fb->height());
711                         if (r)
712                                 printf("crtc->set_plane() failed for plane %u: %s\n",
713                                        p.plane->id(), strerror(-r));
714                 }
715         }
718 static void set_crtcs_n_planes_atomic(Card& card, const vector<OutputInfo>& outputs)
720         int r;
722         // XXX DRM framework doesn't allow moving an active plane from one crtc to another.
723         // See drm_atomic.c::plane_switching_crtc().
724         // For the time being, disable all crtcs and planes here.
726         AtomicReq disable_req(card);
728         // Disable unused crtcs
729         for (Crtc* crtc : card.get_crtcs()) {
730                 //if (find_if(outputs.begin(), outputs.end(), [crtc](const OutputInfo& o) { return o.crtc == crtc; }) != outputs.end())
731                 //      continue;
733                 disable_req.add(crtc, {
734                                 { "ACTIVE", 0 },
735                         });
736         }
738         // Disable unused planes
739         for (Plane* plane : card.get_planes()) {
740                 //if (find_if(outputs.begin(), outputs.end(), [plane](const OutputInfo& o) { return o.primary_plane == plane; }) != outputs.end())
741                 //      continue;
743                 disable_req.add(plane, {
744                                 { "FB_ID", 0 },
745                                 { "CRTC_ID", 0 },
746                         });
747         }
749         r = disable_req.commit_sync(true);
750         if (r)
751                 EXIT("Atomic commit failed when disabling: %d\n", r);
754         // Keep blobs here so that we keep ref to them until we have committed the req
755         vector<unique_ptr<Blob>> blobs;
757         AtomicReq req(card);
759         for (const OutputInfo& o : outputs) {
760                 auto conn = o.connector;
761                 auto crtc = o.crtc;
763                 blobs.emplace_back(o.mode.to_blob(card));
764                 Blob* mode_blob = blobs.back().get();
766                 req.add(conn, {
767                                 { "CRTC_ID", crtc->id() },
768                         });
770                 req.add(crtc, {
771                                 { "ACTIVE", 1 },
772                                 { "MODE_ID", mode_blob->id() },
773                         });
775                 if (!o.fbs.empty()) {
776                         auto fb = o.fbs[0];
778                         req.add(o.primary_plane, {
779                                         { "FB_ID", fb->id() },
780                                         { "CRTC_ID", crtc->id() },
781                                         { "SRC_X", 0 << 16 },
782                                         { "SRC_Y", 0 << 16 },
783                                         { "SRC_W", fb->width() << 16 },
784                                         { "SRC_H", fb->height() << 16 },
785                                         { "CRTC_X", 0 },
786                                         { "CRTC_Y", 0 },
787                                         { "CRTC_W", fb->width() },
788                                         { "CRTC_H", fb->height() },
789                                 });
790                 }
792                 for (const PlaneInfo& p : o.planes) {
793                         auto fb = p.fbs[0];
795                         req.add(p.plane, {
796                                         { "FB_ID", fb->id() },
797                                         { "CRTC_ID", crtc->id() },
798                                         { "SRC_X", (p.view_x ?: 0) << 16 },
799                                         { "SRC_Y", (p.view_y ?: 0) << 16 },
800                                         { "SRC_W", (p.view_w ?: fb->width()) << 16 },
801                                         { "SRC_H", (p.view_h ?: fb->height()) << 16 },
802                                         { "CRTC_X", p.x },
803                                         { "CRTC_Y", p.y },
804                                         { "CRTC_W", p.w },
805                                         { "CRTC_H", p.h },
806                                 });
807                 }
808         }
810         r = req.test(true);
811         if (r)
812                 EXIT("Atomic test failed: %d\n", r);
814         r = req.commit_sync(true);
815         if (r)
816                 EXIT("Atomic commit failed: %d\n", r);
819 static void set_crtcs_n_planes(Card& card, const vector<OutputInfo>& outputs)
821         if (card.has_atomic())
822                 set_crtcs_n_planes_atomic(card, outputs);
823         else
824                 set_crtcs_n_planes_legacy(card, outputs);
827 class FlipState : private PageFlipHandlerBase
829 public:
830         FlipState(Card& card, const string& name, vector<const OutputInfo*> outputs)
831                 : m_card(card), m_name(name), m_outputs(outputs)
832         {
833         }
835         void start_flipping()
836         {
837                 m_prev_frame = m_prev_print = std::chrono::steady_clock::now();
838                 m_slowest_frame = std::chrono::duration<float>::min();
839                 m_frame_num = 0;
840                 queue_next();
841         }
843 private:
844         void handle_page_flip(uint32_t frame, double time)
845         {
846                 m_frame_num++;
848                 auto now = std::chrono::steady_clock::now();
850                 std::chrono::duration<float> diff = now - m_prev_frame;
851                 if (diff > m_slowest_frame)
852                         m_slowest_frame = diff;
854                 if (m_frame_num  % 100 == 0) {
855                         std::chrono::duration<float> fsec = now - m_prev_print;
856                         printf("Connector %s: fps %f, slowest %.2f ms\n",
857                                m_name.c_str(),
858                                100.0 / fsec.count(),
859                                m_slowest_frame.count() * 1000);
860                         m_prev_print = now;
861                         m_slowest_frame = std::chrono::duration<float>::min();
862                 }
864                 m_prev_frame = now;
866                 queue_next();
867         }
869         static unsigned get_bar_pos(MappedFramebuffer* fb, unsigned frame_num)
870         {
871                 return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
872         }
874         static void draw_bar(MappedFramebuffer* fb, unsigned frame_num)
875         {
876                 int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
877                 int new_xpos = get_bar_pos(fb, frame_num);
879                 draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
880                 draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
881         }
883         static void do_flip_output(AtomicReq& req, unsigned frame_num, const OutputInfo& o)
884         {
885                 unsigned cur = frame_num % s_num_buffers;
887                 if (!o.fbs.empty()) {
888                         auto fb = o.fbs[cur];
890                         draw_bar(fb, frame_num);
892                         req.add(o.primary_plane, {
893                                         { "FB_ID", fb->id() },
894                                 });
895                 }
897                 for (const PlaneInfo& p : o.planes) {
898                         auto fb = p.fbs[cur];
900                         draw_bar(fb, frame_num);
902                         req.add(p.plane, {
903                                         { "FB_ID", fb->id() },
904                                 });
905                 }
906         }
908         void do_flip_output_legacy(unsigned frame_num, const OutputInfo& o)
909         {
910                 unsigned cur = frame_num % s_num_buffers;
912                 if (!o.fbs.empty()) {
913                         auto fb = o.fbs[cur];
915                         draw_bar(fb, frame_num);
917                         int r = o.crtc->page_flip(*fb, this);
918                         ASSERT(r == 0);
919                 }
921                 for (const PlaneInfo& p : o.planes) {
922                         auto fb = p.fbs[cur];
924                         draw_bar(fb, frame_num);
926                         int r = o.crtc->set_plane(p.plane, *fb,
927                                                   p.x, p.y, p.w, p.h,
928                                                   0, 0, fb->width(), fb->height());
929                         ASSERT(r == 0);
930                 }
931         }
933         void queue_next()
934         {
935                 if (m_card.has_atomic()) {
936                         AtomicReq req(m_card);
938                         for (auto o : m_outputs)
939                                 do_flip_output(req, m_frame_num, *o);
941                         int r = req.commit(this);
942                         if (r)
943                                 EXIT("Flip commit failed: %d\n", r);
944                 } else {
945                         ASSERT(m_outputs.size() == 1);
946                         do_flip_output_legacy(m_frame_num, *m_outputs[0]);
947                 }
948         }
950         Card& m_card;
951         string m_name;
952         vector<const OutputInfo*> m_outputs;
953         unsigned m_frame_num;
955         chrono::steady_clock::time_point m_prev_print;
956         chrono::steady_clock::time_point m_prev_frame;
957         chrono::duration<float> m_slowest_frame;
959         static const unsigned bar_width = 20;
960         static const unsigned bar_speed = 8;
961 };
963 static void main_flip(Card& card, const vector<OutputInfo>& outputs)
965         fd_set fds;
967         FD_ZERO(&fds);
969         int fd = card.fd();
971         vector<unique_ptr<FlipState>> flipstates;
973         if (!s_flip_sync) {
974                 for (const OutputInfo& o : outputs) {
975                         auto fs = unique_ptr<FlipState>(new FlipState(card, to_string(o.connector->idx()), { &o }));
976                         flipstates.push_back(move(fs));
977                 }
978         } else {
979                 vector<const OutputInfo*> ois;
981                 string name;
982                 for (const OutputInfo& o : outputs) {
983                         name += to_string(o.connector->idx()) + ",";
984                         ois.push_back(&o);
985                 }
987                 auto fs = unique_ptr<FlipState>(new FlipState(card, name, ois));
988                 flipstates.push_back(move(fs));
989         }
991         for (unique_ptr<FlipState>& fs : flipstates)
992                 fs->start_flipping();
994         while (true) {
995                 int r;
997                 FD_SET(0, &fds);
998                 FD_SET(fd, &fds);
1000                 r = select(fd + 1, &fds, NULL, NULL, NULL);
1001                 if (r < 0) {
1002                         fprintf(stderr, "select() failed with %d: %m\n", errno);
1003                         break;
1004                 } else if (FD_ISSET(0, &fds)) {
1005                         fprintf(stderr, "Exit due to user-input\n");
1006                         break;
1007                 } else if (FD_ISSET(fd, &fds)) {
1008                         card.call_page_flip_handlers();
1009                 }
1010         }
1013 int main(int argc, char **argv)
1015         vector<Arg> output_args = parse_cmdline(argc, argv);
1017         Card card(s_device_path);
1019         if (!card.has_atomic() && s_flip_sync)
1020                 EXIT("Synchronized flipping requires atomic modesetting");
1022         vector<OutputInfo> outputs = setups_to_outputs(card, output_args);
1024         ResourceManager resman(card);
1026         if (card.has_atomic()) {
1027                 for (OutputInfo& o : outputs) {
1028                         if (o.fbs.empty())
1029                                 continue;
1031                         o.primary_plane = resman.reserve_primary_plane(o.crtc);
1033                         if (!o.primary_plane)
1034                                 EXIT("Could not get primary plane for crtc '%u'", o.crtc->id());
1035                 }
1036         }
1038         if (!s_flip_mode)
1039                 draw_test_patterns(outputs);
1041         print_outputs(outputs);
1043         set_crtcs_n_planes(card, outputs);
1045         printf("press enter to exit\n");
1047         if (s_flip_mode)
1048                 main_flip(card, outputs);
1049         else
1050                 getchar();