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"
11 #include "test.h"
13 using namespace std;
14 using namespace kms;
16 static void main_loop(Card& card);
18 class Flipper
19 {
20 public:
21 Flipper(Card& card, unsigned width, unsigned height)
22 : m_current(0), m_bar_xpos(0)
23 {
24 auto format = PixelFormat::XRGB8888;
25 m_fbs[0] = new DumbFramebuffer(card, width, height, format);
26 m_fbs[1] = new DumbFramebuffer(card, width, height, format);
27 }
29 ~Flipper()
30 {
31 delete m_fbs[0];
32 delete m_fbs[1];
33 }
35 Framebuffer* get_next()
36 {
37 m_current ^= 1;
39 const int bar_width = 20;
40 const int bar_speed = 8;
42 auto fb = m_fbs[m_current];
44 int current_xpos = m_bar_xpos;
45 int old_xpos = (current_xpos + (fb->width() - bar_width - bar_speed)) % (fb->width() - bar_width);
46 int new_xpos = (current_xpos + bar_speed) % (fb->width() - bar_width);
48 draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
50 m_bar_xpos = new_xpos;
52 return fb;
53 }
55 private:
56 DumbFramebuffer* m_fbs[2];
58 int m_current;
59 int m_bar_xpos;
60 };
62 class OutputFlipHandler : private PageFlipHandlerBase
63 {
64 public:
65 OutputFlipHandler(Connector* conn, Crtc* crtc, const Videomode& mode)
66 : m_connector(conn), m_crtc(crtc), m_mode(mode),
67 m_flipper(conn->card(), mode.hdisplay, mode.vdisplay),
68 m_plane(0), m_plane_flipper(0)
69 {
70 }
72 OutputFlipHandler(Connector* conn, Crtc* crtc, const Videomode& mode,
73 Plane* plane, unsigned pwidth, unsigned pheight)
74 : m_connector(conn), m_crtc(crtc), m_mode(mode),
75 m_flipper(conn->card(), mode.hdisplay, mode.vdisplay),
76 m_plane(plane)
77 {
78 m_plane_flipper = new Flipper(conn->card(), pwidth, pheight);
79 }
81 ~OutputFlipHandler()
82 {
83 if (m_plane_flipper)
84 delete m_plane_flipper;
85 }
87 OutputFlipHandler(const OutputFlipHandler& other) = delete;
88 OutputFlipHandler& operator=(const OutputFlipHandler& other) = delete;
90 void set_mode()
91 {
92 auto fb = m_flipper.get_next();
93 int r = m_crtc->set_mode(m_connector, *fb, m_mode);
94 ASSERT(r == 0);
96 if (m_crtc->card().has_atomic()) {
97 Plane* root_plane = 0;
98 for (Plane* p : m_crtc->get_possible_planes()) {
99 if (p->crtc_id() == m_crtc->id()) {
100 root_plane = p;
101 break;
102 }
103 }
105 FAIL_IF(!root_plane, "No primary plane for crtc %d", m_crtc->id());
107 m_root_plane = root_plane;
108 }
110 if (m_plane) {
111 auto planefb = m_plane_flipper->get_next();
112 r = m_crtc->set_plane(m_plane, *planefb,
113 0, 0, planefb->width(), planefb->height(),
114 0, 0, planefb->width(), planefb->height());
115 ASSERT(r == 0);
116 }
117 }
119 void start_flipping()
120 {
121 m_time_last = m_t1 = std::chrono::steady_clock::now();
122 m_slowest_frame = std::chrono::duration<float>::min();
123 m_frame_num = 0;
124 queue_next();
125 }
127 private:
128 void handle_page_flip(uint32_t frame, double time)
129 {
130 ++m_frame_num;
132 auto now = std::chrono::steady_clock::now();
134 std::chrono::duration<float> diff = now - m_time_last;
135 if (diff > m_slowest_frame)
136 m_slowest_frame = diff;
138 if (m_frame_num % 100 == 0) {
139 std::chrono::duration<float> fsec = now - m_t1;
140 printf("Output %d: fps %f, slowest %.2f ms\n",
141 m_connector->idx(), 100.0 / fsec.count(),
142 m_slowest_frame.count() * 1000);
143 m_t1 = now;
144 m_slowest_frame = std::chrono::duration<float>::min();
145 }
147 m_time_last = now;
149 queue_next();
150 }
152 void queue_next()
153 {
154 auto crtc = m_crtc;
155 auto& card = crtc->card();
157 auto fb = m_flipper.get_next();
158 Framebuffer* planefb = m_plane ? m_plane_flipper->get_next() : 0;
160 if (card.has_atomic()) {
161 int r;
163 AtomicReq req(card);
165 req.add(m_root_plane, "FB_ID", fb->id());
166 if (m_plane)
167 req.add(m_plane, "FB_ID", planefb->id());
169 r = req.test();
170 ASSERT(r == 0);
172 r = req.commit(this);
173 ASSERT(r == 0);
174 } else {
175 int r = crtc->page_flip(*fb, this);
176 ASSERT(r == 0);
178 if (m_plane) {
179 r = m_crtc->set_plane(m_plane, *planefb,
180 0, 0, planefb->width(), planefb->height(),
181 0, 0, planefb->width(), planefb->height());
182 ASSERT(r == 0);
183 }
184 }
185 }
187 private:
188 Connector* m_connector;
189 Crtc* m_crtc;
190 Videomode m_mode;
191 Plane* m_root_plane;
193 int m_frame_num;
194 chrono::steady_clock::time_point m_t1;
195 chrono::steady_clock::time_point m_time_last;
196 chrono::duration<float> m_slowest_frame;
198 Flipper m_flipper;
200 Plane* m_plane;
201 Flipper* m_plane_flipper;
202 };
204 int main()
205 {
206 Card card;
208 if (card.master() == false)
209 printf("Not DRM master, modeset may fail\n");
211 vector<OutputFlipHandler*> outputs;
213 for (auto pipe : card.get_connected_pipelines())
214 {
215 auto conn = pipe.connector;
216 auto crtc = pipe.crtc;
217 auto mode = conn->get_default_mode();
220 Plane* plane = 0;
221 #if 0 // disable the plane for now
222 for (Plane* p : crtc->get_possible_planes()) {
223 if (p->plane_type() == PlaneType::Overlay) {
224 plane = p;
225 break;
226 }
227 }
228 #endif
229 OutputFlipHandler* output;
230 if (plane)
231 output = new OutputFlipHandler(conn, crtc, mode, plane, 500, 400);
232 else
233 output = new OutputFlipHandler(conn, crtc, mode);
234 outputs.push_back(output);
235 }
237 for(auto out : outputs)
238 out->set_mode();
240 for(auto out : outputs)
241 out->start_flipping();
243 main_loop(card);
245 for(auto out : outputs)
246 delete out;
247 }
249 static void main_loop(Card& card)
250 {
251 fd_set fds;
253 FD_ZERO(&fds);
255 int fd = card.fd();
257 printf("press enter to exit\n");
259 while (true) {
260 int r;
262 FD_SET(0, &fds);
263 FD_SET(fd, &fds);
265 r = select(fd + 1, &fds, NULL, NULL, NULL);
266 if (r < 0) {
267 fprintf(stderr, "select() failed with %d: %m\n", errno);
268 break;
269 } else if (FD_ISSET(0, &fds)) {
270 fprintf(stderr, "exit due to user-input\n");
271 break;
272 } else if (FD_ISSET(fd, &fds)) {
273 card.call_page_flip_handlers();
274 }
275 }
276 }