kmscube: use drmModeAddFB2 version of ExtFB
[android/external-libkmsxx.git] / kmscube / cube-gbm.cpp
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++/kms++.h>
13 #include <kms++util/kms++util.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), 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;
135                 PixelFormat format = (PixelFormat)gbm_bo_get_format(bo);
137                 uint32_t handles[4] { handle };
138                 uint32_t strides[4] { stride };
139                 uint32_t offsets[4] { 0 };
141                 fb = new ExtFramebuffer(card, width, height, format, handles, strides, offsets);
143                 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
145                 return fb;
146         }
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         }
156         void free_prev()
157         {
158                 if (bo_prev) {
159                         gsurface->release_buffer(bo_prev);
160                         bo_prev = 0;
161                 }
162         }
164         uint32_t width() const { return m_width; }
165         uint32_t height() const { return m_height; }
167 private:
168         Card& card;
169         const EglState& egl;
171         unique_ptr<GbmSurface> gsurface;
172         EGLSurface esurface;
174         int m_width;
175         int m_height;
177         struct gbm_bo* bo_prev;
178         struct gbm_bo* bo_next;
179 };
181 class OutputHandler : private PageFlipHandlerBase
183 public:
184         OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* plane, float rotation_mult)
185                 : m_frame_num(0), m_connector(connector), m_crtc(crtc), 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());
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         }
199         OutputHandler(const OutputHandler& other) = delete;
200         OutputHandler& operator=(const OutputHandler& other) = delete;
202         void setup()
203         {
204                 int ret;
206                 m_surface1->make_current();
207                 m_surface1->swap_buffers();
208                 Framebuffer* fb = m_surface1->lock_next();
210                 Framebuffer* planefb = 0;
212                 if (m_plane) {
213                         m_surface2->make_current();
214                         m_surface2->swap_buffers();
215                         planefb = m_surface2->lock_next();
216                 }
219                 ret = m_crtc->set_mode(m_connector, *fb, m_mode);
220                 FAIL_IF(ret, "failed to set mode");
222                 if (m_crtc->card().has_atomic()) {
223                         Plane* root_plane = 0;
224                         for (Plane* p : m_crtc->get_possible_planes()) {
225                                 if (p->crtc_id() == m_crtc->id()) {
226                                         root_plane = p;
227                                         break;
228                                 }
229                         }
231                         FAIL_IF(!root_plane, "No primary plane for crtc %d", m_crtc->id());
233                         m_root_plane = root_plane;
234                 }
236                 if (m_plane) {
237                         ret = m_crtc->set_plane(m_plane, *planefb,
238                                                 0, 0, planefb->width(), planefb->height(),
239                                                 0, 0, planefb->width(), planefb->height());
240                         FAIL_IF(ret, "failed to set plane");
241                 }
242         }
244         void start_flipping()
245         {
246                 m_t1 = chrono::steady_clock::now();
247                 queue_next();
248         }
250 private:
251         void handle_page_flip(uint32_t frame, double time)
252         {
253                 ++m_frame_num;
255                 if (m_frame_num  % 100 == 0) {
256                         auto t2 = chrono::steady_clock::now();
257                         chrono::duration<float> fsec = t2 - m_t1;
258                         printf("fps: %f\n", 100.0 / fsec.count());
259                         m_t1 = t2;
260                 }
262                 s_flip_pending--;
264                 m_surface1->free_prev();
265                 if (m_plane)
266                         m_surface2->free_prev();
268                 if (s_need_exit)
269                         return;
271                 queue_next();
272         }
274         void queue_next()
275         {
276                 m_surface1->make_current();
277                 m_scene1->draw(m_frame_num * m_rotation_mult);
278                 m_surface1->swap_buffers();
279                 Framebuffer* fb = m_surface1->lock_next();
281                 Framebuffer* planefb = 0;
283                 if (m_plane) {
284                         m_surface2->make_current();
285                         m_scene2->draw(m_frame_num * m_rotation_mult * 2);
286                         m_surface2->swap_buffers();
287                         planefb = m_surface2->lock_next();
288                 }
290                 if (m_crtc->card().has_atomic()) {
291                         int r;
293                         AtomicReq req(m_crtc->card());
295                         req.add(m_root_plane, "FB_ID", fb->id());
296                         if (m_plane)
297                                 req.add(m_plane, "FB_ID", planefb->id());
299                         r = req.test();
300                         FAIL_IF(r, "atomic test failed");
302                         r = req.commit(this);
303                         FAIL_IF(r, "atomic commit failed");
304                 } else {
305                         int ret;
307                         ret = m_crtc->page_flip(*fb, this);
308                         FAIL_IF(ret, "failed to queue page flip");
310                         if (m_plane) {
311                                 ret = m_crtc->set_plane(m_plane, *planefb,
312                                                         0, 0, planefb->width(), planefb->height(),
313                                                         0, 0, planefb->width(), planefb->height());
314                                 FAIL_IF(ret, "failed to set plane");
315                         }
316                 }
318                 s_flip_pending++;
319         }
321         int m_frame_num;
322         chrono::steady_clock::time_point m_t1;
324         Connector* m_connector;
325         Crtc* m_crtc;
326         Plane* m_plane;
327         Videomode m_mode;
328         Plane* m_root_plane;
330         unique_ptr<GbmEglSurface> m_surface1;
331         unique_ptr<GbmEglSurface> m_surface2;
333         unique_ptr<GlScene> m_scene1;
334         unique_ptr<GlScene> m_scene2;
336         float m_rotation_mult;
337 };
339 void main_gbm()
341         Card card;
343         GbmDevice gdev(card);
344         EglState egl(gdev.handle());
346         vector<unique_ptr<OutputHandler>> outputs;
347         vector<Plane*> used_planes;
349         float rot_mult = 1;
351         for (auto pipe : card.get_connected_pipelines()) {
352                 auto connector = pipe.connector;
353                 auto crtc = pipe.crtc;
354                 auto mode = connector->get_default_mode();
356                 Plane* plane = 0;
358                 if (s_support_planes) {
359                         for (Plane* p : crtc->get_possible_planes()) {
360                                 if (find(used_planes.begin(), used_planes.end(), p) != used_planes.end())
361                                         continue;
363                                 if (p->plane_type() != PlaneType::Overlay)
364                                         continue;
366                                 plane = p;
367                                 break;
368                         }
369                 }
371                 if (plane)
372                         used_planes.push_back(plane);
374                 auto out = new OutputHandler(card, gdev, egl, connector, crtc, mode, plane, rot_mult);
375                 outputs.emplace_back(out);
377                 rot_mult *= 1.33;
378         }
380         for (auto& out : outputs)
381                 out->setup();
383         for (auto& out : outputs)
384                 out->start_flipping();
386         struct pollfd fds[2] = { };
387         fds[0].fd = 0;
388         fds[0].events =  POLLIN;
389         fds[1].fd = card.fd();
390         fds[1].events =  POLLIN;
392         while (!s_need_exit || s_flip_pending) {
393                 int r = poll(fds, ARRAY_SIZE(fds), -1);
394                 FAIL_IF(r < 0, "poll error %d", r);
396                 if (fds[0].revents)
397                         s_need_exit = true;
399                 if (fds[1].revents)
400                         card.call_page_flip_handlers();
401         }