From 227830e15bc192f26c82a0cfe4a3ded358f0c074 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 16 Aug 2016 15:38:44 +0300 Subject: rename testpat and fbtestpat --- utils/CMakeLists.txt | 10 +- utils/fbtest.cpp | 58 ++++ utils/fbtestpat.cpp | 58 ---- utils/kmstest.cpp | 881 +++++++++++++++++++++++++++++++++++++++++++++++++++ utils/testpat.cpp | 881 --------------------------------------------------- 5 files changed, 944 insertions(+), 944 deletions(-) create mode 100644 utils/fbtest.cpp delete mode 100644 utils/fbtestpat.cpp create mode 100644 utils/kmstest.cpp delete mode 100644 utils/testpat.cpp (limited to 'utils') diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 0b15481..f79b207 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,8 +1,8 @@ include_directories(${LIBDRM_INCLUDE_DIRS}) link_directories(${LIBDRM_LIBRARY_DIRS}) -add_executable (testpat testpat.cpp) -target_link_libraries(testpat kms++ kms++util ${LIBDRM_LIBRARIES}) +add_executable (kmstest kmstest.cpp) +target_link_libraries(kmstest kms++ kms++util ${LIBDRM_LIBRARIES}) add_executable (kmsview kmsview.cpp) target_link_libraries(kmsview kms++ kms++util ${LIBDRM_LIBRARIES}) @@ -10,8 +10,8 @@ target_link_libraries(kmsview kms++ kms++util ${LIBDRM_LIBRARIES}) add_executable (kmsprint kmsprint.cpp) target_link_libraries(kmsprint kms++ kms++util ${LIBDRM_LIBRARIES}) -add_executable (fbtestpat fbtestpat.cpp) -target_link_libraries(fbtestpat kms++util) +add_executable (fbtest fbtest.cpp) +target_link_libraries(fbtest kms++util) add_executable (kmscapture kmscapture.cpp) target_link_libraries(kmscapture kms++ kms++util ${LIBDRM_LIBRARIES}) @@ -25,5 +25,5 @@ target_link_libraries(wbcap kms++ kms++util ${LIBDRM_LIBRARIES}) add_executable (wbm2m wbm2m.cpp) target_link_libraries(wbm2m kms++ kms++util ${LIBDRM_LIBRARIES}) -install(TARGETS testpat kmsprint fbtestpat +install(TARGETS kmstest kmsprint fbtest DESTINATION bin) diff --git a/utils/fbtest.cpp b/utils/fbtest.cpp new file mode 100644 index 0000000..1c9a5f1 --- /dev/null +++ b/utils/fbtest.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +using namespace kms; + +int main(int argc, char** argv) +{ + const char* fbdev = "/dev/fb0"; + int r; + + int fd = open(fbdev, O_RDWR); + FAIL_IF(fd < 0, "open %s failed\n", fbdev); + + struct fb_var_screeninfo var; + + r = ioctl(fd, FBIOGET_VSCREENINFO, &var); + FAIL_IF(r, "FBIOGET_VSCREENINFO failed"); + + struct fb_fix_screeninfo fix; + + r = ioctl(fd, FBIOGET_FSCREENINFO, &fix); + FAIL_IF(r, "FBIOGET_FSCREENINFO failed"); + + uint8_t* ptr = (uint8_t*)mmap(NULL, + var.yres_virtual * fix.line_length, + PROT_WRITE | PROT_READ, + MAP_SHARED, fd, 0); + + FAIL_IF(ptr == MAP_FAILED, "mmap failed"); + + ExtCPUFramebuffer buf(var.xres, var.yres, PixelFormat::XRGB8888, + ptr, var.yres_virtual * fix.line_length, fix.line_length, 0); + + printf("%s: res %dx%d, virtual %dx%d, line_len %d\n", + fbdev, + var.xres, var.yres, + var.xres_virtual, var.yres_virtual, + fix.line_length); + + draw_test_pattern(buf); + draw_text(buf, buf.width() / 2, 0, fbdev, RGB(255, 255, 255)); + + close(fd); + + return 0; +} diff --git a/utils/fbtestpat.cpp b/utils/fbtestpat.cpp deleted file mode 100644 index 1c9a5f1..0000000 --- a/utils/fbtestpat.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -using namespace kms; - -int main(int argc, char** argv) -{ - const char* fbdev = "/dev/fb0"; - int r; - - int fd = open(fbdev, O_RDWR); - FAIL_IF(fd < 0, "open %s failed\n", fbdev); - - struct fb_var_screeninfo var; - - r = ioctl(fd, FBIOGET_VSCREENINFO, &var); - FAIL_IF(r, "FBIOGET_VSCREENINFO failed"); - - struct fb_fix_screeninfo fix; - - r = ioctl(fd, FBIOGET_FSCREENINFO, &fix); - FAIL_IF(r, "FBIOGET_FSCREENINFO failed"); - - uint8_t* ptr = (uint8_t*)mmap(NULL, - var.yres_virtual * fix.line_length, - PROT_WRITE | PROT_READ, - MAP_SHARED, fd, 0); - - FAIL_IF(ptr == MAP_FAILED, "mmap failed"); - - ExtCPUFramebuffer buf(var.xres, var.yres, PixelFormat::XRGB8888, - ptr, var.yres_virtual * fix.line_length, fix.line_length, 0); - - printf("%s: res %dx%d, virtual %dx%d, line_len %d\n", - fbdev, - var.xres, var.yres, - var.xres_virtual, var.yres_virtual, - fix.line_length); - - draw_test_pattern(buf); - draw_text(buf, buf.width() / 2, 0, fbdev, RGB(255, 255, 255)); - - close(fd); - - return 0; -} diff --git a/utils/kmstest.cpp b/utils/kmstest.cpp new file mode 100644 index 0000000..ccddccb --- /dev/null +++ b/utils/kmstest.cpp @@ -0,0 +1,881 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +using namespace std; +using namespace kms; + +struct PlaneInfo +{ + Plane* plane; + + unsigned x; + unsigned y; + unsigned w; + unsigned h; + + vector fbs; +}; + +struct OutputInfo +{ + Connector* connector; + + Crtc* crtc; + Plane* primary_plane; + Videomode mode; + bool user_set_crtc; + vector fbs; + + vector 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 s_used_crtcs; +static set s_used_planes; + +__attribute__ ((unused)) +static void print_regex_match(smatch sm) +{ + for (unsigned i = 0; i < sm.size(); ++i) { + string str = sm[i].str(); + printf("%u: %s\n", i, str.c_str()); + } +} + +static void get_default_connector(Card& card, OutputInfo& output) +{ + output.connector = card.get_first_connected_connector(); + output.mode = output.connector->get_default_mode(); +} + +static void parse_connector(Card& card, const string& str, OutputInfo& output) +{ + Connector* conn = resolve_connector(card, str); + + if (!conn) + EXIT("No connector '%s'", str.c_str()); + + if (!conn->connected()) + EXIT("Connector '%s' not connected", conn->fullname().c_str()); + + output.connector = conn; + output.mode = output.connector->get_default_mode(); +} + +static void get_default_crtc(Card& card, OutputInfo& output) +{ + Crtc* crtc = output.connector->get_current_crtc(); + + if (crtc && s_used_crtcs.find(crtc) == s_used_crtcs.end()) { + s_used_crtcs.insert(crtc); + output.crtc = crtc; + return; + } + + for (const auto& possible : output.connector->get_possible_crtcs()) { + if (s_used_crtcs.find(possible) == s_used_crtcs.end()) { + s_used_crtcs.insert(possible); + output.crtc = possible; + return; + } + } + + EXIT("Could not find available crtc"); +} + +static void parse_crtc(Card& card, const string& crtc_str, OutputInfo& output) +{ + // @12:1920x1200@60 + const regex mode_re("(?:(@?)(\\d+):)?(?:(\\d+)x(\\d+)(i)?)(?:@([\\d\\.]+))?"); + + smatch sm; + if (!regex_match(crtc_str, sm, mode_re)) + EXIT("Failed to parse crtc option '%s'", crtc_str.c_str()); + + if (sm[2].matched) { + bool use_id = sm[1].length() == 1; + unsigned num = stoul(sm[2].str()); + + 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 { + output.crtc = output.connector->get_current_crtc(); + } + + unsigned w = stoul(sm[3]); + unsigned h = stoul(sm[4]); + bool ilace = sm[5].matched ? true : false; + float refresh = sm[6].matched ? stof(sm[6]) : 0; + + bool found_mode = false; + + try { + output.mode = output.connector->get_mode(w, h, refresh, ilace); + found_mode = true; + } catch (exception& e) { } + + if (!found_mode && s_use_dmt) { + try { + output.mode = find_dmt(w, h, refresh, ilace); + found_mode = true; + printf("Found mode from DMT\n"); + } catch (exception& e) { } + } + + if (!found_mode && s_use_cea) { + try { + output.mode = find_cea(w, h, refresh, ilace); + found_mode = true; + printf("Found mode from CEA\n"); + } catch (exception& e) { } + } + + if (!found_mode) + throw invalid_argument("Mode not found"); +} + +static void parse_plane(Card& card, const string& plane_str, const OutputInfo& output, PlaneInfo& pinfo) +{ + // 3:400,400-400x400 + const regex plane_re("(?:(@?)(\\d+):)?(?:(\\d+),(\\d+)-)?(\\d+)x(\\d+)"); + + smatch sm; + if (!regex_match(plane_str, sm, plane_re)) + EXIT("Failed to parse plane option '%s'", plane_str.c_str()); + + if (sm[2].matched) { + bool use_id = sm[1].length() == 1; + unsigned num = stoul(sm[2].str()); + + 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 { + for (Plane* p : output.crtc->get_possible_planes()) { + if (s_used_planes.find(p) != s_used_planes.end()) + continue; + + if (p->plane_type() != PlaneType::Overlay) + continue; + + pinfo.plane = p; + } + + if (!pinfo.plane) + EXIT("Failed to find available plane"); + } + + s_used_planes.insert(pinfo.plane); + + pinfo.w = stoul(sm[5]); + pinfo.h = stoul(sm[6]); + + if (sm[3].matched) + pinfo.x = stoul(sm[3]); + else + pinfo.x = output.mode.hdisplay / 2 - pinfo.w / 2; + + if (sm[4].matched) + pinfo.y = stoul(sm[4]); + else + pinfo.y = output.mode.vdisplay / 2 - pinfo.h / 2; +} + +static vector get_default_fb(Card& card, unsigned width, unsigned height) +{ + vector v; + + for (unsigned i = 0; i < s_num_buffers; ++i) + v.push_back(new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888)); + + return v; +} + +static vector parse_fb(Card& card, const string& fb_str, unsigned def_w, unsigned def_h) +{ + unsigned w = def_w; + unsigned h = def_h; + PixelFormat format = PixelFormat::XRGB8888; + + if (!fb_str.empty()) { + // XXX the regexp is not quite correct + // 400x400-NV12 + const regex fb_re("(?:(\\d+)x(\\d+))?(?:-)?(\\w\\w\\w\\w)?"); + + smatch sm; + if (!regex_match(fb_str, sm, fb_re)) + EXIT("Failed to parse fb option '%s'", fb_str.c_str()); + + if (sm[1].matched) + w = stoul(sm[1]); + if (sm[2].matched) + h = stoul(sm[2]); + if (sm[3].matched) + format = FourCCToPixelFormat(sm[3]); + } + + vector 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 = + "Usage: testpat [OPTION]...\n\n" + "Show a test pattern on a display or plane\n\n" + "Options:\n" + " --device=DEVICE DEVICE is the path to DRM card to open\n" + " -c, --connector=CONN CONN is \n" + " -r, --crtc=CRTC CRTC is [:]x[@]\n" + " -p, --plane=PLANE PLANE is [:][,-]x\n" + " -f, --fb=FB FB is [x][-][<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" + ", and can be given by index () or id ().\n" + " can also be given by name.\n" + "\n" + "Options can be given multiple times to set up multiple displays or planes.\n" + "Options may apply to previous options, e.g. a plane will be set on a crtc set in\n" + "an earlier option.\n" + "If you omit parameters, testpat tries to guess what you mean\n" + "\n" + "Examples:\n" + "\n" + "Set eDP-1 mode to 1920x1080@60, show XR24 framebuffer on the crtc, and a 400x400 XB24 plane:\n" + " testpat -c eDP-1 -r 1920x1080@60 -f XR24 -p 400x400 -f XB24\n\n" + "XR24 framebuffer on first connected connector in the default mode:\n" + " testpat -f XR24\n\n" + "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" + ; + +static void usage() +{ + puts(usage_str); +} + +enum class ObjectType +{ + Connector, + Crtc, + Plane, + Framebuffer, +}; + +struct Arg +{ + ObjectType type; + string arg; +}; + +static string s_device_path = "/dev/dri/card0"; + +static vector parse_cmdline(int argc, char **argv) +{ + vector args; + + OptionSet optionset = { + Option("|device=", + [&](string s) + { + s_device_path = s; + }), + Option("c|connector=", + [&](string s) + { + args.push_back(Arg { ObjectType::Connector, s }); + }), + Option("r|crtc=", [&](string s) + { + args.push_back(Arg { ObjectType::Crtc, s }); + }), + Option("p|plane=", [&](string s) + { + args.push_back(Arg { ObjectType::Plane, s }); + }), + Option("f|fb=", [&](string s) + { + args.push_back(Arg { ObjectType::Framebuffer, s }); + }), + Option("|dmt", []() + { + s_use_dmt = true; + }), + Option("|cea", []() + { + s_use_cea = true; + }), + Option("|flip", []() + { + s_flip_mode = true; + s_num_buffers = 2; + }), + Option("|sync", []() + { + s_flip_sync = true; + }), + Option("h|help", [&]() + { + usage(); + exit(-1); + }), + }; + + optionset.parse(argc, argv); + + if (optionset.params().size() > 0) { + usage(); + exit(-1); + } + + return args; +} + +static vector setups_to_outputs(Card& card, const vector& output_args) +{ + vector outputs; + + if (output_args.size() == 0) { + // no output args, show a pattern on all screens + for (auto& pipe : card.get_connected_pipelines()) { + OutputInfo output = { }; + output.connector = pipe.connector; + output.crtc = pipe.crtc; + output.mode = output.connector->get_default_mode(); + + output.fbs = get_default_fb(card, output.mode.hdisplay, output.mode.vdisplay); + + outputs.push_back(output); + } + + return outputs; + } + + OutputInfo* current_output = 0; + PlaneInfo* current_plane = 0; + + for (auto& arg : output_args) { + switch (arg.type) { + case ObjectType::Connector: + { + outputs.push_back(OutputInfo { }); + current_output = &outputs.back(); + + parse_connector(card, arg.arg, *current_output); + current_plane = 0; + + break; + } + + case ObjectType::Crtc: + { + if (!current_output) { + outputs.push_back(OutputInfo { }); + current_output = &outputs.back(); + } + + if (!current_output->connector) + get_default_connector(card, *current_output); + + parse_crtc(card, arg.arg, *current_output); + + current_output->user_set_crtc = true; + + current_plane = 0; + + break; + } + + case ObjectType::Plane: + { + if (!current_output) { + outputs.push_back(OutputInfo { }); + current_output = &outputs.back(); + } + + if (!current_output->connector) + get_default_connector(card, *current_output); + + if (!current_output->crtc) + get_default_crtc(card, *current_output); + + current_output->planes.push_back(PlaneInfo { }); + current_plane = ¤t_output->planes.back(); + + parse_plane(card, arg.arg, *current_output, *current_plane); + + break; + } + + case ObjectType::Framebuffer: + { + if (!current_output) { + outputs.push_back(OutputInfo { }); + current_output = &outputs.back(); + } + + if (!current_output->connector) + get_default_connector(card, *current_output); + + if (!current_output->crtc) + get_default_crtc(card, *current_output); + + int def_w, def_h; + + if (current_plane) { + def_w = current_plane->w; + def_h = current_plane->h; + } else { + def_w = current_output->mode.hdisplay; + def_h = current_output->mode.vdisplay; + } + + auto fbs = parse_fb(card, arg.arg, def_w, def_h); + + if (current_plane) + current_plane->fbs = fbs; + else + current_output->fbs = fbs; + + break; + } + } + } + + // create default framebuffers if needed + for (OutputInfo& o : outputs) { + if (!o.crtc) { + get_default_crtc(card, *current_output); + o.user_set_crtc = true; + } + + 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.fbs.empty()) + p.fbs = get_default_fb(card, p.w, p.h); + } + } + + return outputs; +} + +static std::string videomode_to_string(const Videomode& m) +{ + string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp()); + string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp()); + + return sformat("%s %.3f %s %s %u (%.2f) %#x %#x", + m.name.c_str(), + m.clock / 1000.0, + h.c_str(), v.c_str(), + m.vrefresh, m.calculated_vrefresh(), + m.flags, + m.type); +} + +static void print_outputs(const vector& outputs) +{ + for (unsigned i = 0; i < outputs.size(); ++i) { + const OutputInfo& o = outputs[i]; + + printf("Connector %u/@%u: %s\n", o.connector->idx(), o.connector->id(), + o.connector->fullname().c_str()); + printf(" Crtc %u/@%u", o.crtc->idx(), o.crtc->id()); + if (o.primary_plane) + printf(" (plane %u/@%u)", o.primary_plane->idx(), o.primary_plane->id()); + printf(": %s\n", videomode_to_string(o.mode).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->idx(), p.plane->id(), + p.x, p.y, p.w, p.h); + printf(" Fb %u %ux%u-%s\n", fb->id(), fb->width(), fb->height(), + PixelFormatToFourCC(fb->format()).c_str()); + } + } +} + +static void draw_test_patterns(const vector& 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& outputs) +{ + for (const OutputInfo& o : outputs) { + auto conn = o.connector; + auto crtc = o.crtc; + + 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) { + auto fb = p.fbs[0]; + int r = crtc->set_plane(p.plane, *fb, + p.x, p.y, p.w, p.h, + 0, 0, fb->width(), fb->height()); + if (r) + printf("crtc->set_plane() failed for plane %u: %s\n", + p.plane->id(), strerror(-r)); + } + } +} + +static void set_crtcs_n_planes(Card& card, const vector& outputs) +{ + // Keep blobs here so that we keep ref to them until we have committed the req + vector> 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 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::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 diff = now - m_prev_frame; + if (diff > m_slowest_frame) + m_slowest_frame = diff; + + if (m_frame_num % 100 == 0) { + std::chrono::duration 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::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 m_outputs; + unsigned m_frame_num; + + chrono::steady_clock::time_point m_prev_print; + chrono::steady_clock::time_point m_prev_frame; + chrono::duration m_slowest_frame; + + static const unsigned bar_width = 20; + static const unsigned bar_speed = 8; +}; + +static void main_flip(Card& card, const vector& outputs) +{ + fd_set fds; + + FD_ZERO(&fds); + + int fd = card.fd(); + + vector> flipstates; + + if (!s_flip_sync) { + for (const OutputInfo& o : outputs) { + auto fs = unique_ptr(new FlipState(card, to_string(o.connector->idx()), { &o })); + flipstates.push_back(move(fs)); + } + } else { + vector ois; + + string name; + for (const OutputInfo& o : outputs) { + name += to_string(o.connector->idx()) + ","; + ois.push_back(&o); + } + + auto fs = unique_ptr(new FlipState(card, name, ois)); + flipstates.push_back(move(fs)); + } + + for (unique_ptr& 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 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 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); + + if (card.has_atomic()) + set_crtcs_n_planes(card, outputs); + else + set_crtcs_n_planes_legacy(card, outputs); + + printf("press enter to exit\n"); + + if (s_flip_mode) + main_flip(card, outputs); + else + getchar(); +} diff --git a/utils/testpat.cpp b/utils/testpat.cpp deleted file mode 100644 index ccddccb..0000000 --- a/utils/testpat.cpp +++ /dev/null @@ -1,881 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -using namespace std; -using namespace kms; - -struct PlaneInfo -{ - Plane* plane; - - unsigned x; - unsigned y; - unsigned w; - unsigned h; - - vector fbs; -}; - -struct OutputInfo -{ - Connector* connector; - - Crtc* crtc; - Plane* primary_plane; - Videomode mode; - bool user_set_crtc; - vector fbs; - - vector 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 s_used_crtcs; -static set s_used_planes; - -__attribute__ ((unused)) -static void print_regex_match(smatch sm) -{ - for (unsigned i = 0; i < sm.size(); ++i) { - string str = sm[i].str(); - printf("%u: %s\n", i, str.c_str()); - } -} - -static void get_default_connector(Card& card, OutputInfo& output) -{ - output.connector = card.get_first_connected_connector(); - output.mode = output.connector->get_default_mode(); -} - -static void parse_connector(Card& card, const string& str, OutputInfo& output) -{ - Connector* conn = resolve_connector(card, str); - - if (!conn) - EXIT("No connector '%s'", str.c_str()); - - if (!conn->connected()) - EXIT("Connector '%s' not connected", conn->fullname().c_str()); - - output.connector = conn; - output.mode = output.connector->get_default_mode(); -} - -static void get_default_crtc(Card& card, OutputInfo& output) -{ - Crtc* crtc = output.connector->get_current_crtc(); - - if (crtc && s_used_crtcs.find(crtc) == s_used_crtcs.end()) { - s_used_crtcs.insert(crtc); - output.crtc = crtc; - return; - } - - for (const auto& possible : output.connector->get_possible_crtcs()) { - if (s_used_crtcs.find(possible) == s_used_crtcs.end()) { - s_used_crtcs.insert(possible); - output.crtc = possible; - return; - } - } - - EXIT("Could not find available crtc"); -} - -static void parse_crtc(Card& card, const string& crtc_str, OutputInfo& output) -{ - // @12:1920x1200@60 - const regex mode_re("(?:(@?)(\\d+):)?(?:(\\d+)x(\\d+)(i)?)(?:@([\\d\\.]+))?"); - - smatch sm; - if (!regex_match(crtc_str, sm, mode_re)) - EXIT("Failed to parse crtc option '%s'", crtc_str.c_str()); - - if (sm[2].matched) { - bool use_id = sm[1].length() == 1; - unsigned num = stoul(sm[2].str()); - - 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 { - output.crtc = output.connector->get_current_crtc(); - } - - unsigned w = stoul(sm[3]); - unsigned h = stoul(sm[4]); - bool ilace = sm[5].matched ? true : false; - float refresh = sm[6].matched ? stof(sm[6]) : 0; - - bool found_mode = false; - - try { - output.mode = output.connector->get_mode(w, h, refresh, ilace); - found_mode = true; - } catch (exception& e) { } - - if (!found_mode && s_use_dmt) { - try { - output.mode = find_dmt(w, h, refresh, ilace); - found_mode = true; - printf("Found mode from DMT\n"); - } catch (exception& e) { } - } - - if (!found_mode && s_use_cea) { - try { - output.mode = find_cea(w, h, refresh, ilace); - found_mode = true; - printf("Found mode from CEA\n"); - } catch (exception& e) { } - } - - if (!found_mode) - throw invalid_argument("Mode not found"); -} - -static void parse_plane(Card& card, const string& plane_str, const OutputInfo& output, PlaneInfo& pinfo) -{ - // 3:400,400-400x400 - const regex plane_re("(?:(@?)(\\d+):)?(?:(\\d+),(\\d+)-)?(\\d+)x(\\d+)"); - - smatch sm; - if (!regex_match(plane_str, sm, plane_re)) - EXIT("Failed to parse plane option '%s'", plane_str.c_str()); - - if (sm[2].matched) { - bool use_id = sm[1].length() == 1; - unsigned num = stoul(sm[2].str()); - - 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 { - for (Plane* p : output.crtc->get_possible_planes()) { - if (s_used_planes.find(p) != s_used_planes.end()) - continue; - - if (p->plane_type() != PlaneType::Overlay) - continue; - - pinfo.plane = p; - } - - if (!pinfo.plane) - EXIT("Failed to find available plane"); - } - - s_used_planes.insert(pinfo.plane); - - pinfo.w = stoul(sm[5]); - pinfo.h = stoul(sm[6]); - - if (sm[3].matched) - pinfo.x = stoul(sm[3]); - else - pinfo.x = output.mode.hdisplay / 2 - pinfo.w / 2; - - if (sm[4].matched) - pinfo.y = stoul(sm[4]); - else - pinfo.y = output.mode.vdisplay / 2 - pinfo.h / 2; -} - -static vector get_default_fb(Card& card, unsigned width, unsigned height) -{ - vector v; - - for (unsigned i = 0; i < s_num_buffers; ++i) - v.push_back(new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888)); - - return v; -} - -static vector parse_fb(Card& card, const string& fb_str, unsigned def_w, unsigned def_h) -{ - unsigned w = def_w; - unsigned h = def_h; - PixelFormat format = PixelFormat::XRGB8888; - - if (!fb_str.empty()) { - // XXX the regexp is not quite correct - // 400x400-NV12 - const regex fb_re("(?:(\\d+)x(\\d+))?(?:-)?(\\w\\w\\w\\w)?"); - - smatch sm; - if (!regex_match(fb_str, sm, fb_re)) - EXIT("Failed to parse fb option '%s'", fb_str.c_str()); - - if (sm[1].matched) - w = stoul(sm[1]); - if (sm[2].matched) - h = stoul(sm[2]); - if (sm[3].matched) - format = FourCCToPixelFormat(sm[3]); - } - - vector 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 = - "Usage: testpat [OPTION]...\n\n" - "Show a test pattern on a display or plane\n\n" - "Options:\n" - " --device=DEVICE DEVICE is the path to DRM card to open\n" - " -c, --connector=CONN CONN is \n" - " -r, --crtc=CRTC CRTC is [:]x[@]\n" - " -p, --plane=PLANE PLANE is [:][,-]x\n" - " -f, --fb=FB FB is [x][-][<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" - ", and can be given by index () or id ().\n" - " can also be given by name.\n" - "\n" - "Options can be given multiple times to set up multiple displays or planes.\n" - "Options may apply to previous options, e.g. a plane will be set on a crtc set in\n" - "an earlier option.\n" - "If you omit parameters, testpat tries to guess what you mean\n" - "\n" - "Examples:\n" - "\n" - "Set eDP-1 mode to 1920x1080@60, show XR24 framebuffer on the crtc, and a 400x400 XB24 plane:\n" - " testpat -c eDP-1 -r 1920x1080@60 -f XR24 -p 400x400 -f XB24\n\n" - "XR24 framebuffer on first connected connector in the default mode:\n" - " testpat -f XR24\n\n" - "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" - ; - -static void usage() -{ - puts(usage_str); -} - -enum class ObjectType -{ - Connector, - Crtc, - Plane, - Framebuffer, -}; - -struct Arg -{ - ObjectType type; - string arg; -}; - -static string s_device_path = "/dev/dri/card0"; - -static vector parse_cmdline(int argc, char **argv) -{ - vector args; - - OptionSet optionset = { - Option("|device=", - [&](string s) - { - s_device_path = s; - }), - Option("c|connector=", - [&](string s) - { - args.push_back(Arg { ObjectType::Connector, s }); - }), - Option("r|crtc=", [&](string s) - { - args.push_back(Arg { ObjectType::Crtc, s }); - }), - Option("p|plane=", [&](string s) - { - args.push_back(Arg { ObjectType::Plane, s }); - }), - Option("f|fb=", [&](string s) - { - args.push_back(Arg { ObjectType::Framebuffer, s }); - }), - Option("|dmt", []() - { - s_use_dmt = true; - }), - Option("|cea", []() - { - s_use_cea = true; - }), - Option("|flip", []() - { - s_flip_mode = true; - s_num_buffers = 2; - }), - Option("|sync", []() - { - s_flip_sync = true; - }), - Option("h|help", [&]() - { - usage(); - exit(-1); - }), - }; - - optionset.parse(argc, argv); - - if (optionset.params().size() > 0) { - usage(); - exit(-1); - } - - return args; -} - -static vector setups_to_outputs(Card& card, const vector& output_args) -{ - vector outputs; - - if (output_args.size() == 0) { - // no output args, show a pattern on all screens - for (auto& pipe : card.get_connected_pipelines()) { - OutputInfo output = { }; - output.connector = pipe.connector; - output.crtc = pipe.crtc; - output.mode = output.connector->get_default_mode(); - - output.fbs = get_default_fb(card, output.mode.hdisplay, output.mode.vdisplay); - - outputs.push_back(output); - } - - return outputs; - } - - OutputInfo* current_output = 0; - PlaneInfo* current_plane = 0; - - for (auto& arg : output_args) { - switch (arg.type) { - case ObjectType::Connector: - { - outputs.push_back(OutputInfo { }); - current_output = &outputs.back(); - - parse_connector(card, arg.arg, *current_output); - current_plane = 0; - - break; - } - - case ObjectType::Crtc: - { - if (!current_output) { - outputs.push_back(OutputInfo { }); - current_output = &outputs.back(); - } - - if (!current_output->connector) - get_default_connector(card, *current_output); - - parse_crtc(card, arg.arg, *current_output); - - current_output->user_set_crtc = true; - - current_plane = 0; - - break; - } - - case ObjectType::Plane: - { - if (!current_output) { - outputs.push_back(OutputInfo { }); - current_output = &outputs.back(); - } - - if (!current_output->connector) - get_default_connector(card, *current_output); - - if (!current_output->crtc) - get_default_crtc(card, *current_output); - - current_output->planes.push_back(PlaneInfo { }); - current_plane = ¤t_output->planes.back(); - - parse_plane(card, arg.arg, *current_output, *current_plane); - - break; - } - - case ObjectType::Framebuffer: - { - if (!current_output) { - outputs.push_back(OutputInfo { }); - current_output = &outputs.back(); - } - - if (!current_output->connector) - get_default_connector(card, *current_output); - - if (!current_output->crtc) - get_default_crtc(card, *current_output); - - int def_w, def_h; - - if (current_plane) { - def_w = current_plane->w; - def_h = current_plane->h; - } else { - def_w = current_output->mode.hdisplay; - def_h = current_output->mode.vdisplay; - } - - auto fbs = parse_fb(card, arg.arg, def_w, def_h); - - if (current_plane) - current_plane->fbs = fbs; - else - current_output->fbs = fbs; - - break; - } - } - } - - // create default framebuffers if needed - for (OutputInfo& o : outputs) { - if (!o.crtc) { - get_default_crtc(card, *current_output); - o.user_set_crtc = true; - } - - 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.fbs.empty()) - p.fbs = get_default_fb(card, p.w, p.h); - } - } - - return outputs; -} - -static std::string videomode_to_string(const Videomode& m) -{ - string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp()); - string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp()); - - return sformat("%s %.3f %s %s %u (%.2f) %#x %#x", - m.name.c_str(), - m.clock / 1000.0, - h.c_str(), v.c_str(), - m.vrefresh, m.calculated_vrefresh(), - m.flags, - m.type); -} - -static void print_outputs(const vector& outputs) -{ - for (unsigned i = 0; i < outputs.size(); ++i) { - const OutputInfo& o = outputs[i]; - - printf("Connector %u/@%u: %s\n", o.connector->idx(), o.connector->id(), - o.connector->fullname().c_str()); - printf(" Crtc %u/@%u", o.crtc->idx(), o.crtc->id()); - if (o.primary_plane) - printf(" (plane %u/@%u)", o.primary_plane->idx(), o.primary_plane->id()); - printf(": %s\n", videomode_to_string(o.mode).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->idx(), p.plane->id(), - p.x, p.y, p.w, p.h); - printf(" Fb %u %ux%u-%s\n", fb->id(), fb->width(), fb->height(), - PixelFormatToFourCC(fb->format()).c_str()); - } - } -} - -static void draw_test_patterns(const vector& 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& outputs) -{ - for (const OutputInfo& o : outputs) { - auto conn = o.connector; - auto crtc = o.crtc; - - 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) { - auto fb = p.fbs[0]; - int r = crtc->set_plane(p.plane, *fb, - p.x, p.y, p.w, p.h, - 0, 0, fb->width(), fb->height()); - if (r) - printf("crtc->set_plane() failed for plane %u: %s\n", - p.plane->id(), strerror(-r)); - } - } -} - -static void set_crtcs_n_planes(Card& card, const vector& outputs) -{ - // Keep blobs here so that we keep ref to them until we have committed the req - vector> 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 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::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 diff = now - m_prev_frame; - if (diff > m_slowest_frame) - m_slowest_frame = diff; - - if (m_frame_num % 100 == 0) { - std::chrono::duration 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::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 m_outputs; - unsigned m_frame_num; - - chrono::steady_clock::time_point m_prev_print; - chrono::steady_clock::time_point m_prev_frame; - chrono::duration m_slowest_frame; - - static const unsigned bar_width = 20; - static const unsigned bar_speed = 8; -}; - -static void main_flip(Card& card, const vector& outputs) -{ - fd_set fds; - - FD_ZERO(&fds); - - int fd = card.fd(); - - vector> flipstates; - - if (!s_flip_sync) { - for (const OutputInfo& o : outputs) { - auto fs = unique_ptr(new FlipState(card, to_string(o.connector->idx()), { &o })); - flipstates.push_back(move(fs)); - } - } else { - vector ois; - - string name; - for (const OutputInfo& o : outputs) { - name += to_string(o.connector->idx()) + ","; - ois.push_back(&o); - } - - auto fs = unique_ptr(new FlipState(card, name, ois)); - flipstates.push_back(move(fs)); - } - - for (unique_ptr& 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 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 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); - - if (card.has_atomic()) - set_crtcs_n_planes(card, outputs); - else - set_crtcs_n_planes_legacy(card, outputs); - - printf("press enter to exit\n"); - - if (s_flip_mode) - main_flip(card, outputs); - else - getchar(); -} -- cgit v1.2.3