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>
38 #include <X11/Xlib-xcb.h>
40 #include "esUtil.h"
42 #include <kms++.h>
43 #include "test.h"
44 #include "opts.h"
46 using namespace kms;
47 using namespace std;
49 static bool s_verbose;
50 static int s_flip_pending;
51 static bool s_need_exit;
53 static bool s_support_planes;
55 class GbmDevice
56 {
57 public:
58 GbmDevice(Card& card)
59 {
60 m_dev = gbm_create_device(card.fd());
61 FAIL_IF(!m_dev, "failed to create gbm device");
62 }
64 ~GbmDevice()
65 {
66 gbm_device_destroy(m_dev);
67 }
69 GbmDevice(const GbmDevice& other) = delete;
70 GbmDevice& operator=(const GbmDevice& other) = delete;
72 struct gbm_device* handle() const { return m_dev; }
74 private:
75 struct gbm_device* m_dev;
76 };
78 class GbmSurface
79 {
80 public:
81 GbmSurface(GbmDevice& gdev, int width, int height)
82 {
83 m_surface = gbm_surface_create(gdev.handle(), width, height,
84 GBM_FORMAT_XRGB8888,
85 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
86 FAIL_IF(!m_surface, "failed to create gbm surface");
87 }
89 ~GbmSurface()
90 {
91 gbm_surface_destroy(m_surface);
92 }
94 GbmSurface(const GbmSurface& other) = delete;
95 GbmSurface& operator=(const GbmSurface& other) = delete;
97 bool has_free()
98 {
99 return gbm_surface_has_free_buffers(m_surface);
100 }
102 gbm_bo* lock_front_buffer()
103 {
104 return gbm_surface_lock_front_buffer(m_surface);
105 }
107 void release_buffer(gbm_bo *bo)
108 {
109 gbm_surface_release_buffer(m_surface, bo);
110 }
112 struct gbm_surface* handle() const { return m_surface; }
114 private:
115 struct gbm_surface* m_surface;
116 };
118 class EglState
119 {
120 public:
121 EglState(EGLNativeDisplayType display_id)
122 {
123 EGLBoolean b;
124 EGLint major, minor, n;
126 static const EGLint context_attribs[] = {
127 EGL_CONTEXT_CLIENT_VERSION, 2,
128 EGL_NONE
129 };
131 static const EGLint config_attribs[] = {
132 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
133 EGL_RED_SIZE, 8,
134 EGL_GREEN_SIZE, 8,
135 EGL_BLUE_SIZE, 8,
136 EGL_ALPHA_SIZE, 0,
137 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
138 EGL_NONE
139 };
141 m_display = eglGetDisplay(display_id);
142 FAIL_IF(!m_display, "failed to get egl display");
144 b = eglInitialize(m_display, &major, &minor);
145 FAIL_IF(!b, "failed to initialize");
147 if (s_verbose) {
148 printf("Using display %p with EGL version %d.%d\n", m_display, major, minor);
150 printf("EGL_VENDOR: %s\n", eglQueryString(m_display, EGL_VENDOR));
151 printf("EGL_VERSION: %s\n", eglQueryString(m_display, EGL_VERSION));
152 printf("EGL_EXTENSIONS: %s\n", eglQueryString(m_display, EGL_EXTENSIONS));
153 printf("EGL_CLIENT_APIS: %s\n", eglQueryString(m_display, EGL_CLIENT_APIS));
154 }
156 b = eglBindAPI(EGL_OPENGL_ES_API);
157 FAIL_IF(!b, "failed to bind api EGL_OPENGL_ES_API");
159 b = eglChooseConfig(m_display, config_attribs, &m_config, 1, &n);
160 FAIL_IF(!b || n != 1, "failed to choose config");
162 auto getconf = [this](EGLint a) { EGLint v = -1; eglGetConfigAttrib(m_display, m_config, a, &v); return v; };
164 if (s_verbose) {
165 printf("EGL Config %d: color buf %d/%d/%d/%d = %d, depth %d, stencil %d\n",
166 getconf(EGL_CONFIG_ID),
167 getconf(EGL_ALPHA_SIZE),
168 getconf(EGL_RED_SIZE),
169 getconf(EGL_GREEN_SIZE),
170 getconf(EGL_BLUE_SIZE),
171 getconf(EGL_BUFFER_SIZE),
172 getconf(EGL_DEPTH_SIZE),
173 getconf(EGL_STENCIL_SIZE));
174 }
176 m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs);
177 FAIL_IF(!m_context, "failed to create context");
179 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context);
180 }
182 ~EglState()
183 {
184 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
185 eglTerminate(m_display);
186 }
188 EGLDisplay display() const { return m_display; }
189 EGLConfig config() const { return m_config; }
190 EGLContext context() const { return m_context; }
192 private:
193 EGLDisplay m_display;
194 EGLConfig m_config;
195 EGLContext m_context;
196 };
198 class GlScene
199 {
200 public:
201 GlScene()
202 {
203 GLuint vertex_shader, fragment_shader;
204 GLint ret;
206 #include "cube.h"
208 static const char *vertex_shader_source =
209 "uniform mat4 modelviewMatrix; \n"
210 "uniform mat4 modelviewprojectionMatrix;\n"
211 "uniform mat3 normalMatrix; \n"
212 " \n"
213 "attribute vec4 in_position; \n"
214 "attribute vec3 in_normal; \n"
215 "attribute vec4 in_color; \n"
216 "\n"
217 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
218 " \n"
219 "varying vec4 vVaryingColor; \n"
220 " \n"
221 "void main() \n"
222 "{ \n"
223 " gl_Position = modelviewprojectionMatrix * in_position;\n"
224 " vec3 vEyeNormal = normalMatrix * in_normal;\n"
225 " vec4 vPosition4 = modelviewMatrix * in_position;\n"
226 " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
227 " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
228 " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
229 " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
230 "} \n";
232 static const char *fragment_shader_source =
233 "precision mediump float; \n"
234 " \n"
235 "varying vec4 vVaryingColor; \n"
236 " \n"
237 "void main() \n"
238 "{ \n"
239 " gl_FragColor = vVaryingColor; \n"
240 "} \n";
243 if (s_verbose) {
244 printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR));
245 printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
246 printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
247 printf("GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS));
248 }
250 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
252 glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
253 glCompileShader(vertex_shader);
255 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
256 FAIL_IF(!ret, "vertex shader compilation failed!");
258 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
260 glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
261 glCompileShader(fragment_shader);
263 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
264 FAIL_IF(!ret, "fragment shader compilation failed!");
266 GLuint program = glCreateProgram();
268 glAttachShader(program, vertex_shader);
269 glAttachShader(program, fragment_shader);
271 glBindAttribLocation(program, 0, "in_position");
272 glBindAttribLocation(program, 1, "in_normal");
273 glBindAttribLocation(program, 2, "in_color");
275 glLinkProgram(program);
277 glGetProgramiv(program, GL_LINK_STATUS, &ret);
278 FAIL_IF(!ret, "program linking failed!");
280 glUseProgram(program);
282 m_modelviewmatrix = glGetUniformLocation(program, "modelviewMatrix");
283 m_modelviewprojectionmatrix = glGetUniformLocation(program, "modelviewprojectionMatrix");
284 m_normalmatrix = glGetUniformLocation(program, "normalMatrix");
286 glEnable(GL_CULL_FACE);
288 GLintptr positionsoffset = 0;
289 GLintptr colorsoffset = sizeof(vVertices);
290 GLintptr normalsoffset = sizeof(vVertices) + sizeof(vColors);
291 GLuint vbo;
293 glGenBuffers(1, &vbo);
294 glBindBuffer(GL_ARRAY_BUFFER, vbo);
295 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
296 glBufferSubData(GL_ARRAY_BUFFER, positionsoffset, sizeof(vVertices), &vVertices[0]);
297 glBufferSubData(GL_ARRAY_BUFFER, colorsoffset, sizeof(vColors), &vColors[0]);
298 glBufferSubData(GL_ARRAY_BUFFER, normalsoffset, sizeof(vNormals), &vNormals[0]);
299 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)positionsoffset);
300 glEnableVertexAttribArray(0);
301 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)normalsoffset);
302 glEnableVertexAttribArray(1);
303 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)colorsoffset);
304 glEnableVertexAttribArray(2);
305 }
307 GlScene(const GlScene& other) = delete;
308 GlScene& operator=(const GlScene& other) = delete;
310 void set_viewport(uint32_t width, uint32_t height)
311 {
312 m_width = width;
313 m_height = height;
314 }
316 void draw(uint32_t framenum)
317 {
318 glViewport(0, 0, m_width, m_height);
320 glClearColor(0.5, 0.5, 0.5, 1.0);
321 glClear(GL_COLOR_BUFFER_BIT);
323 ESMatrix modelview;
325 esMatrixLoadIdentity(&modelview);
326 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
327 esRotate(&modelview, 45.0f + (0.75f * framenum), 1.0f, 0.0f, 0.0f);
328 esRotate(&modelview, 45.0f - (0.5f * framenum), 0.0f, 1.0f, 0.0f);
329 esRotate(&modelview, 10.0f + (0.45f * framenum), 0.0f, 0.0f, 1.0f);
331 GLfloat aspect = (float)m_height / m_width;
333 ESMatrix projection;
334 esMatrixLoadIdentity(&projection);
335 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
337 ESMatrix modelviewprojection;
338 esMatrixLoadIdentity(&modelviewprojection);
339 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
341 float normal[9];
342 normal[0] = modelview.m[0][0];
343 normal[1] = modelview.m[0][1];
344 normal[2] = modelview.m[0][2];
345 normal[3] = modelview.m[1][0];
346 normal[4] = modelview.m[1][1];
347 normal[5] = modelview.m[1][2];
348 normal[6] = modelview.m[2][0];
349 normal[7] = modelview.m[2][1];
350 normal[8] = modelview.m[2][2];
352 glUniformMatrix4fv(m_modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
353 glUniformMatrix4fv(m_modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
354 glUniformMatrix3fv(m_normalmatrix, 1, GL_FALSE, normal);
356 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
357 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
358 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
359 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
360 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
361 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
362 }
364 private:
365 GLint m_modelviewmatrix, m_modelviewprojectionmatrix, m_normalmatrix;
367 uint32_t m_width;
368 uint32_t m_height;
369 };
371 class GbmEglSurface
372 {
373 public:
374 GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height)
375 : card(card), gdev(gdev), egl(egl), m_width(width), m_height(height),
376 bo_prev(0), bo_next(0)
377 {
378 gsurface = unique_ptr<GbmSurface>(new GbmSurface(gdev, width, height));
379 esurface = eglCreateWindowSurface(egl.display(), egl.config(), gsurface->handle(), NULL);
380 FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
381 }
383 ~GbmEglSurface()
384 {
385 if (bo_next)
386 gsurface->release_buffer(bo_next);
387 eglDestroySurface(egl.display(), esurface);
388 }
390 void make_current()
391 {
392 FAIL_IF(!gsurface->has_free(), "No free buffers");
394 eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
395 }
397 void swap_buffers()
398 {
399 eglSwapBuffers(egl.display(), esurface);
400 }
402 static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
403 {
404 auto fb = reinterpret_cast<Framebuffer*>(data);
405 delete fb;
406 }
408 static Framebuffer* drm_fb_get_from_bo(struct gbm_bo *bo, Card& card)
409 {
410 auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo));
411 if (fb)
412 return fb;
414 uint32_t width = gbm_bo_get_width(bo);
415 uint32_t height = gbm_bo_get_height(bo);
416 uint32_t stride = gbm_bo_get_stride(bo);
417 uint32_t handle = gbm_bo_get_handle(bo).u32;
419 fb = new ExtFramebuffer(card, width, height, 24, 32, stride, handle);
421 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
423 return fb;
424 }
426 struct Framebuffer* lock_next()
427 {
428 bo_prev = bo_next;
429 bo_next = gsurface->lock_front_buffer();
430 FAIL_IF(!bo_next, "could not lock gbm buffer");
431 return drm_fb_get_from_bo(bo_next, card);
432 }
434 void free_prev()
435 {
436 if (bo_prev) {
437 gsurface->release_buffer(bo_prev);
438 bo_prev = 0;
439 }
440 }
442 uint32_t width() const { return m_width; }
443 uint32_t height() const { return m_height; }
445 private:
446 Card& card;
447 GbmDevice& gdev;
448 const EglState& egl;
450 unique_ptr<GbmSurface> gsurface;
451 EGLSurface esurface;
453 int m_width;
454 int m_height;
456 struct gbm_bo* bo_prev;
457 struct gbm_bo* bo_next;
458 };
460 class EglSurface
461 {
462 public:
463 EglSurface(const EglState& egl, EGLNativeWindowType wnd)
464 : egl(egl)
465 {
466 esurface = eglCreateWindowSurface(egl.display(), egl.config(), wnd, NULL);
467 FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
468 }
470 ~EglSurface()
471 {
472 eglDestroySurface(egl.display(), esurface);
473 }
475 void make_current()
476 {
477 eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
478 }
480 void swap_buffers()
481 {
482 eglSwapBuffers(egl.display(), esurface);
483 }
485 private:
486 const EglState& egl;
488 EGLSurface esurface;
489 };
491 class OutputHandler : private PageFlipHandlerBase
492 {
493 public:
494 OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* plane, float rotation_mult)
495 : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_plane(plane), m_mode(mode),
496 m_rotation_mult(rotation_mult)
497 {
498 m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay));
499 m_scene1 = unique_ptr<GlScene>(new GlScene());
500 m_scene1->set_viewport(m_surface1->width(), m_surface1->height());
502 if (m_plane) {
503 m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400));
504 m_scene2 = unique_ptr<GlScene>(new GlScene());
505 m_scene2->set_viewport(m_surface2->width(), m_surface2->height());
506 }
507 }
509 OutputHandler(const OutputHandler& other) = delete;
510 OutputHandler& operator=(const OutputHandler& other) = delete;
512 void setup()
513 {
514 int ret;
516 m_surface1->make_current();
517 m_surface1->swap_buffers();
518 struct Framebuffer* fb = m_surface1->lock_next();
520 struct Framebuffer* planefb = 0;
522 if (m_plane) {
523 m_surface2->make_current();
524 m_surface2->swap_buffers();
525 planefb = m_surface2->lock_next();
526 }
529 ret = m_crtc->set_mode(m_connector, *fb, m_mode);
530 FAIL_IF(ret, "failed to set mode");
532 if (m_crtc->card().has_atomic()) {
533 Plane* root_plane = 0;
534 for (Plane* p : m_crtc->get_possible_planes()) {
535 if (p->crtc_id() == m_crtc->id()) {
536 root_plane = p;
537 break;
538 }
539 }
541 FAIL_IF(!root_plane, "No primary plane for crtc %d", m_crtc->id());
543 m_root_plane = root_plane;
544 }
546 if (m_plane) {
547 ret = m_crtc->set_plane(m_plane, *planefb,
548 0, 0, planefb->width(), planefb->height(),
549 0, 0, planefb->width(), planefb->height());
550 FAIL_IF(ret, "failed to set plane");
551 }
552 }
554 void start_flipping()
555 {
556 m_t1 = chrono::steady_clock::now();
557 queue_next();
558 }
560 private:
561 void handle_page_flip(uint32_t frame, double time)
562 {
563 ++m_frame_num;
565 if (m_frame_num % 100 == 0) {
566 auto t2 = chrono::steady_clock::now();
567 chrono::duration<float> fsec = t2 - m_t1;
568 printf("fps: %f\n", 100.0 / fsec.count());
569 m_t1 = t2;
570 }
572 s_flip_pending--;
574 m_surface1->free_prev();
575 if (m_plane)
576 m_surface2->free_prev();
578 if (s_need_exit)
579 return;
581 queue_next();
582 }
584 void queue_next()
585 {
586 m_surface1->make_current();
587 m_scene1->draw(m_frame_num * m_rotation_mult);
588 m_surface1->swap_buffers();
589 struct Framebuffer* fb = m_surface1->lock_next();
591 struct Framebuffer* planefb = 0;
593 if (m_plane) {
594 m_surface2->make_current();
595 m_scene2->draw(m_frame_num * m_rotation_mult * 2);
596 m_surface2->swap_buffers();
597 planefb = m_surface2->lock_next();
598 }
600 if (m_crtc->card().has_atomic()) {
601 int r;
603 AtomicReq req(m_crtc->card());
605 req.add(m_root_plane, "FB_ID", fb->id());
606 if (m_plane)
607 req.add(m_plane, "FB_ID", planefb->id());
609 r = req.test();
610 FAIL_IF(r, "atomic test failed");
612 r = req.commit(this);
613 FAIL_IF(r, "atomic commit failed");
614 } else {
615 int ret;
617 ret = m_crtc->page_flip(*fb, this);
618 FAIL_IF(ret, "failed to queue page flip");
620 if (m_plane) {
621 ret = m_crtc->set_plane(m_plane, *planefb,
622 0, 0, planefb->width(), planefb->height(),
623 0, 0, planefb->width(), planefb->height());
624 FAIL_IF(ret, "failed to set plane");
625 }
626 }
628 s_flip_pending++;
629 }
631 int m_frame_num;
632 chrono::steady_clock::time_point m_t1;
634 Connector* m_connector;
635 Crtc* m_crtc;
636 Plane* m_plane;
637 Videomode m_mode;
638 Plane* m_root_plane;
640 unique_ptr<GbmEglSurface> m_surface1;
641 unique_ptr<GbmEglSurface> m_surface2;
643 unique_ptr<GlScene> m_scene1;
644 unique_ptr<GlScene> m_scene2;
646 float m_rotation_mult;
647 };
649 static void main_gbm()
650 {
651 Card card;
653 GbmDevice gdev(card);
654 EglState egl(gdev.handle());
656 vector<unique_ptr<OutputHandler>> outputs;
657 vector<Plane*> used_planes;
659 float rot_mult = 1;
661 for (auto pipe : card.get_connected_pipelines()) {
662 auto connector = pipe.connector;
663 auto crtc = pipe.crtc;
664 auto mode = connector->get_default_mode();
666 Plane* plane = 0;
668 if (s_support_planes) {
669 for (Plane* p : crtc->get_possible_planes()) {
670 if (find(used_planes.begin(), used_planes.end(), p) != used_planes.end())
671 continue;
673 if (p->plane_type() != PlaneType::Overlay)
674 continue;
676 plane = p;
677 break;
678 }
679 }
681 if (plane)
682 used_planes.push_back(plane);
684 auto out = new OutputHandler(card, gdev, egl, connector, crtc, mode, plane, rot_mult);
685 outputs.emplace_back(out);
687 rot_mult *= 1.33;
688 }
690 for (auto& out : outputs)
691 out->setup();
693 for (auto& out : outputs)
694 out->start_flipping();
696 struct pollfd fds[2] = { 0 };
697 fds[0].fd = 0;
698 fds[0].events = POLLIN;
699 fds[1].fd = card.fd();
700 fds[1].events = POLLIN;
702 while (!s_need_exit || s_flip_pending) {
703 int r = poll(fds, ARRAY_SIZE(fds), -1);
704 FAIL_IF(r < 0, "poll error %d", r);
706 if (fds[0].revents)
707 s_need_exit = true;
709 if (fds[1].revents)
710 card.call_page_flip_handlers();
711 }
712 }
714 static void main_x()
715 {
716 Display* display = XOpenDisplay(NULL);
718 xcb_connection_t *connection = XGetXCBConnection(display);
720 /* Get the first screen */
721 const xcb_setup_t *setup = xcb_get_setup (connection);
722 xcb_screen_t *screen = xcb_setup_roots_iterator (setup).data;
724 /* Create the window */
726 uint32_t width = 600;
727 uint32_t height = 600;
729 const uint32_t xcb_window_attrib_mask = XCB_CW_EVENT_MASK;
730 const uint32_t xcb_window_attrib_list[] = {
731 XCB_EVENT_MASK_EXPOSURE,
732 };
734 xcb_window_t window = xcb_generate_id (connection);
735 xcb_create_window (connection, /* Connection */
736 XCB_COPY_FROM_PARENT, /* depth (same as root)*/
737 window, /* window Id */
738 screen->root, /* parent window */
739 0, 0, /* x, y */
740 width, height, /* width, height */
741 0, /* border_width */
742 XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
743 screen->root_visual, /* visual */
744 xcb_window_attrib_mask,
745 xcb_window_attrib_list);
747 xcb_map_window (connection, window);
748 xcb_flush (connection);
750 EglState egl(reinterpret_cast<EGLNativeDisplayType>(display));
751 EglSurface surface(egl, reinterpret_cast<EGLNativeWindowType>(window));
752 GlScene scene;
754 scene.set_viewport(width, height);
756 int framenum = 0;
758 surface.make_current();
759 surface.swap_buffers();
761 xcb_generic_event_t *event;
762 while ( (event = xcb_poll_for_event (connection)) ) {
764 surface.make_current();
765 scene.draw(framenum++);
766 surface.swap_buffers();
767 }
769 xcb_disconnect (connection);
770 }
772 static void main_null()
773 {
774 EglState egl(EGL_DEFAULT_DISPLAY);
775 EglSurface surface(egl, 0);
776 GlScene scene;
778 scene.set_viewport(600, 600);
780 int framenum = 0;
782 struct pollfd fds[1] = { 0 };
783 fds[0].fd = 0;
784 fds[0].events = POLLIN;
786 while (true) {
787 int r = poll(fds, ARRAY_SIZE(fds), 0);
788 ASSERT(r >= 0);
790 if (fds[0].revents)
791 break;
793 surface.make_current();
794 scene.draw(framenum++);
795 surface.swap_buffers();
796 }
797 }
799 int main(int argc, char *argv[])
800 {
801 OptionSet optionset = {
802 Option("v|verbose",
803 [&]()
804 {
805 s_verbose = true;
806 }),
807 };
809 optionset.parse(argc, argv);
811 string m;
813 if (optionset.params().size() == 0)
814 m = "gbm";
815 else
816 m = optionset.params().front();
818 if (m == "gbm")
819 main_gbm();
820 else if (m == "null")
821 main_null();
822 else if (m == "x")
823 main_x();
824 else {
825 printf("Unknown mode %s\n", m.c_str());
826 return -1;
827 }
829 return 0;
830 }