1 #include <chrono>
2 #include <cstdio>
3 #include <vector>
4 #include <memory>
5 #include <algorithm>
6 #include <poll.h>
7
8 #include <xf86drm.h>
9 #include <xf86drmMode.h>
10 #include <gbm.h>
11
12 #include <kms++/kms++.h>
13 #include <kms++util/kms++util.h>
14 #include "cube-egl.h"
15 #include "cube-gles2.h"
16
17 using namespace kms;
18 using namespace std;
19
20 static int s_flip_pending;
21 static bool s_need_exit;
22
23 static bool s_support_planes;
24
25 class GbmDevice
26 {
27 public:
GbmDevice(Card & card)28 GbmDevice(Card& card)
29 {
30 m_dev = gbm_create_device(card.fd());
31 FAIL_IF(!m_dev, "failed to create gbm device");
32 }
33
~GbmDevice()34 ~GbmDevice()
35 {
36 gbm_device_destroy(m_dev);
37 }
38
39 GbmDevice(const GbmDevice& other) = delete;
40 GbmDevice& operator=(const GbmDevice& other) = delete;
41
handle() const42 struct gbm_device* handle() const { return m_dev; }
43
44 private:
45 struct gbm_device* m_dev;
46 };
47
48 class GbmSurface
49 {
50 public:
GbmSurface(GbmDevice & gdev,int width,int height)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 }
58
~GbmSurface()59 ~GbmSurface()
60 {
61 gbm_surface_destroy(m_surface);
62 }
63
64 GbmSurface(const GbmSurface& other) = delete;
65 GbmSurface& operator=(const GbmSurface& other) = delete;
66
has_free()67 bool has_free()
68 {
69 return gbm_surface_has_free_buffers(m_surface);
70 }
71
lock_front_buffer()72 gbm_bo* lock_front_buffer()
73 {
74 return gbm_surface_lock_front_buffer(m_surface);
75 }
76
release_buffer(gbm_bo * bo)77 void release_buffer(gbm_bo* bo)
78 {
79 gbm_surface_release_buffer(m_surface, bo);
80 }
81
handle() const82 struct gbm_surface* handle() const { return m_surface; }
83
84 private:
85 struct gbm_surface* m_surface;
86 };
87
88 class GbmEglSurface
89 {
90 public:
GbmEglSurface(Card & card,GbmDevice & gdev,const EglState & egl,int width,int height)91 GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height)
92 : card(card), 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 }
99
~GbmEglSurface()100 ~GbmEglSurface()
101 {
102 if (bo_next)
103 gsurface->release_buffer(bo_next);
104 eglDestroySurface(egl.display(), esurface);
105 }
106
make_current()107 void make_current()
108 {
109 FAIL_IF(!gsurface->has_free(), "No free buffers");
110
111 eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
112 }
113
swap_buffers()114 void swap_buffers()
115 {
116 eglSwapBuffers(egl.display(), esurface);
117 }
118
drm_fb_destroy_callback(struct gbm_bo * bo,void * data)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 }
124
drm_fb_get_from_bo(struct gbm_bo * bo,Card & card)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;
130
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;
135 PixelFormat format = (PixelFormat)gbm_bo_get_format(bo);
136
137 vector<uint32_t> handles{ handle };
138 vector<uint32_t> strides{ stride };
139 vector<uint32_t> offsets{ 0 };
140
141 fb = new ExtFramebuffer(card, width, height, format, handles, strides, offsets);
142
143 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
144
145 return fb;
146 }
147
lock_next()148 Framebuffer* lock_next()
149 {
150 bo_prev = bo_next;
151 bo_next = gsurface->lock_front_buffer();
152 FAIL_IF(!bo_next, "could not lock gbm buffer");
153 return drm_fb_get_from_bo(bo_next, card);
154 }
155
free_prev()156 void free_prev()
157 {
158 if (bo_prev) {
159 gsurface->release_buffer(bo_prev);
160 bo_prev = 0;
161 }
162 }
163
width() const164 uint32_t width() const { return m_width; }
height() const165 uint32_t height() const { return m_height; }
166
167 private:
168 Card& card;
169 const EglState& egl;
170
171 unique_ptr<GbmSurface> gsurface;
172 EGLSurface esurface;
173
174 int m_width;
175 int m_height;
176
177 struct gbm_bo* bo_prev;
178 struct gbm_bo* bo_next;
179 };
180
181 class OutputHandler : private PageFlipHandlerBase
182 {
183 public:
OutputHandler(Card & card,GbmDevice & gdev,const EglState & egl,Connector * connector,Crtc * crtc,Videomode & mode,Plane * root_plane,Plane * plane,float rotation_mult)184 OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* root_plane, Plane* plane, float rotation_mult)
185 : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_root_plane(root_plane), m_plane(plane), m_mode(mode),
186 m_rotation_mult(rotation_mult)
187 {
188 m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay));
189 m_scene1 = unique_ptr<GlScene>(new GlScene());
190 m_scene1->set_viewport(m_surface1->width(), m_surface1->height());
191
192 if (m_plane) {
193 m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400));
194 m_scene2 = unique_ptr<GlScene>(new GlScene());
195 m_scene2->set_viewport(m_surface2->width(), m_surface2->height());
196 }
197 }
198
199 OutputHandler(const OutputHandler& other) = delete;
200 OutputHandler& operator=(const OutputHandler& other) = delete;
201
setup()202 void setup()
203 {
204 int ret;
205
206 m_surface1->make_current();
207 m_surface1->swap_buffers();
208 Framebuffer* fb = m_surface1->lock_next();
209
210 Framebuffer* planefb = 0;
211
212 if (m_plane) {
213 m_surface2->make_current();
214 m_surface2->swap_buffers();
215 planefb = m_surface2->lock_next();
216 }
217
218 ret = m_crtc->set_mode(m_connector, *fb, m_mode);
219 FAIL_IF(ret, "failed to set mode");
220
221 if (m_plane) {
222 ret = m_crtc->set_plane(m_plane, *planefb,
223 0, 0, planefb->width(), planefb->height(),
224 0, 0, planefb->width(), planefb->height());
225 FAIL_IF(ret, "failed to set plane");
226 }
227 }
228
start_flipping()229 void start_flipping()
230 {
231 m_t1 = chrono::steady_clock::now();
232 queue_next();
233 }
234
235 private:
handle_page_flip(uint32_t frame,double time)236 void handle_page_flip(uint32_t frame, double time)
237 {
238 ++m_frame_num;
239
240 if (m_frame_num % 100 == 0) {
241 auto t2 = chrono::steady_clock::now();
242 chrono::duration<float> fsec = t2 - m_t1;
243 printf("fps: %f\n", 100.0 / fsec.count());
244 m_t1 = t2;
245 }
246
247 s_flip_pending--;
248
249 m_surface1->free_prev();
250 if (m_plane)
251 m_surface2->free_prev();
252
253 if (s_need_exit)
254 return;
255
256 queue_next();
257 }
258
queue_next()259 void queue_next()
260 {
261 m_surface1->make_current();
262 m_scene1->draw(m_frame_num * m_rotation_mult);
263 m_surface1->swap_buffers();
264 Framebuffer* fb = m_surface1->lock_next();
265
266 Framebuffer* planefb = 0;
267
268 if (m_plane) {
269 m_surface2->make_current();
270 m_scene2->draw(m_frame_num * m_rotation_mult * 2);
271 m_surface2->swap_buffers();
272 planefb = m_surface2->lock_next();
273 }
274
275 int r;
276
277 AtomicReq req(m_crtc->card());
278
279 req.add(m_root_plane, "FB_ID", fb->id());
280 if (m_plane)
281 req.add(m_plane, "FB_ID", planefb->id());
282
283 r = req.test();
284 FAIL_IF(r, "atomic test failed");
285
286 r = req.commit(this);
287 FAIL_IF(r, "atomic commit failed");
288
289 s_flip_pending++;
290 }
291
292 int m_frame_num;
293 chrono::steady_clock::time_point m_t1;
294
295 Connector* m_connector;
296 Crtc* m_crtc;
297 Plane* m_root_plane;
298 Plane* m_plane;
299 Videomode m_mode;
300
301 unique_ptr<GbmEglSurface> m_surface1;
302 unique_ptr<GbmEglSurface> m_surface2;
303
304 unique_ptr<GlScene> m_scene1;
305 unique_ptr<GlScene> m_scene2;
306
307 float m_rotation_mult;
308 };
309
main_gbm()310 void main_gbm()
311 {
312 Card card;
313
314 FAIL_IF(!card.has_atomic(), "No atomic modesetting");
315
316 GbmDevice gdev(card);
317 EglState egl(gdev.handle());
318
319 ResourceManager resman(card);
320
321 vector<unique_ptr<OutputHandler>> outputs;
322
323 float rot_mult = 1;
324
325 for (Connector* conn : card.get_connectors()) {
326 if (!conn->connected())
327 continue;
328
329 resman.reserve_connector(conn);
330
331 Crtc* crtc = resman.reserve_crtc(conn);
332 auto mode = conn->get_default_mode();
333
334 Plane* root_plane = resman.reserve_generic_plane(crtc);
335 FAIL_IF(!root_plane, "Root plane not available");
336
337 Plane* plane = nullptr;
338
339 if (s_support_planes)
340 plane = resman.reserve_generic_plane(crtc);
341
342 auto out = new OutputHandler(card, gdev, egl, conn, crtc, mode, root_plane, plane, rot_mult);
343 outputs.emplace_back(out);
344
345 rot_mult *= 1.33;
346 }
347
348 for (auto& out : outputs)
349 out->setup();
350
351 for (auto& out : outputs)
352 out->start_flipping();
353
354 struct pollfd fds[2] = {};
355 fds[0].fd = 0;
356 fds[0].events = POLLIN;
357 fds[1].fd = card.fd();
358 fds[1].events = POLLIN;
359
360 while (!s_need_exit || s_flip_pending) {
361 int r = poll(fds, ARRAY_SIZE(fds), -1);
362 FAIL_IF(r < 0, "poll error %d", r);
363
364 if (fds[0].revents)
365 s_need_exit = true;
366
367 if (fds[1].revents)
368 card.call_page_flip_handlers();
369 }
370 }
371