1 #include <cstdio>
2 #include <algorithm>
3 #include <chrono>
5 #include <xf86drm.h>
6 #include <xf86drmMode.h>
7 #include <drm_fourcc.h>
9 #include <kms++.h>
10 #include <kms++util.h>
12 using namespace std;
13 using namespace kms;
15 static void main_loop(Card& card);
17 class Flipper
18 {
19 public:
20 Flipper(Card& card, unsigned width, unsigned height)
21 : m_current(0), m_bar_xpos(0)
22 {
23 auto format = PixelFormat::XRGB8888;
24 m_fbs[0] = new DumbFramebuffer(card, width, height, format);
25 m_fbs[1] = new DumbFramebuffer(card, width, height, format);
26 }
28 ~Flipper()
29 {
30 delete m_fbs[0];
31 delete m_fbs[1];
32 }
34 Framebuffer* get_next()
35 {
36 m_current ^= 1;
38 const int bar_width = 20;
39 const int bar_speed = 8;
41 auto fb = m_fbs[m_current];
43 int current_xpos = m_bar_xpos;
44 int old_xpos = (current_xpos + (fb->width() - bar_width - bar_speed)) % (fb->width() - bar_width);
45 int new_xpos = (current_xpos + bar_speed) % (fb->width() - bar_width);
47 draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
49 m_bar_xpos = new_xpos;
51 return fb;
52 }
54 private:
55 DumbFramebuffer* m_fbs[2];
57 int m_current;
58 int m_bar_xpos;
59 };
61 class OutputFlipHandler : private PageFlipHandlerBase
62 {
63 public:
64 OutputFlipHandler(Connector* conn, Crtc* crtc, const Videomode& mode)
65 : m_connector(conn), m_crtc(crtc), m_mode(mode),
66 m_flipper(conn->card(), mode.hdisplay, mode.vdisplay),
67 m_plane(0), m_plane_flipper(0)
68 {
69 }
71 OutputFlipHandler(Connector* conn, Crtc* crtc, const Videomode& mode,
72 Plane* plane, unsigned pwidth, unsigned pheight)
73 : m_connector(conn), m_crtc(crtc), m_mode(mode),
74 m_flipper(conn->card(), mode.hdisplay, mode.vdisplay),
75 m_plane(plane)
76 {
77 m_plane_flipper = new Flipper(conn->card(), pwidth, pheight);
78 }
80 ~OutputFlipHandler()
81 {
82 if (m_plane_flipper)
83 delete m_plane_flipper;
84 }
86 OutputFlipHandler(const OutputFlipHandler& other) = delete;
87 OutputFlipHandler& operator=(const OutputFlipHandler& other) = delete;
89 void set_mode()
90 {
91 auto fb = m_flipper.get_next();
92 int r = m_crtc->set_mode(m_connector, *fb, m_mode);
93 ASSERT(r == 0);
95 if (m_crtc->card().has_atomic())
96 m_root_plane = m_crtc->get_primary_plane();
98 if (m_plane) {
99 auto planefb = m_plane_flipper->get_next();
100 r = m_crtc->set_plane(m_plane, *planefb,
101 0, 0, planefb->width(), planefb->height(),
102 0, 0, planefb->width(), planefb->height());
103 ASSERT(r == 0);
104 }
105 }
107 void start_flipping()
108 {
109 m_time_last = m_t1 = std::chrono::steady_clock::now();
110 m_slowest_frame = std::chrono::duration<float>::min();
111 m_frame_num = 0;
112 queue_next();
113 }
115 private:
116 void handle_page_flip(uint32_t frame, double time)
117 {
118 ++m_frame_num;
120 auto now = std::chrono::steady_clock::now();
122 std::chrono::duration<float> diff = now - m_time_last;
123 if (diff > m_slowest_frame)
124 m_slowest_frame = diff;
126 if (m_frame_num % 100 == 0) {
127 std::chrono::duration<float> fsec = now - m_t1;
128 printf("Output %d: fps %f, slowest %.2f ms\n",
129 m_connector->idx(), 100.0 / fsec.count(),
130 m_slowest_frame.count() * 1000);
131 m_t1 = now;
132 m_slowest_frame = std::chrono::duration<float>::min();
133 }
135 m_time_last = now;
137 queue_next();
138 }
140 void queue_next()
141 {
142 auto crtc = m_crtc;
143 auto& card = crtc->card();
145 auto fb = m_flipper.get_next();
146 Framebuffer* planefb = m_plane ? m_plane_flipper->get_next() : 0;
148 if (card.has_atomic()) {
149 int r;
151 AtomicReq req(card);
153 req.add(m_root_plane, "FB_ID", fb->id());
154 if (m_plane)
155 req.add(m_plane, "FB_ID", planefb->id());
157 r = req.test();
158 ASSERT(r == 0);
160 r = req.commit(this);
161 ASSERT(r == 0);
162 } else {
163 int r = crtc->page_flip(*fb, this);
164 ASSERT(r == 0);
166 if (m_plane) {
167 r = m_crtc->set_plane(m_plane, *planefb,
168 0, 0, planefb->width(), planefb->height(),
169 0, 0, planefb->width(), planefb->height());
170 ASSERT(r == 0);
171 }
172 }
173 }
175 private:
176 Connector* m_connector;
177 Crtc* m_crtc;
178 Videomode m_mode;
179 Plane* m_root_plane;
181 int m_frame_num;
182 chrono::steady_clock::time_point m_t1;
183 chrono::steady_clock::time_point m_time_last;
184 chrono::duration<float> m_slowest_frame;
186 Flipper m_flipper;
188 Plane* m_plane;
189 Flipper* m_plane_flipper;
190 };
192 int main()
193 {
194 Card card;
196 if (card.master() == false)
197 printf("Not DRM master, modeset may fail\n");
199 vector<OutputFlipHandler*> outputs;
201 for (auto pipe : card.get_connected_pipelines())
202 {
203 auto conn = pipe.connector;
204 auto crtc = pipe.crtc;
205 auto mode = conn->get_default_mode();
208 Plane* plane = 0;
209 #if 0 // disable the plane for now
210 for (Plane* p : crtc->get_possible_planes()) {
211 if (p->plane_type() == PlaneType::Overlay) {
212 plane = p;
213 break;
214 }
215 }
216 #endif
217 OutputFlipHandler* output;
218 if (plane)
219 output = new OutputFlipHandler(conn, crtc, mode, plane, 500, 400);
220 else
221 output = new OutputFlipHandler(conn, crtc, mode);
222 outputs.push_back(output);
223 }
225 for(auto out : outputs)
226 out->set_mode();
228 for(auto out : outputs)
229 out->start_flipping();
231 main_loop(card);
233 for(auto out : outputs)
234 delete out;
235 }
237 static void main_loop(Card& card)
238 {
239 fd_set fds;
241 FD_ZERO(&fds);
243 int fd = card.fd();
245 printf("press enter to exit\n");
247 while (true) {
248 int r;
250 FD_SET(0, &fds);
251 FD_SET(fd, &fds);
253 r = select(fd + 1, &fds, NULL, NULL, NULL);
254 if (r < 0) {
255 fprintf(stderr, "select() failed with %d: %m\n", errno);
256 break;
257 } else if (FD_ISSET(0, &fds)) {
258 fprintf(stderr, "exit due to user-input\n");
259 break;
260 } else if (FD_ISSET(fd, &fds)) {
261 card.call_page_flip_handlers();
262 }
263 }
264 }