1 /*
2 * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
3 * Copyright (c) 2012 Rob Clark <rob@ti.com>
4 * Copyright (c) 2013 Anand Balagopalakrishnan <anandb@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 <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
37 #include <xf86drm.h>
38 #include <xf86drmMode.h>
39 #include <gbm.h>
41 #include "esUtil.h"
43 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
45 #define MAX_DISPLAYS (4)
46 uint8_t DISP_ID = 0;
47 uint8_t all_display = 0;
48 int8_t connector_id = -1;
50 static struct {
51 EGLDisplay display;
52 EGLConfig config;
53 EGLContext context;
54 EGLSurface surface;
55 GLuint program;
56 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
57 GLuint vbo;
58 GLuint positionsoffset, colorsoffset, normalsoffset;
59 GLuint vertex_shader, fragment_shader;
60 } gl;
62 static struct {
63 struct gbm_device *dev;
64 struct gbm_surface *surface;
65 } gbm;
67 static struct {
68 int fd;
69 uint32_t ndisp;
70 uint32_t crtc_id[MAX_DISPLAYS];
71 uint32_t connector_id[MAX_DISPLAYS];
72 uint32_t resource_id;
73 uint32_t encoder[MAX_DISPLAYS];
74 drmModeModeInfo *mode[MAX_DISPLAYS];
75 drmModeConnector *connectors[MAX_DISPLAYS];
76 } drm;
78 struct drm_fb {
79 struct gbm_bo *bo;
80 uint32_t fb_id;
81 };
83 static int init_drm(void)
84 {
85 static const char *modules[] = {
86 "omapdrm", "i915", "radeon", "nouveau", "vmwgfx", "exynos"
87 };
88 drmModeRes *resources;
89 drmModeConnector *connector = NULL;
90 drmModeEncoder *encoder = NULL;
91 int i, j;
92 uint32_t maxRes, curRes;
94 for (i = 0; i < ARRAY_SIZE(modules); i++) {
95 printf("trying to load module %s...", modules[i]);
96 drm.fd = drmOpen(modules[i], NULL);
97 if (drm.fd < 0) {
98 printf("failed.\n");
99 } else {
100 printf("success.\n");
101 break;
102 }
103 }
105 if (drm.fd < 0) {
106 printf("could not open drm device\n");
107 return -1;
108 }
110 resources = drmModeGetResources(drm.fd);
111 if (!resources) {
112 printf("drmModeGetResources failed: %s\n", strerror(errno));
113 return -1;
114 }
115 drm.resource_id = (uint32_t) resources;
117 /* find a connected connector: */
118 for (i = 0; i < resources->count_connectors; i++) {
119 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
120 if (connector->connection == DRM_MODE_CONNECTED) {
121 /* choose the first supported mode */
122 drm.mode[drm.ndisp] = &connector->modes[0];
123 drm.connector_id[drm.ndisp] = connector->connector_id;
125 for (j=0; j<resources->count_encoders; j++) {
126 encoder = drmModeGetEncoder(drm.fd, resources->encoders[j]);
127 if (encoder->encoder_id == connector->encoder_id)
128 break;
130 drmModeFreeEncoder(encoder);
131 encoder = NULL;
132 }
134 if (!encoder) {
135 printf("no encoder!\n");
136 return -1;
137 }
139 drm.encoder[drm.ndisp] = (uint32_t) encoder;
140 drm.crtc_id[drm.ndisp] = encoder->crtc_id;
141 drm.connectors[drm.ndisp] = connector;
143 printf("### Display [%d]: CRTC = %d, Connector = %d\n", drm.ndisp, drm.crtc_id[drm.ndisp], drm.connector_id[drm.ndisp]);
144 printf("\tMode chosen [%s] : Clock => %d, Vertical refresh => %d, Type => %d\n", drm.mode[drm.ndisp]->name, drm.mode[drm.ndisp]->clock, drm.mode[drm.ndisp]->vrefresh, drm.mode[drm.ndisp]->type);
145 printf("\tHorizontal => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->hdisplay, drm.mode[drm.ndisp]->hsync_start, drm.mode[drm.ndisp]->hsync_end, drm.mode[drm.ndisp]->htotal, drm.mode[drm.ndisp]->hskew);
146 printf("\tVertical => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->vdisplay, drm.mode[drm.ndisp]->vsync_start, drm.mode[drm.ndisp]->vsync_end, drm.mode[drm.ndisp]->vtotal, drm.mode[drm.ndisp]->vscan);
148 /* If a connector_id is specified, use the corresponding display */
149 if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
150 DISP_ID = drm.ndisp;
152 /* If all displays are enabled, choose the connector with maximum
153 * resolution as the primary display */
154 if (all_display) {
155 maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
156 curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;
158 if (curRes > maxRes)
159 DISP_ID = drm.ndisp;
160 }
162 drm.ndisp++;
163 } else {
164 drmModeFreeConnector(connector);
165 }
166 }
168 if (drm.ndisp == 0) {
169 /* we could be fancy and listen for hotplug events and wait for
170 * a connector..
171 */
172 printf("no connected connector!\n");
173 return -1;
174 }
176 return 0;
177 }
179 static int init_gbm(void)
180 {
181 gbm.dev = gbm_create_device(drm.fd);
183 gbm.surface = gbm_surface_create(gbm.dev,
184 drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay,
185 GBM_FORMAT_XRGB8888,
186 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
187 if (!gbm.surface) {
188 printf("failed to create gbm surface\n");
189 return -1;
190 }
192 return 0;
193 }
195 static int init_gl(void)
196 {
197 EGLint major, minor, n;
198 GLint ret;
200 static const GLfloat vVertices[] = {
201 // front
202 -1.0f, -1.0f, +1.0f, // point blue
203 +1.0f, -1.0f, +1.0f, // point magenta
204 -1.0f, +1.0f, +1.0f, // point cyan
205 +1.0f, +1.0f, +1.0f, // point white
206 // back
207 +1.0f, -1.0f, -1.0f, // point red
208 -1.0f, -1.0f, -1.0f, // point black
209 +1.0f, +1.0f, -1.0f, // point yellow
210 -1.0f, +1.0f, -1.0f, // point green
211 // right
212 +1.0f, -1.0f, +1.0f, // point magenta
213 +1.0f, -1.0f, -1.0f, // point red
214 +1.0f, +1.0f, +1.0f, // point white
215 +1.0f, +1.0f, -1.0f, // point yellow
216 // left
217 -1.0f, -1.0f, -1.0f, // point black
218 -1.0f, -1.0f, +1.0f, // point blue
219 -1.0f, +1.0f, -1.0f, // point green
220 -1.0f, +1.0f, +1.0f, // point cyan
221 // top
222 -1.0f, +1.0f, +1.0f, // point cyan
223 +1.0f, +1.0f, +1.0f, // point white
224 -1.0f, +1.0f, -1.0f, // point green
225 +1.0f, +1.0f, -1.0f, // point yellow
226 // bottom
227 -1.0f, -1.0f, -1.0f, // point black
228 +1.0f, -1.0f, -1.0f, // point red
229 -1.0f, -1.0f, +1.0f, // point blue
230 +1.0f, -1.0f, +1.0f // point magenta
231 };
233 static const GLfloat vColors[] = {
234 // front
235 0.0f, 0.0f, 1.0f, // blue
236 1.0f, 0.0f, 1.0f, // magenta
237 0.0f, 1.0f, 1.0f, // cyan
238 1.0f, 1.0f, 1.0f, // white
239 // back
240 1.0f, 0.0f, 0.0f, // red
241 0.0f, 0.0f, 0.0f, // black
242 1.0f, 1.0f, 0.0f, // yellow
243 0.0f, 1.0f, 0.0f, // green
244 // right
245 1.0f, 0.0f, 1.0f, // magenta
246 1.0f, 0.0f, 0.0f, // red
247 1.0f, 1.0f, 1.0f, // white
248 1.0f, 1.0f, 0.0f, // yellow
249 // left
250 0.0f, 0.0f, 0.0f, // black
251 0.0f, 0.0f, 1.0f, // blue
252 0.0f, 1.0f, 0.0f, // green
253 0.0f, 1.0f, 1.0f, // cyan
254 // top
255 0.0f, 1.0f, 1.0f, // cyan
256 1.0f, 1.0f, 1.0f, // white
257 0.0f, 1.0f, 0.0f, // green
258 1.0f, 1.0f, 0.0f, // yellow
259 // bottom
260 0.0f, 0.0f, 0.0f, // black
261 1.0f, 0.0f, 0.0f, // red
262 0.0f, 0.0f, 1.0f, // blue
263 1.0f, 0.0f, 1.0f // magenta
264 };
266 static const GLfloat vNormals[] = {
267 // front
268 +0.0f, +0.0f, +1.0f, // forward
269 +0.0f, +0.0f, +1.0f, // forward
270 +0.0f, +0.0f, +1.0f, // forward
271 +0.0f, +0.0f, +1.0f, // forward
272 // back
273 +0.0f, +0.0f, -1.0f, // backbard
274 +0.0f, +0.0f, -1.0f, // backbard
275 +0.0f, +0.0f, -1.0f, // backbard
276 +0.0f, +0.0f, -1.0f, // backbard
277 // right
278 +1.0f, +0.0f, +0.0f, // right
279 +1.0f, +0.0f, +0.0f, // right
280 +1.0f, +0.0f, +0.0f, // right
281 +1.0f, +0.0f, +0.0f, // right
282 // left
283 -1.0f, +0.0f, +0.0f, // left
284 -1.0f, +0.0f, +0.0f, // left
285 -1.0f, +0.0f, +0.0f, // left
286 -1.0f, +0.0f, +0.0f, // left
287 // top
288 +0.0f, +1.0f, +0.0f, // up
289 +0.0f, +1.0f, +0.0f, // up
290 +0.0f, +1.0f, +0.0f, // up
291 +0.0f, +1.0f, +0.0f, // up
292 // bottom
293 +0.0f, -1.0f, +0.0f, // down
294 +0.0f, -1.0f, +0.0f, // down
295 +0.0f, -1.0f, +0.0f, // down
296 +0.0f, -1.0f, +0.0f // down
297 };
299 static const EGLint context_attribs[] = {
300 EGL_CONTEXT_CLIENT_VERSION, 2,
301 EGL_NONE
302 };
304 static const EGLint config_attribs[] = {
305 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
306 EGL_RED_SIZE, 1,
307 EGL_GREEN_SIZE, 1,
308 EGL_BLUE_SIZE, 1,
309 EGL_ALPHA_SIZE, 0,
310 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
311 EGL_NONE
312 };
314 static const char *vertex_shader_source =
315 "uniform mat4 modelviewMatrix; \n"
316 "uniform mat4 modelviewprojectionMatrix;\n"
317 "uniform mat3 normalMatrix; \n"
318 " \n"
319 "attribute vec4 in_position; \n"
320 "attribute vec3 in_normal; \n"
321 "attribute vec4 in_color; \n"
322 "\n"
323 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
324 " \n"
325 "varying vec4 vVaryingColor; \n"
326 " \n"
327 "void main() \n"
328 "{ \n"
329 " gl_Position = modelviewprojectionMatrix * in_position;\n"
330 " vec3 vEyeNormal = normalMatrix * in_normal;\n"
331 " vec4 vPosition4 = modelviewMatrix * in_position;\n"
332 " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
333 " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
334 " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
335 " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
336 "} \n";
338 static const char *fragment_shader_source =
339 "precision mediump float; \n"
340 " \n"
341 "varying vec4 vVaryingColor; \n"
342 " \n"
343 "void main() \n"
344 "{ \n"
345 " gl_FragColor = vVaryingColor; \n"
346 "} \n";
348 gl.display = eglGetDisplay(gbm.dev);
350 if (!eglInitialize(gl.display, &major, &minor)) {
351 printf("failed to initialize\n");
352 return -1;
353 }
355 printf("Using display %p with EGL version %d.%d\n",
356 gl.display, major, minor);
358 printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
359 printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
360 printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
362 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
363 printf("failed to bind api EGL_OPENGL_ES_API\n");
364 return -1;
365 }
367 if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
368 printf("failed to choose config: %d\n", n);
369 return -1;
370 }
372 gl.context = eglCreateContext(gl.display, gl.config,
373 EGL_NO_CONTEXT, context_attribs);
374 if (gl.context == NULL) {
375 printf("failed to create context\n");
376 return -1;
377 }
379 gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
380 if (gl.surface == EGL_NO_SURFACE) {
381 printf("failed to create egl surface\n");
382 return -1;
383 }
385 /* connect the context to the surface */
386 eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
389 gl.vertex_shader = glCreateShader(GL_VERTEX_SHADER);
391 glShaderSource(gl.vertex_shader, 1, &vertex_shader_source, NULL);
392 glCompileShader(gl.vertex_shader);
394 glGetShaderiv(gl.vertex_shader, GL_COMPILE_STATUS, &ret);
395 if (!ret) {
396 char *log;
398 printf("vertex shader compilation failed!:\n");
399 glGetShaderiv(gl.vertex_shader, GL_INFO_LOG_LENGTH, &ret);
400 if (ret > 1) {
401 log = malloc(ret);
402 glGetShaderInfoLog(gl.vertex_shader, ret, NULL, log);
403 printf("%s", log);
404 }
406 return -1;
407 }
409 gl.fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
411 glShaderSource(gl.fragment_shader, 1, &fragment_shader_source, NULL);
412 glCompileShader(gl.fragment_shader);
414 glGetShaderiv(gl.fragment_shader, GL_COMPILE_STATUS, &ret);
415 if (!ret) {
416 char *log;
418 printf("fragment shader compilation failed!:\n");
419 glGetShaderiv(gl.fragment_shader, GL_INFO_LOG_LENGTH, &ret);
421 if (ret > 1) {
422 log = malloc(ret);
423 glGetShaderInfoLog(gl.fragment_shader, ret, NULL, log);
424 printf("%s", log);
425 }
427 return -1;
428 }
430 gl.program = glCreateProgram();
432 glAttachShader(gl.program, gl.vertex_shader);
433 glAttachShader(gl.program, gl.fragment_shader);
435 glBindAttribLocation(gl.program, 0, "in_position");
436 glBindAttribLocation(gl.program, 1, "in_normal");
437 glBindAttribLocation(gl.program, 2, "in_color");
439 glLinkProgram(gl.program);
441 glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
442 if (!ret) {
443 char *log;
445 printf("program linking failed!:\n");
446 glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
448 if (ret > 1) {
449 log = malloc(ret);
450 glGetProgramInfoLog(gl.program, ret, NULL, log);
451 printf("%s", log);
452 }
454 return -1;
455 }
457 glUseProgram(gl.program);
459 gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
460 gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
461 gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
463 glViewport(0, 0, drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay);
464 glEnable(GL_CULL_FACE);
466 gl.positionsoffset = 0;
467 gl.colorsoffset = sizeof(vVertices);
468 gl.normalsoffset = sizeof(vVertices) + sizeof(vColors);
469 glGenBuffers(1, &gl.vbo);
470 glBindBuffer(GL_ARRAY_BUFFER, gl.vbo);
471 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
472 glBufferSubData(GL_ARRAY_BUFFER, gl.positionsoffset, sizeof(vVertices), &vVertices[0]);
473 glBufferSubData(GL_ARRAY_BUFFER, gl.colorsoffset, sizeof(vColors), &vColors[0]);
474 glBufferSubData(GL_ARRAY_BUFFER, gl.normalsoffset, sizeof(vNormals), &vNormals[0]);
475 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.positionsoffset);
476 glEnableVertexAttribArray(0);
477 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.normalsoffset);
478 glEnableVertexAttribArray(1);
479 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.colorsoffset);
480 glEnableVertexAttribArray(2);
482 return 0;
483 }
485 static void exit_gbm(void)
486 {
487 gbm_surface_destroy(gbm.surface);
488 gbm_device_destroy(gbm.dev);
489 return;
490 }
492 static void exit_gl(void)
493 {
494 glDeleteProgram(gl.program);
495 glDeleteBuffers(1, &gl.vbo);
496 glDeleteShader(gl.fragment_shader);
497 glDeleteShader(gl.vertex_shader);
498 eglDestroySurface(gl.display, gl.surface);
499 eglDestroyContext(gl.display, gl.context);
500 eglTerminate(gl.display);
501 return;
502 }
504 static void exit_drm(void)
505 {
507 drmModeRes *resources;
508 int i;
510 resources = (drmModeRes *)drm.resource_id;
511 for (i = 0; i < resources->count_connectors; i++) {
512 drmModeFreeEncoder(drm.encoder[i]);
513 drmModeFreeConnector(drm.connectors[i]);
514 }
515 drmModeFreeResources(drm.resource_id);
516 drmClose(drm.fd);
517 return;
518 }
520 void cleanup_kmscube(void)
521 {
522 exit_gl();
523 exit_gbm();
524 exit_drm();
525 printf("Cleanup of GL, GBM and DRM completed\n");
526 return;
527 }
529 static void draw(uint32_t i)
530 {
531 ESMatrix modelview;
533 /* clear the color buffer */
534 glClearColor(0.5, 0.5, 0.5, 1.0);
535 glClear(GL_COLOR_BUFFER_BIT);
537 esMatrixLoadIdentity(&modelview);
538 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
539 esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
540 esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
541 esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
543 GLfloat aspect = (GLfloat)(drm.mode[DISP_ID]->vdisplay) / (GLfloat)(drm.mode[DISP_ID]->hdisplay);
545 ESMatrix projection;
546 esMatrixLoadIdentity(&projection);
547 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
549 ESMatrix modelviewprojection;
550 esMatrixLoadIdentity(&modelviewprojection);
551 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
553 float normal[9];
554 normal[0] = modelview.m[0][0];
555 normal[1] = modelview.m[0][1];
556 normal[2] = modelview.m[0][2];
557 normal[3] = modelview.m[1][0];
558 normal[4] = modelview.m[1][1];
559 normal[5] = modelview.m[1][2];
560 normal[6] = modelview.m[2][0];
561 normal[7] = modelview.m[2][1];
562 normal[8] = modelview.m[2][2];
564 glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
565 glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
566 glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
568 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
569 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
570 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
571 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
572 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
573 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
574 }
576 static void
577 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
578 {
579 struct drm_fb *fb = data;
580 struct gbm_device *gbm = gbm_bo_get_device(bo);
582 if (fb->fb_id)
583 drmModeRmFB(drm.fd, fb->fb_id);
585 free(fb);
586 }
588 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
589 {
590 struct drm_fb *fb = gbm_bo_get_user_data(bo);
591 uint32_t width, height, stride, handle;
592 int ret;
594 if (fb)
595 return fb;
597 fb = calloc(1, sizeof *fb);
598 fb->bo = bo;
600 width = gbm_bo_get_width(bo);
601 height = gbm_bo_get_height(bo);
602 stride = gbm_bo_get_stride(bo);
603 handle = gbm_bo_get_handle(bo).u32;
605 ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
606 if (ret) {
607 printf("failed to create fb: %s\n", strerror(errno));
608 free(fb);
609 return NULL;
610 }
612 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
614 return fb;
615 }
617 static void page_flip_handler(int fd, unsigned int frame,
618 unsigned int sec, unsigned int usec, void *data)
619 {
620 int *waiting_for_flip = data;
621 *waiting_for_flip = 0;
622 }
624 void print_usage()
625 {
626 printf("Usage : kmscube <options>\n");
627 printf("\t-h : Help\n");
628 printf("\t-a : Enable all displays\n");
629 printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
630 }
632 int main(int argc, char *argv[])
633 {
634 fd_set fds;
635 drmEventContext evctx = {
636 .version = DRM_EVENT_CONTEXT_VERSION,
637 .page_flip_handler = page_flip_handler,
638 };
639 struct gbm_bo *bo;
640 struct drm_fb *fb;
641 uint32_t i = 0;
642 int ret;
643 int opt;
645 while ((opt = getopt(argc, argv, "ahc:")) != -1) {
646 switch(opt) {
647 case 'a':
648 all_display = 1;
649 break;
651 case 'h':
652 print_usage();
653 return 0;
655 case 'c':
656 connector_id = atoi(optarg);
657 break;
659 default:
660 printf("Undefined option %s\n", argv[optind]);
661 print_usage();
662 return -1;
663 }
664 }
666 if (all_display) {
667 printf("### Enabling all displays\n");
668 connector_id = -1;
669 }
671 ret = init_drm();
672 if (ret) {
673 printf("failed to initialize DRM\n");
674 return ret;
675 }
676 printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",
677 drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,
678 drm.mode[DISP_ID]->vdisplay);
680 FD_ZERO(&fds);
681 FD_SET(drm.fd, &fds);
683 ret = init_gbm();
684 if (ret) {
685 printf("failed to initialize GBM\n");
686 return ret;
687 }
689 ret = init_gl();
690 if (ret) {
691 printf("failed to initialize EGL\n");
692 return ret;
693 }
695 /* clear the color buffer */
696 glClearColor(0.5, 0.5, 0.5, 1.0);
697 glClear(GL_COLOR_BUFFER_BIT);
698 eglSwapBuffers(gl.display, gl.surface);
699 bo = gbm_surface_lock_front_buffer(gbm.surface);
700 fb = drm_fb_get_from_bo(bo);
702 /* set mode: */
703 if (all_display) {
704 for (i=0; i<drm.ndisp; i++) {
705 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0,
706 &drm.connector_id[i], 1, drm.mode[i]);
707 if (ret) {
708 printf("display %d failed to set mode: %s\n", i, strerror(errno));
709 return ret;
710 }
711 }
712 } else {
713 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
714 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
715 if (ret) {
716 printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
717 return ret;
718 }
719 }
721 while (1) {
722 struct gbm_bo *next_bo;
723 int waiting_for_flip = 1;
725 draw(i++);
727 eglSwapBuffers(gl.display, gl.surface);
728 next_bo = gbm_surface_lock_front_buffer(gbm.surface);
729 fb = drm_fb_get_from_bo(next_bo);
731 /*
732 * Here you could also update drm plane layers if you want
733 * hw composition
734 */
736 ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
737 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
738 if (ret) {
739 printf("failed to queue page flip: %s\n", strerror(errno));
740 return -1;
741 }
743 while (waiting_for_flip) {
744 ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
745 if (ret < 0) {
746 printf("select err: %s\n", strerror(errno));
747 return ret;
748 } else if (ret == 0) {
749 printf("select timeout!\n");
750 return -1;
751 } else if (FD_ISSET(0, &fds)) {
752 continue;
753 }
754 drmHandleEvent(drm.fd, &evctx);
755 }
757 /* release last buffer to render on again: */
758 gbm_surface_release_buffer(gbm.surface, bo);
759 bo = next_bo;
760 }
762 cleanup_kmscube();
763 printf("\n Exiting kmscube \n");
765 return ret;
766 }