1 #include <chrono>
2 #include <cstdio>
3 #include <vector>
4 #include <memory>
5 #include <algorithm>
6 #include <poll.h>
8 #include <xf86drm.h>
9 #include <xf86drmMode.h>
10 #include <gbm.h>
12 #include <kms++.h>
13 #include "test.h"
14 #include "cube-egl.h"
15 #include "cube-gles2.h"
17 using namespace kms;
18 using namespace std;
20 static int s_flip_pending;
21 static bool s_need_exit;
23 static bool s_support_planes;
25 class GbmDevice
26 {
27 public:
28 GbmDevice(Card& card)
29 {
30 m_dev = gbm_create_device(card.fd());
31 FAIL_IF(!m_dev, "failed to create gbm device");
32 }
34 ~GbmDevice()
35 {
36 gbm_device_destroy(m_dev);
37 }
39 GbmDevice(const GbmDevice& other) = delete;
40 GbmDevice& operator=(const GbmDevice& other) = delete;
42 struct gbm_device* handle() const { return m_dev; }
44 private:
45 struct gbm_device* m_dev;
46 };
48 class GbmSurface
49 {
50 public:
51 GbmSurface(GbmDevice& gdev, int width, int height)
52 {
53 m_surface = gbm_surface_create(gdev.handle(), width, height,
54 GBM_FORMAT_XRGB8888,
55 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
56 FAIL_IF(!m_surface, "failed to create gbm surface");
57 }
59 ~GbmSurface()
60 {
61 gbm_surface_destroy(m_surface);
62 }
64 GbmSurface(const GbmSurface& other) = delete;
65 GbmSurface& operator=(const GbmSurface& other) = delete;
67 bool has_free()
68 {
69 return gbm_surface_has_free_buffers(m_surface);
70 }
72 gbm_bo* lock_front_buffer()
73 {
74 return gbm_surface_lock_front_buffer(m_surface);
75 }
77 void release_buffer(gbm_bo *bo)
78 {
79 gbm_surface_release_buffer(m_surface, bo);
80 }
82 struct gbm_surface* handle() const { return m_surface; }
84 private:
85 struct gbm_surface* m_surface;
86 };
88 class GbmEglSurface
89 {
90 public:
91 GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height)
92 : card(card), gdev(gdev), egl(egl), m_width(width), m_height(height),
93 bo_prev(0), bo_next(0)
94 {
95 gsurface = unique_ptr<GbmSurface>(new GbmSurface(gdev, width, height));
96 esurface = eglCreateWindowSurface(egl.display(), egl.config(), gsurface->handle(), NULL);
97 FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
98 }
100 ~GbmEglSurface()
101 {
102 if (bo_next)
103 gsurface->release_buffer(bo_next);
104 eglDestroySurface(egl.display(), esurface);
105 }
107 void make_current()
108 {
109 FAIL_IF(!gsurface->has_free(), "No free buffers");
111 eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
112 }
114 void swap_buffers()
115 {
116 eglSwapBuffers(egl.display(), esurface);
117 }
119 static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
120 {
121 auto fb = reinterpret_cast<Framebuffer*>(data);
122 delete fb;
123 }
125 static Framebuffer* drm_fb_get_from_bo(struct gbm_bo *bo, Card& card)
126 {
127 auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo));
128 if (fb)
129 return fb;
131 uint32_t width = gbm_bo_get_width(bo);
132 uint32_t height = gbm_bo_get_height(bo);
133 uint32_t stride = gbm_bo_get_stride(bo);
134 uint32_t handle = gbm_bo_get_handle(bo).u32;
136 fb = new ExtFramebuffer(card, width, height, 24, 32, stride, handle);
138 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
140 return fb;
141 }
143 struct Framebuffer* lock_next()
144 {
145 bo_prev = bo_next;
146 bo_next = gsurface->lock_front_buffer();
147 FAIL_IF(!bo_next, "could not lock gbm buffer");
148 return drm_fb_get_from_bo(bo_next, card);
149 }
151 void free_prev()
152 {
153 if (bo_prev) {
154 gsurface->release_buffer(bo_prev);
155 bo_prev = 0;
156 }
157 }
159 uint32_t width() const { return m_width; }
160 uint32_t height() const { return m_height; }
162 private:
163 Card& card;
164 GbmDevice& gdev;
165 const EglState& egl;
167 unique_ptr<GbmSurface> gsurface;
168 EGLSurface esurface;
170 int m_width;
171 int m_height;
173 struct gbm_bo* bo_prev;
174 struct gbm_bo* bo_next;
175 };
177 class OutputHandler : private PageFlipHandlerBase
178 {
179 public:
180 OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* plane, float rotation_mult)
181 : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_plane(plane), m_mode(mode),
182 m_rotation_mult(rotation_mult)
183 {
184 m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay));
185 m_scene1 = unique_ptr<GlScene>(new GlScene());
186 m_scene1->set_viewport(m_surface1->width(), m_surface1->height());
188 if (m_plane) {
189 m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400));
190 m_scene2 = unique_ptr<GlScene>(new GlScene());
191 m_scene2->set_viewport(m_surface2->width(), m_surface2->height());
192 }
193 }
195 OutputHandler(const OutputHandler& other) = delete;
196 OutputHandler& operator=(const OutputHandler& other) = delete;
198 void setup()
199 {
200 int ret;
202 m_surface1->make_current();
203 m_surface1->swap_buffers();
204 struct Framebuffer* fb = m_surface1->lock_next();
206 struct Framebuffer* planefb = 0;
208 if (m_plane) {
209 m_surface2->make_current();
210 m_surface2->swap_buffers();
211 planefb = m_surface2->lock_next();
212 }
215 ret = m_crtc->set_mode(m_connector, *fb, m_mode);
216 FAIL_IF(ret, "failed to set mode");
218 if (m_crtc->card().has_atomic()) {
219 Plane* root_plane = 0;
220 for (Plane* p : m_crtc->get_possible_planes()) {
221 if (p->crtc_id() == m_crtc->id()) {
222 root_plane = p;
223 break;
224 }
225 }
227 FAIL_IF(!root_plane, "No primary plane for crtc %d", m_crtc->id());
229 m_root_plane = root_plane;
230 }
232 if (m_plane) {
233 ret = m_crtc->set_plane(m_plane, *planefb,
234 0, 0, planefb->width(), planefb->height(),
235 0, 0, planefb->width(), planefb->height());
236 FAIL_IF(ret, "failed to set plane");
237 }
238 }
240 void start_flipping()
241 {
242 m_t1 = chrono::steady_clock::now();
243 queue_next();
244 }
246 private:
247 void handle_page_flip(uint32_t frame, double time)
248 {
249 ++m_frame_num;
251 if (m_frame_num % 100 == 0) {
252 auto t2 = chrono::steady_clock::now();
253 chrono::duration<float> fsec = t2 - m_t1;
254 printf("fps: %f\n", 100.0 / fsec.count());
255 m_t1 = t2;
256 }
258 s_flip_pending--;
260 m_surface1->free_prev();
261 if (m_plane)
262 m_surface2->free_prev();
264 if (s_need_exit)
265 return;
267 queue_next();
268 }
270 void queue_next()
271 {
272 m_surface1->make_current();
273 m_scene1->draw(m_frame_num * m_rotation_mult);
274 m_surface1->swap_buffers();
275 struct Framebuffer* fb = m_surface1->lock_next();
277 struct Framebuffer* planefb = 0;
279 if (m_plane) {
280 m_surface2->make_current();
281 m_scene2->draw(m_frame_num * m_rotation_mult * 2);
282 m_surface2->swap_buffers();
283 planefb = m_surface2->lock_next();
284 }
286 if (m_crtc->card().has_atomic()) {
287 int r;
289 AtomicReq req(m_crtc->card());
291 req.add(m_root_plane, "FB_ID", fb->id());
292 if (m_plane)
293 req.add(m_plane, "FB_ID", planefb->id());
295 r = req.test();
296 FAIL_IF(r, "atomic test failed");
298 r = req.commit(this);
299 FAIL_IF(r, "atomic commit failed");
300 } else {
301 int ret;
303 ret = m_crtc->page_flip(*fb, this);
304 FAIL_IF(ret, "failed to queue page flip");
306 if (m_plane) {
307 ret = m_crtc->set_plane(m_plane, *planefb,
308 0, 0, planefb->width(), planefb->height(),
309 0, 0, planefb->width(), planefb->height());
310 FAIL_IF(ret, "failed to set plane");
311 }
312 }
314 s_flip_pending++;
315 }
317 int m_frame_num;
318 chrono::steady_clock::time_point m_t1;
320 Connector* m_connector;
321 Crtc* m_crtc;
322 Plane* m_plane;
323 Videomode m_mode;
324 Plane* m_root_plane;
326 unique_ptr<GbmEglSurface> m_surface1;
327 unique_ptr<GbmEglSurface> m_surface2;
329 unique_ptr<GlScene> m_scene1;
330 unique_ptr<GlScene> m_scene2;
332 float m_rotation_mult;
333 };
335 void main_gbm()
336 {
337 Card card;
339 GbmDevice gdev(card);
340 EglState egl(gdev.handle());
342 vector<unique_ptr<OutputHandler>> outputs;
343 vector<Plane*> used_planes;
345 float rot_mult = 1;
347 for (auto pipe : card.get_connected_pipelines()) {
348 auto connector = pipe.connector;
349 auto crtc = pipe.crtc;
350 auto mode = connector->get_default_mode();
352 Plane* plane = 0;
354 if (s_support_planes) {
355 for (Plane* p : crtc->get_possible_planes()) {
356 if (find(used_planes.begin(), used_planes.end(), p) != used_planes.end())
357 continue;
359 if (p->plane_type() != PlaneType::Overlay)
360 continue;
362 plane = p;
363 break;
364 }
365 }
367 if (plane)
368 used_planes.push_back(plane);
370 auto out = new OutputHandler(card, gdev, egl, connector, crtc, mode, plane, rot_mult);
371 outputs.emplace_back(out);
373 rot_mult *= 1.33;
374 }
376 for (auto& out : outputs)
377 out->setup();
379 for (auto& out : outputs)
380 out->start_flipping();
382 struct pollfd fds[2] = { };
383 fds[0].fd = 0;
384 fds[0].events = POLLIN;
385 fds[1].fd = card.fd();
386 fds[1].events = POLLIN;
388 while (!s_need_exit || s_flip_pending) {
389 int r = poll(fds, ARRAY_SIZE(fds), -1);
390 FAIL_IF(r < 0, "poll error %d", r);
392 if (fds[0].revents)
393 s_need_exit = true;
395 if (fds[1].revents)
396 card.call_page_flip_handlers();
397 }
398 }