kmscube: support null egl
[android/external-libkmsxx.git] / kmscube / kmscube.cpp
1 /*
2  * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
3  * Copyright (c) 2012 Rob Clark <rob@ti.com>
4  * Copyright (c) 2015 Tomi Valkeinen <tomi.valkeinen@ti.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sub license,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial portions
15  * of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
26 /* Based on a egl cube test app originally written by Arvin Schnell */
28 #include <chrono>
29 #include <cstdio>
30 #include <vector>
31 #include <memory>
32 #include <algorithm>
33 #include <poll.h>
35 #include <xf86drm.h>
36 #include <xf86drmMode.h>
37 #include <gbm.h>
39 #include "esUtil.h"
41 #include <kms++.h>
42 #include "test.h"
43 #include "opts.h"
45 using namespace kms;
46 using namespace std;
48 static bool s_verbose;
49 static int s_flip_pending;
50 static bool s_need_exit;
52 class GbmDevice
53 {
54 public:
55         GbmDevice(Card& card)
56         {
57                 m_dev = gbm_create_device(card.fd());
58                 FAIL_IF(!m_dev, "failed to create gbm device");
59         }
61         ~GbmDevice()
62         {
63                 gbm_device_destroy(m_dev);
64         }
66         GbmDevice(const GbmDevice& other) = delete;
67         GbmDevice& operator=(const GbmDevice& other) = delete;
69         struct gbm_device* handle() const { return m_dev; }
71 private:
72         struct gbm_device* m_dev;
73 };
75 class GbmSurface
76 {
77 public:
78         GbmSurface(GbmDevice& gdev, int width, int height)
79         {
80                 m_surface = gbm_surface_create(gdev.handle(), width, height,
81                                                GBM_FORMAT_XRGB8888,
82                                                GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
83                 FAIL_IF(!m_surface, "failed to create gbm surface");
84         }
86         ~GbmSurface()
87         {
88                 gbm_surface_destroy(m_surface);
89         }
91         GbmSurface(const GbmSurface& other) = delete;
92         GbmSurface& operator=(const GbmSurface& other) = delete;
94         gbm_bo* lock_front_buffer()
95         {
96                 return gbm_surface_lock_front_buffer(m_surface);
97         }
99         void release_buffer(gbm_bo *bo)
100         {
101                 gbm_surface_release_buffer(m_surface, bo);
102         }
104         struct gbm_surface* handle() const { return m_surface; }
106 private:
107         struct gbm_surface* m_surface;
108 };
110 class EglState
112 public:
113         EglState(EGLNativeDisplayType display_id)
114         {
115                 EGLBoolean b;
116                 EGLint major, minor, n;
118                 static const EGLint context_attribs[] = {
119                         EGL_CONTEXT_CLIENT_VERSION, 2,
120                         EGL_NONE
121                 };
123                 static const EGLint config_attribs[] = {
124                         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
125                         EGL_RED_SIZE, 8,
126                         EGL_GREEN_SIZE, 8,
127                         EGL_BLUE_SIZE, 8,
128                         EGL_ALPHA_SIZE, 0,
129                         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
130                         EGL_NONE
131                 };
133                 m_display = eglGetDisplay(display_id);
134                 FAIL_IF(!m_display, "failed to get egl display");
136                 b = eglInitialize(m_display, &major, &minor);
137                 FAIL_IF(!b, "failed to initialize");
139                 if (s_verbose) {
140                         printf("Using display %p with EGL version %d.%d\n", m_display, major, minor);
142                         printf("EGL_VENDOR:      %s\n", eglQueryString(m_display, EGL_VENDOR));
143                         printf("EGL_VERSION:     %s\n", eglQueryString(m_display, EGL_VERSION));
144                         printf("EGL_EXTENSIONS:  %s\n", eglQueryString(m_display, EGL_EXTENSIONS));
145                         printf("EGL_CLIENT_APIS: %s\n", eglQueryString(m_display, EGL_CLIENT_APIS));
146                 }
148                 b = eglBindAPI(EGL_OPENGL_ES_API);
149                 FAIL_IF(!b, "failed to bind api EGL_OPENGL_ES_API");
151                 b = eglChooseConfig(m_display, config_attribs, &m_config, 1, &n);
152                 FAIL_IF(!b || n != 1, "failed to choose config");
154                 auto getconf = [this](EGLint a) { EGLint v = -1; eglGetConfigAttrib(m_display, m_config, a, &v); return v; };
156                 if (s_verbose) {
157                         printf("EGL Config %d: color buf %d/%d/%d/%d = %d, depth %d, stencil %d\n",
158                                getconf(EGL_CONFIG_ID),
159                                getconf(EGL_ALPHA_SIZE),
160                                getconf(EGL_RED_SIZE),
161                                getconf(EGL_GREEN_SIZE),
162                                getconf(EGL_BLUE_SIZE),
163                                getconf(EGL_BUFFER_SIZE),
164                                getconf(EGL_DEPTH_SIZE),
165                                getconf(EGL_STENCIL_SIZE));
166                 }
168                 m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs);
169                 FAIL_IF(!m_context, "failed to create context");
171                 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context);
172         }
174         ~EglState()
175         {
176                 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
177                 eglTerminate(m_display);
178         }
180         EGLDisplay display() const { return m_display; }
181         EGLConfig config() const { return m_config; }
182         EGLContext context() const { return m_context; }
184 private:
185         EGLDisplay m_display;
186         EGLConfig m_config;
187         EGLContext m_context;
188 };
190 class GlScene
192 public:
193         GlScene()
194         {
195                 GLuint vertex_shader, fragment_shader;
196                 GLint ret;
198 #include "cube.h"
200                 static const char *vertex_shader_source =
201                                 "uniform mat4 modelviewMatrix;      \n"
202                                 "uniform mat4 modelviewprojectionMatrix;\n"
203                                 "uniform mat3 normalMatrix;         \n"
204                                 "                                   \n"
205                                 "attribute vec4 in_position;        \n"
206                                 "attribute vec3 in_normal;          \n"
207                                 "attribute vec4 in_color;           \n"
208                                 "\n"
209                                 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
210                                 "                                   \n"
211                                 "varying vec4 vVaryingColor;        \n"
212                                 "                                   \n"
213                                 "void main()                        \n"
214                                 "{                                  \n"
215                                 "    gl_Position = modelviewprojectionMatrix * in_position;\n"
216                                 "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
217                                 "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
218                                 "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
219                                 "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
220                                 "    float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
221                                 "    vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
222                                 "}                                  \n";
224                 static const char *fragment_shader_source =
225                                 "precision mediump float;           \n"
226                                 "                                   \n"
227                                 "varying vec4 vVaryingColor;        \n"
228                                 "                                   \n"
229                                 "void main()                        \n"
230                                 "{                                  \n"
231                                 "    gl_FragColor = vVaryingColor;  \n"
232                                 "}                                  \n";
235                 if (s_verbose) {
236                         printf("GL_VENDOR:       %s\n", glGetString(GL_VENDOR));
237                         printf("GL_VERSION:      %s\n", glGetString(GL_VERSION));
238                         printf("GL_RENDERER:     %s\n", glGetString(GL_RENDERER));
239                         printf("GL_EXTENSIONS:   %s\n", glGetString(GL_EXTENSIONS));
240                 }
242                 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
244                 glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
245                 glCompileShader(vertex_shader);
247                 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
248                 FAIL_IF(!ret, "vertex shader compilation failed!");
250                 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
252                 glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
253                 glCompileShader(fragment_shader);
255                 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
256                 FAIL_IF(!ret, "fragment shader compilation failed!");
258                 GLuint program = glCreateProgram();
260                 glAttachShader(program, vertex_shader);
261                 glAttachShader(program, fragment_shader);
263                 glBindAttribLocation(program, 0, "in_position");
264                 glBindAttribLocation(program, 1, "in_normal");
265                 glBindAttribLocation(program, 2, "in_color");
267                 glLinkProgram(program);
269                 glGetProgramiv(program, GL_LINK_STATUS, &ret);
270                 FAIL_IF(!ret, "program linking failed!");
272                 glUseProgram(program);
274                 m_modelviewmatrix = glGetUniformLocation(program, "modelviewMatrix");
275                 m_modelviewprojectionmatrix = glGetUniformLocation(program, "modelviewprojectionMatrix");
276                 m_normalmatrix = glGetUniformLocation(program, "normalMatrix");
278                 glEnable(GL_CULL_FACE);
280                 GLintptr positionsoffset = 0;
281                 GLintptr colorsoffset = sizeof(vVertices);
282                 GLintptr normalsoffset = sizeof(vVertices) + sizeof(vColors);
283                 GLuint vbo;
285                 glGenBuffers(1, &vbo);
286                 glBindBuffer(GL_ARRAY_BUFFER, vbo);
287                 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
288                 glBufferSubData(GL_ARRAY_BUFFER, positionsoffset, sizeof(vVertices), &vVertices[0]);
289                 glBufferSubData(GL_ARRAY_BUFFER, colorsoffset, sizeof(vColors), &vColors[0]);
290                 glBufferSubData(GL_ARRAY_BUFFER, normalsoffset, sizeof(vNormals), &vNormals[0]);
291                 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)positionsoffset);
292                 glEnableVertexAttribArray(0);
293                 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)normalsoffset);
294                 glEnableVertexAttribArray(1);
295                 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)colorsoffset);
296                 glEnableVertexAttribArray(2);
297         }
299         GlScene(const GlScene& other) = delete;
300         GlScene& operator=(const GlScene& other) = delete;
302         void set_viewport(uint32_t width, uint32_t height)
303         {
304                 m_width = width;
305                 m_height = height;
306         }
308         void draw(uint32_t framenum)
309         {
310                 glViewport(0, 0, m_width, m_height);
312                 glClearColor(0.5, 0.5, 0.5, 1.0);
313                 glClear(GL_COLOR_BUFFER_BIT);
315                 ESMatrix modelview;
317                 esMatrixLoadIdentity(&modelview);
318                 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
319                 esRotate(&modelview, 45.0f + (0.75f * framenum), 1.0f, 0.0f, 0.0f);
320                 esRotate(&modelview, 45.0f - (0.5f * framenum), 0.0f, 1.0f, 0.0f);
321                 esRotate(&modelview, 10.0f + (0.45f * framenum), 0.0f, 0.0f, 1.0f);
323                 GLfloat aspect = (float)m_height / m_width;
325                 ESMatrix projection;
326                 esMatrixLoadIdentity(&projection);
327                 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
329                 ESMatrix modelviewprojection;
330                 esMatrixLoadIdentity(&modelviewprojection);
331                 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
333                 float normal[9];
334                 normal[0] = modelview.m[0][0];
335                 normal[1] = modelview.m[0][1];
336                 normal[2] = modelview.m[0][2];
337                 normal[3] = modelview.m[1][0];
338                 normal[4] = modelview.m[1][1];
339                 normal[5] = modelview.m[1][2];
340                 normal[6] = modelview.m[2][0];
341                 normal[7] = modelview.m[2][1];
342                 normal[8] = modelview.m[2][2];
344                 glUniformMatrix4fv(m_modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
345                 glUniformMatrix4fv(m_modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
346                 glUniformMatrix3fv(m_normalmatrix, 1, GL_FALSE, normal);
348                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
349                 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
350                 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
351                 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
352                 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
353                 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
354         }
356 private:
357         GLint m_modelviewmatrix, m_modelviewprojectionmatrix, m_normalmatrix;
359         uint32_t m_width;
360         uint32_t m_height;
361 };
363 class GbmEglSurface
365 public:
366         GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height)
367                 : card(card), gdev(gdev), egl(egl), m_width(width), m_height(height),
368                   bo_prev(0), bo_next(0)
369         {
370                 gsurface = unique_ptr<GbmSurface>(new GbmSurface(gdev, width, height));
371                 esurface = eglCreateWindowSurface(egl.display(), egl.config(), gsurface->handle(), NULL);
372                 FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
373         }
375         ~GbmEglSurface()
376         {
377                 if (bo_next)
378                         gsurface->release_buffer(bo_next);
379                 eglDestroySurface(egl.display(), esurface);
380         }
382         void make_current()
383         {
384                 eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
385         }
387         void swap_buffers()
388         {
389                 eglSwapBuffers(egl.display(), esurface);
390         }
392         static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
393         {
394                 auto fb = reinterpret_cast<Framebuffer*>(data);
395                 delete fb;
396         }
398         static Framebuffer* drm_fb_get_from_bo(struct gbm_bo *bo, Card& card)
399         {
400                 auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo));
401                 if (fb)
402                         return fb;
404                 uint32_t width = gbm_bo_get_width(bo);
405                 uint32_t height = gbm_bo_get_height(bo);
406                 uint32_t stride = gbm_bo_get_stride(bo);
407                 uint32_t handle = gbm_bo_get_handle(bo).u32;
409                 fb = new ExtFramebuffer(card, width, height, 24, 32, stride, handle);
411                 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
413                 return fb;
414         }
416         struct Framebuffer* lock_next()
417         {
418                 bo_prev = bo_next;
419                 bo_next = gsurface->lock_front_buffer();
420                 FAIL_IF(!bo_next, "could not lock gbm buffer");
421                 return drm_fb_get_from_bo(bo_next, card);
422         }
424         void free_prev()
425         {
426                 if (bo_prev) {
427                         gsurface->release_buffer(bo_prev);
428                         bo_prev = 0;
429                 }
430         }
432         uint32_t width() const { return m_width; }
433         uint32_t height() const { return m_height; }
435 private:
436         Card& card;
437         GbmDevice& gdev;
438         const EglState& egl;
440         unique_ptr<GbmSurface> gsurface;
441         EGLSurface esurface;
443         int m_width;
444         int m_height;
446         struct gbm_bo* bo_prev;
447         struct gbm_bo* bo_next;
448 };
450 class NullEglSurface
452 public:
453         NullEglSurface(const EglState& egl)
454                 : egl(egl)
455         {
456                 esurface = eglCreateWindowSurface(egl.display(), egl.config(), 0, NULL);
457                 FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
458         }
460         ~NullEglSurface()
461         {
462                 eglDestroySurface(egl.display(), esurface);
463         }
465         void make_current()
466         {
467                 eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
468         }
470         void swap_buffers()
471         {
472                 eglSwapBuffers(egl.display(), esurface);
473         }
475 private:
476         const EglState& egl;
478         EGLSurface esurface;
479 };
481 class OutputHandler : private PageFlipHandlerBase
483 public:
484         OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* plane, float rotation_mult)
485                 : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_plane(plane), m_mode(mode),
486                   m_rotation_mult(rotation_mult)
487         {
488                 m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay));
489                 m_scene1 = unique_ptr<GlScene>(new GlScene());
490                 m_scene1->set_viewport(m_surface1->width(), m_surface1->height());
492                 if (m_plane) {
493                         m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400));
494                         m_scene2 = unique_ptr<GlScene>(new GlScene());
495                         m_scene2->set_viewport(m_surface2->width(), m_surface2->height());
496                 }
497         }
499         OutputHandler(const OutputHandler& other) = delete;
500         OutputHandler& operator=(const OutputHandler& other) = delete;
502         void setup()
503         {
504                 int ret;
506                 m_surface1->make_current();
507                 m_surface1->swap_buffers();
508                 struct Framebuffer* fb = m_surface1->lock_next();
510                 struct Framebuffer* planefb = 0;
512                 if (m_plane) {
513                         m_surface2->make_current();
514                         m_surface2->swap_buffers();
515                         planefb = m_surface2->lock_next();
516                 }
519                 ret = m_crtc->set_mode(m_connector, *fb, m_mode);
520                 FAIL_IF(ret, "failed to set mode");
522                 if (m_crtc->card().has_atomic()) {
523                         Plane* root_plane = 0;
524                         for (Plane* p : m_crtc->get_possible_planes()) {
525                                 if (p->crtc_id() == m_crtc->id()) {
526                                         root_plane = p;
527                                         break;
528                                 }
529                         }
531                         FAIL_IF(!root_plane, "No primary plane for crtc %d", m_crtc->id());
533                         m_root_plane = root_plane;
534                 }
536                 if (m_plane) {
537                         ret = m_crtc->set_plane(m_plane, *planefb,
538                                                 0, 0, planefb->width(), planefb->height(),
539                                                 0, 0, planefb->width(), planefb->height());
540                         FAIL_IF(ret, "failed to set plane");
541                 }
542         }
544         void start_flipping()
545         {
546                 m_t1 = chrono::steady_clock::now();
547                 queue_next();
548         }
550 private:
551         void handle_page_flip(uint32_t frame, double time)
552         {
553                 ++m_frame_num;
555                 if (m_frame_num  % 100 == 0) {
556                         auto t2 = chrono::steady_clock::now();
557                         chrono::duration<float> fsec = t2 - m_t1;
558                         printf("fps: %f\n", 100.0 / fsec.count());
559                         m_t1 = t2;
560                 }
562                 s_flip_pending--;
564                 m_surface1->free_prev();
565                 if (m_plane)
566                         m_surface2->free_prev();
568                 if (s_need_exit)
569                         return;
571                 queue_next();
572         }
574         void queue_next()
575         {
576                 m_surface1->make_current();
577                 m_scene1->draw(m_frame_num * m_rotation_mult);
578                 m_surface1->swap_buffers();
579                 struct Framebuffer* fb = m_surface1->lock_next();
581                 struct Framebuffer* planefb = 0;
583                 if (m_plane) {
584                         m_surface2->make_current();
585                         m_scene2->draw(m_frame_num * m_rotation_mult * 2);
586                         m_surface2->swap_buffers();
587                         planefb = m_surface2->lock_next();
588                 }
590                 if (m_crtc->card().has_atomic()) {
591                         int r;
593                         AtomicReq req(m_crtc->card());
595                         req.add(m_root_plane, "FB_ID", fb->id());
596                         if (m_plane)
597                                 req.add(m_plane, "FB_ID", planefb->id());
599                         r = req.test();
600                         FAIL_IF(r, "atomic test failed");
602                         r = req.commit(this);
603                         FAIL_IF(r, "atomic commit failed");
604                 } else {
605                         int ret;
607                         ret = m_crtc->page_flip(*fb, this);
608                         FAIL_IF(ret, "failed to queue page flip");
610                         if (m_plane) {
611                                 ret = m_crtc->set_plane(m_plane, *planefb,
612                                                         0, 0, planefb->width(), planefb->height(),
613                                                         0, 0, planefb->width(), planefb->height());
614                                 FAIL_IF(ret, "failed to set plane");
615                         }
616                 }
618                 s_flip_pending++;
619         }
621         int m_frame_num;
622         chrono::steady_clock::time_point m_t1;
624         Connector* m_connector;
625         Crtc* m_crtc;
626         Plane* m_plane;
627         Videomode m_mode;
628         Plane* m_root_plane;
630         unique_ptr<GbmEglSurface> m_surface1;
631         unique_ptr<GbmEglSurface> m_surface2;
633         unique_ptr<GlScene> m_scene1;
634         unique_ptr<GlScene> m_scene2;
636         float m_rotation_mult;
637 };
639 static void main_gbm()
641         Card card;
643         GbmDevice gdev(card);
644         EglState egl(gdev.handle());
646         vector<unique_ptr<OutputHandler>> outputs;
647         vector<Plane*> used_planes;
649         float rot_mult = 1;
651         for (auto pipe : card.get_connected_pipelines()) {
652                 auto connector = pipe.connector;
653                 auto crtc = pipe.crtc;
654                 auto mode = connector->get_default_mode();
656                 Plane* plane = 0;
658                 for (Plane* p : crtc->get_possible_planes()) {
659                         if (find(used_planes.begin(), used_planes.end(), p) != used_planes.end())
660                                 continue;
662                         if (p->plane_type() != PlaneType::Overlay)
663                                 continue;
665                         plane = p;
666                         break;
667                 }
669                 if (plane)
670                         used_planes.push_back(plane);
672                 auto out = new OutputHandler(card, gdev, egl, connector, crtc, mode, plane, rot_mult);
673                 outputs.emplace_back(out);
675                 rot_mult *= 1.33;
676         }
678         for (auto& out : outputs)
679                 out->setup();
681         for (auto& out : outputs)
682                 out->start_flipping();
684         struct pollfd fds[2] = { 0 };
685         fds[0].fd = 0;
686         fds[0].events =  POLLIN;
687         fds[1].fd = card.fd();
688         fds[1].events =  POLLIN;
690         while (!s_need_exit || s_flip_pending) {
691                 int r = poll(fds, ARRAY_SIZE(fds), -1);
692                 FAIL_IF(r < 0, "poll error %d", r);
694                 if (fds[0].revents)
695                         s_need_exit = true;
697                 if (fds[1].revents)
698                         card.call_page_flip_handlers();
699         }
702 static void main_egl()
704         EglState egl(EGL_DEFAULT_DISPLAY);
705         NullEglSurface surface(egl);
706         GlScene scene;
708         scene.set_viewport(600, 600);
710         int framenum = 0;
712         struct pollfd fds[1] = { 0 };
713         fds[0].fd = 0;
714         fds[0].events =  POLLIN;
716         while (true) {
717                 int r = poll(fds, ARRAY_SIZE(fds), 0);
718                 ASSERT(r >= 0);
720                 if (fds[0].revents)
721                         break;
723                 surface.make_current();
724                 scene.draw(framenum++);
725                 surface.swap_buffers();
726         }
729 int main(int argc, char *argv[])
731         OptionSet optionset = {
732                 Option("v|verbose",
733                 [&]()
734                 {
735                         s_verbose = true;
736                 }),
737         };
739         optionset.parse(argc, argv);
741         int mode;
743         if (optionset.params().size() == 0) {
744                 mode = 0;
745         } else {
746                 const string m = optionset.params().front();
748                 if (m == "gbm")
749                         mode = 0;
750                 else if (m == "null")
751                         mode = 1;
752                 else {
753                         printf("Unknown mode %s\n", m.c_str());
754                         return -1;
755                 }
756         }
758         switch (mode) {
759         case 0:
760                 main_gbm();
761                 break;
763         case 1:
764                 main_egl();
765                 break;
766         }
768         return 0;