]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android/external-libkmsxx.git/blobdiff - utils/testpat.cpp
cmake: results to bin & lib dirs
[android/external-libkmsxx.git] / utils / testpat.cpp
index cb2fe4124da8d9d3f8200fc6f886ff04714c2d80..094e22a87d521f86f9728f01406e4a6e70d0366d 100644 (file)
@@ -3,6 +3,7 @@
 #include <algorithm>
 #include <regex>
 #include <set>
+#include <chrono>
 
 #include <kms++.h>
 #include <modedb.h>
@@ -22,7 +23,7 @@ struct PlaneInfo
        unsigned w;
        unsigned h;
 
-       DumbFramebuffer* fb;
+       vector<DumbFramebuffer*> fbs;
 };
 
 struct OutputInfo
@@ -30,15 +31,19 @@ struct OutputInfo
        Connector* connector;
 
        Crtc* crtc;
+       Plane* primary_plane;
        Videomode mode;
        bool user_set_crtc;
-       DumbFramebuffer* fb;
+       vector<DumbFramebuffer*> fbs;
 
        vector<PlaneInfo> planes;
 };
 
 static bool s_use_dmt;
 static bool s_use_cea;
+static unsigned s_num_buffers = 1;
+static bool s_flip_mode;
+static bool s_flip_sync;
 
 static set<Crtc*> s_used_crtcs;
 static set<Plane*> s_used_planes;
@@ -60,36 +65,7 @@ static void get_default_connector(Card& card, OutputInfo& output)
 
 static void parse_connector(Card& card, const string& str, OutputInfo& output)
 {
-       Connector* conn = nullptr;
-
-       auto connectors = card.get_connectors();
-
-       if (str[0] == '@') {
-               char* endptr;
-               unsigned idx = strtoul(str.c_str() + 1, &endptr, 10);
-               if (*endptr == 0) {
-                       if (idx >= connectors.size())
-                               EXIT("Bad connector number '%u'", idx);
-
-                       conn = connectors[idx];
-               }
-       } else {
-               char* endptr;
-               unsigned id = strtoul(str.c_str(), &endptr, 10);
-               if (*endptr == 0) {
-                       Connector* c = card.get_connector(id);
-                       if (!c)
-                               EXIT("Bad connector id '%u'", id);
-
-                       conn = c;
-               }
-       }
-
-       if (!conn) {
-               auto iter = find_if(connectors.begin(), connectors.end(), [&str](Connector *c) { return c->fullname() == str; });
-               if (iter != connectors.end())
-                       conn = *iter;
-       }
+       Connector* conn = resolve_connector(card, str);
 
        if (!conn)
                EXIT("No connector '%s'", str.c_str());
@@ -132,22 +108,22 @@ static void parse_crtc(Card& card, const string& crtc_str, OutputInfo& output)
                EXIT("Failed to parse crtc option '%s'", crtc_str.c_str());
 
        if (sm[2].matched) {
-               bool use_idx = sm[1].length() == 1;
+               bool use_id = sm[1].length() == 1;
                unsigned num = stoul(sm[2].str());
 
-               if (use_idx) {
+               if (use_id) {
+                       Crtc* c = card.get_crtc(num);
+                       if (!c)
+                               EXIT("Bad crtc id '%u'", num);
+
+                       output.crtc = c;
+               } else {
                        auto crtcs = card.get_crtcs();
 
                        if (num >= crtcs.size())
                                EXIT("Bad crtc number '%u'", num);
 
                        output.crtc = crtcs[num];
-               } else {
-                       Crtc* c = card.get_crtc(num);
-                       if (!c)
-                               EXIT("Bad crtc id '%u'", num);
-
-                       output.crtc = c;
                }
        } else {
                output.crtc = output.connector->get_current_crtc();
@@ -195,22 +171,22 @@ static void parse_plane(Card& card, const string& plane_str, const OutputInfo& o
                EXIT("Failed to parse plane option '%s'", plane_str.c_str());
 
        if (sm[2].matched) {
-               bool use_idx = sm[1].length() == 1;
+               bool use_id = sm[1].length() == 1;
                unsigned num = stoul(sm[2].str());
 
-               if (use_idx) {
+               if (use_id) {
+                       Plane* p = card.get_plane(num);
+                       if (!p)
+                               EXIT("Bad plane id '%u'", num);
+
+                       pinfo.plane = p;
+               } else {
                        auto planes = card.get_planes();
 
                        if (num >= planes.size())
                                EXIT("Bad plane number '%u'", num);
 
                        pinfo.plane = planes[num];
-               } else {
-                       Plane* p = card.get_plane(num);
-                       if (!p)
-                               EXIT("Bad plane id '%u'", num);
-
-                       pinfo.plane = p;
                }
        } else {
                for (Plane* p : output.crtc->get_possible_planes()) {
@@ -243,14 +219,17 @@ static void parse_plane(Card& card, const string& plane_str, const OutputInfo& o
                pinfo.y = output.mode.vdisplay / 2 - pinfo.h / 2;
 }
 
-static DumbFramebuffer* get_default_fb(Card& card, unsigned width, unsigned height)
+static vector<DumbFramebuffer*> get_default_fb(Card& card, unsigned width, unsigned height)
 {
-       auto fb = new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888);
-       draw_test_pattern(*fb);
-       return fb;
+       vector<DumbFramebuffer*> v;
+
+       for (unsigned i = 0; i < s_num_buffers; ++i)
+               v.push_back(new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888));
+
+       return v;
 }
 
-static DumbFramebuffer* parse_fb(Card& card, const string& fb_str, unsigned def_w, unsigned def_h)
+static vector<DumbFramebuffer*> parse_fb(Card& card, const string& fb_str, unsigned def_w, unsigned def_h)
 {
        unsigned w = def_w;
        unsigned h = def_h;
@@ -273,9 +252,12 @@ static DumbFramebuffer* parse_fb(Card& card, const string& fb_str, unsigned def_
                        format = FourCCToPixelFormat(sm[3]);
        }
 
-       auto fb = new DumbFramebuffer(card, w, h, format);
-       draw_test_pattern(*fb);
-       return fb;
+       vector<DumbFramebuffer*> v;
+
+       for (unsigned i = 0; i < s_num_buffers; ++i)
+               v.push_back(new DumbFramebuffer(card, w, h, format));
+
+       return v;
 }
 
 static const char* usage_str =
@@ -289,8 +271,10 @@ static const char* usage_str =
                "  -f, --fb=FB               FB is [<w>x<h>][-][<4cc>]\n"
                "      --dmt                 Search for the given mode from DMT tables\n"
                "      --cea                 Search for the given mode from CEA tables\n"
+               "      --flip                Do page flipping for each output\n"
+               "      --sync                Synchronize page flipping\n"
                "\n"
-               "<connector>, <crtc> and <plane> can be given by id (<id>) or index (@<idx>).\n"
+               "<connector>, <crtc> and <plane> can be given by index (<idx>) or id (<id>).\n"
                "<connector> can also be given by name.\n"
                "\n"
                "Options can be given multiple times to set up multiple displays or planes.\n"
@@ -307,7 +291,7 @@ static const char* usage_str =
                "XR24 framebuffer on a 400x400 plane on the first connected connector in the default mode:\n"
                "    testpat -p 400x400 -f XR24\n\n"
                "Test pattern on the second connector with default mode:\n"
-               "    testpat -c @1\n"
+               "    testpat -c 1\n"
                ;
 
 static void usage()
@@ -366,6 +350,15 @@ static vector<Arg> parse_cmdline(int argc, char **argv)
                {
                        s_use_cea = true;
                }),
+               Option("|flip", []()
+               {
+                       s_flip_mode = true;
+                       s_num_buffers = 2;
+               }),
+               Option("|sync", []()
+               {
+                       s_flip_sync = true;
+               }),
                Option("h|help", [&]()
                {
                        usage();
@@ -395,7 +388,7 @@ static vector<OutputInfo> setups_to_outputs(Card& card, const vector<Arg>& outpu
                        output.crtc = pipe.crtc;
                        output.mode = output.connector->get_default_mode();
 
-                       output.fb = get_default_fb(card, output.mode.hdisplay, output.mode.vdisplay);
+                       output.fbs = get_default_fb(card, output.mode.hdisplay, output.mode.vdisplay);
 
                        outputs.push_back(output);
                }
@@ -482,12 +475,12 @@ static vector<OutputInfo> setups_to_outputs(Card& card, const vector<Arg>& outpu
                                def_h = current_output->mode.vdisplay;
                        }
 
-                       auto fb = parse_fb(card, arg.arg, def_w, def_h);
+                       auto fbs = parse_fb(card, arg.arg, def_w, def_h);
 
                        if (current_plane)
-                               current_plane->fb = fb;
+                               current_plane->fbs = fbs;
                        else
-                               current_output->fb = fb;
+                               current_output->fbs = fbs;
 
                        break;
                }
@@ -501,12 +494,12 @@ static vector<OutputInfo> setups_to_outputs(Card& card, const vector<Arg>& outpu
                        o.user_set_crtc = true;
                }
 
-               if (!o.fb && o.user_set_crtc)
-                       o.fb = get_default_fb(card, o.mode.hdisplay, o.mode.vdisplay);
+               if (o.fbs.empty() && o.user_set_crtc)
+                       o.fbs = get_default_fb(card, o.mode.hdisplay, o.mode.vdisplay);
 
                for (PlaneInfo &p : o.planes) {
-                       if (!p.fb)
-                               p.fb = get_default_fb(card, p.w, p.h);
+                       if (p.fbs.empty())
+                               p.fbs = get_default_fb(card, p.w, p.h);
                }
        }
 
@@ -545,40 +538,60 @@ static void print_outputs(const vector<OutputInfo>& outputs)
 
                printf("Connector %u/@%u: %s\n", o.connector->id(), o.connector->idx(),
                       o.connector->fullname().c_str());
-               printf("  Crtc %u/@%u: %ux%u-%u (%s)\n", o.crtc->id(), o.crtc->idx(),
+               printf("  Crtc %u/@%u", o.crtc->id(), o.crtc->idx());
+               if (o.primary_plane)
+                       printf(" (plane %u/@%u)", o.primary_plane->id(), o.primary_plane->idx());
+               printf(": %ux%u-%u (%s)\n",
                       o.mode.hdisplay, o.mode.vdisplay, o.mode.vrefresh,
                       videomode_to_string(o.mode).c_str());
-               if (o.fb)
-                       printf("    Fb %ux%u-%s\n", o.fb->width(), o.fb->height(),
-                              PixelFormatToFourCC(o.fb->format()).c_str());
+               if (!o.fbs.empty()) {
+                       auto fb = o.fbs[0];
+                       printf("    Fb %u %ux%u-%s\n", fb->id(), fb->width(), fb->height(),
+                              PixelFormatToFourCC(fb->format()).c_str());
+               }
 
                for (unsigned j = 0; j < o.planes.size(); ++j) {
                        const PlaneInfo& p = o.planes[j];
+                       auto fb = p.fbs[0];
                        printf("  Plane %u/@%u: %u,%u-%ux%u\n", p.plane->id(), p.plane->idx(),
                               p.x, p.y, p.w, p.h);
-                       printf("    Fb %ux%u-%s\n", p.fb->width(), p.fb->height(),
-                              PixelFormatToFourCC(p.fb->format()).c_str());
+                       printf("    Fb %u %ux%u-%s\n", fb->id(), fb->width(), fb->height(),
+                              PixelFormatToFourCC(fb->format()).c_str());
                }
        }
 }
 
-static void set_crtcs_n_planes(Card& card, const vector<OutputInfo>& outputs)
+static void draw_test_patterns(const vector<OutputInfo>& outputs)
+{
+       for (const OutputInfo& o : outputs) {
+               for (auto fb : o.fbs)
+                       draw_test_pattern(*fb);
+
+               for (const PlaneInfo& p : o.planes)
+                       for (auto fb : p.fbs)
+                               draw_test_pattern(*fb);
+       }
+}
+
+static void set_crtcs_n_planes_legacy(Card& card, const vector<OutputInfo>& outputs)
 {
        for (const OutputInfo& o : outputs) {
                auto conn = o.connector;
                auto crtc = o.crtc;
 
-               if (o.fb) {
-                       int r = crtc->set_mode(conn, *o.fb, o.mode);
+               if (!o.fbs.empty()) {
+                       auto fb = o.fbs[0];
+                       int r = crtc->set_mode(conn, *fb, o.mode);
                        if (r)
                                printf("crtc->set_mode() failed for crtc %u: %s\n",
                                       crtc->id(), strerror(-r));
                }
 
                for (const PlaneInfo& p : o.planes) {
-                       int r = crtc->set_plane(p.plane, *p.fb,
+                       auto fb = p.fbs[0];
+                       int r = crtc->set_plane(p.plane, *fb,
                                                p.x, p.y, p.w, p.h,
-                                               0, 0, p.fb->width(), p.fb->height());
+                                               0, 0, fb->width(), fb->height());
                        if (r)
                                printf("crtc->set_plane() failed for plane %u: %s\n",
                                       p.plane->id(), strerror(-r));
@@ -586,19 +599,295 @@ static void set_crtcs_n_planes(Card& card, const vector<OutputInfo>& outputs)
        }
 }
 
+static void set_crtcs_n_planes(Card& card, const vector<OutputInfo>& outputs)
+{
+       // Keep blobs here so that we keep ref to them until we have committed the req
+       vector<unique_ptr<Blob>> blobs;
+
+       AtomicReq req(card);
+
+       for (const OutputInfo& o : outputs) {
+               auto conn = o.connector;
+               auto crtc = o.crtc;
+
+               if (!o.fbs.empty()) {
+                       auto fb = o.fbs[0];
+
+                       blobs.emplace_back(o.mode.to_blob(card));
+                       Blob* mode_blob = blobs.back().get();
+
+                       req.add(conn, {
+                                       { "CRTC_ID", crtc->id() },
+                               });
+
+                       req.add(crtc, {
+                                       { "ACTIVE", 1 },
+                                       { "MODE_ID", mode_blob->id() },
+                               });
+
+                       req.add(o.primary_plane, {
+                                       { "FB_ID", fb->id() },
+                                       { "CRTC_ID", crtc->id() },
+                                       { "SRC_X", 0 << 16 },
+                                       { "SRC_Y", 0 << 16 },
+                                       { "SRC_W", fb->width() << 16 },
+                                       { "SRC_H", fb->height() << 16 },
+                                       { "CRTC_X", 0 },
+                                       { "CRTC_Y", 0 },
+                                       { "CRTC_W", fb->width() },
+                                       { "CRTC_H", fb->height() },
+                               });
+               }
+
+               for (const PlaneInfo& p : o.planes) {
+                       auto fb = p.fbs[0];
+
+                       req.add(p.plane, {
+                                       { "FB_ID", fb->id() },
+                                       { "CRTC_ID", crtc->id() },
+                                       { "SRC_X", 0 << 16 },
+                                       { "SRC_Y", 0 << 16 },
+                                       { "SRC_W", fb->width() << 16 },
+                                       { "SRC_H", fb->height() << 16 },
+                                       { "CRTC_X", p.x },
+                                       { "CRTC_Y", p.y },
+                                       { "CRTC_W", p.w },
+                                       { "CRTC_H", p.h },
+                               });
+               }
+       }
+
+       int r;
+
+       r = req.test(true);
+       if (r)
+               EXIT("Atomic test failed: %d\n", r);
+
+       r = req.commit_sync(true);
+       if (r)
+               EXIT("Atomic commit failed: %d\n", r);
+}
+
+class FlipState : private PageFlipHandlerBase
+{
+public:
+       FlipState(Card& card, const string& name, vector<const OutputInfo*> outputs)
+               : m_card(card), m_name(name), m_outputs(outputs)
+       {
+       }
+
+       void start_flipping()
+       {
+               m_prev_frame = m_prev_print = std::chrono::steady_clock::now();
+               m_slowest_frame = std::chrono::duration<float>::min();
+               m_frame_num = 0;
+               queue_next();
+       }
+
+private:
+       void handle_page_flip(uint32_t frame, double time)
+       {
+               m_frame_num++;
+
+               auto now = std::chrono::steady_clock::now();
+
+               std::chrono::duration<float> diff = now - m_prev_frame;
+               if (diff > m_slowest_frame)
+                       m_slowest_frame = diff;
+
+               if (m_frame_num  % 100 == 0) {
+                       std::chrono::duration<float> fsec = now - m_prev_print;
+                       printf("Connector %s: fps %f, slowest %.2f ms\n",
+                              m_name.c_str(),
+                              100.0 / fsec.count(),
+                              m_slowest_frame.count() * 1000);
+                       m_prev_print = now;
+                       m_slowest_frame = std::chrono::duration<float>::min();
+               }
+
+               m_prev_frame = now;
+
+               queue_next();
+       }
+
+       static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num)
+       {
+               return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
+       }
+
+       static void draw_bar(DumbFramebuffer* fb, unsigned frame_num)
+       {
+               int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
+               int new_xpos = get_bar_pos(fb, frame_num);
+
+               draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
+               draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
+       }
+
+       static void do_flip_output(AtomicReq& req, unsigned frame_num, const OutputInfo& o)
+       {
+               unsigned cur = frame_num % s_num_buffers;
+
+               if (!o.fbs.empty()) {
+                       auto fb = o.fbs[cur];
+
+                       draw_bar(fb, frame_num);
+
+                       req.add(o.primary_plane, {
+                                       { "FB_ID", fb->id() },
+                               });
+               }
+
+               for (const PlaneInfo& p : o.planes) {
+                       auto fb = p.fbs[cur];
+
+                       draw_bar(fb, frame_num);
+
+                       req.add(p.plane, {
+                                       { "FB_ID", fb->id() },
+                               });
+               }
+       }
+
+       void do_flip_output_legacy(unsigned frame_num, const OutputInfo& o)
+       {
+               unsigned cur = frame_num % s_num_buffers;
+
+               if (!o.fbs.empty()) {
+                       auto fb = o.fbs[cur];
+
+                       draw_bar(fb, frame_num);
+
+                       int r = o.crtc->page_flip(*fb, this);
+                       ASSERT(r == 0);
+               }
+
+               for (const PlaneInfo& p : o.planes) {
+                       auto fb = p.fbs[cur];
+
+                       draw_bar(fb, frame_num);
+
+                       int r = o.crtc->set_plane(p.plane, *fb,
+                                                 p.x, p.y, p.w, p.h,
+                                                 0, 0, fb->width(), fb->height());
+                       ASSERT(r == 0);
+               }
+       }
+
+       void queue_next()
+       {
+               if (m_card.has_atomic()) {
+                       AtomicReq req(m_card);
+
+                       for (auto o : m_outputs)
+                               do_flip_output(req, m_frame_num, *o);
+
+                       int r = req.commit(this);
+                       if (r)
+                               EXIT("Flip commit failed: %d\n", r);
+               } else {
+                       ASSERT(m_outputs.size() == 1);
+                       do_flip_output_legacy(m_frame_num, *m_outputs[0]);
+               }
+       }
+
+       Card& m_card;
+       string m_name;
+       vector<const OutputInfo*> m_outputs;
+       unsigned m_frame_num;
+
+       chrono::steady_clock::time_point m_prev_print;
+       chrono::steady_clock::time_point m_prev_frame;
+       chrono::duration<float> m_slowest_frame;
+
+       static const unsigned bar_width = 20;
+       static const unsigned bar_speed = 8;
+};
+
+static void main_flip(Card& card, const vector<OutputInfo>& outputs)
+{
+       fd_set fds;
+
+       FD_ZERO(&fds);
+
+       int fd = card.fd();
+
+       vector<unique_ptr<FlipState>> flipstates;
+
+       if (!s_flip_sync) {
+               for (const OutputInfo& o : outputs) {
+                       auto fs = unique_ptr<FlipState>(new FlipState(card, to_string(o.connector->idx()), { &o }));
+                       flipstates.push_back(move(fs));
+               }
+       } else {
+               vector<const OutputInfo*> ois;
+
+               string name;
+               for (const OutputInfo& o : outputs) {
+                       name += to_string(o.connector->idx()) + ",";
+                       ois.push_back(&o);
+               }
+
+               auto fs = unique_ptr<FlipState>(new FlipState(card, name, ois));
+               flipstates.push_back(move(fs));
+       }
+
+       for (unique_ptr<FlipState>& fs : flipstates)
+               fs->start_flipping();
+
+       while (true) {
+               int r;
+
+               FD_SET(0, &fds);
+               FD_SET(fd, &fds);
+
+               r = select(fd + 1, &fds, NULL, NULL, NULL);
+               if (r < 0) {
+                       fprintf(stderr, "select() failed with %d: %m\n", errno);
+                       break;
+               } else if (FD_ISSET(0, &fds)) {
+                       fprintf(stderr, "Exit due to user-input\n");
+                       break;
+               } else if (FD_ISSET(fd, &fds)) {
+                       card.call_page_flip_handlers();
+               }
+       }
+}
+
 int main(int argc, char **argv)
 {
        vector<Arg> output_args = parse_cmdline(argc, argv);
 
        Card card(s_device_path);
 
+       if (!card.has_atomic() && s_flip_sync)
+               EXIT("Synchronized flipping requires atomic modesetting");
+
        vector<OutputInfo> outputs = setups_to_outputs(card, output_args);
 
+       if (card.has_atomic()) {
+               for (OutputInfo& o : outputs) {
+                       o.primary_plane = o.crtc->get_primary_plane();
+
+                       if (!o.fbs.empty() && !o.primary_plane)
+                               EXIT("Could not get primary plane for crtc '%u'", o.crtc->id());
+               }
+       }
+
+       if (!s_flip_mode)
+               draw_test_patterns(outputs);
+
        print_outputs(outputs);
 
-       set_crtcs_n_planes(card, outputs);
+       if (card.has_atomic())
+               set_crtcs_n_planes(card, outputs);
+       else
+               set_crtcs_n_planes_legacy(card, outputs);
 
        printf("press enter to exit\n");
 
-       getchar();
+       if (s_flip_mode)
+               main_flip(card, outputs);
+       else
+               getchar();
 }