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>
36 #include <signal.h>
38 #include <xf86drm.h>
39 #include <xf86drmMode.h>
40 #include <gbm.h>
42 #include "esUtil.h"
44 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
46 #define MAX_DISPLAYS (4)
47 uint8_t DISP_ID = 0;
48 uint8_t all_display = 0;
49 int8_t connector_id = -1;
51 static struct {
52 EGLDisplay display;
53 EGLConfig config;
54 EGLContext context;
55 EGLSurface surface;
56 GLuint program;
57 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
58 GLuint vbo;
59 GLuint positionsoffset, colorsoffset, normalsoffset;
60 GLuint vertex_shader, fragment_shader;
61 } gl;
63 static struct {
64 struct gbm_device *dev;
65 struct gbm_surface *surface;
66 } gbm;
68 static struct {
69 int fd;
70 uint32_t ndisp;
71 uint32_t crtc_id[MAX_DISPLAYS];
72 uint32_t connector_id[MAX_DISPLAYS];
73 uint32_t resource_id;
74 uint32_t encoder[MAX_DISPLAYS];
75 drmModeModeInfo *mode[MAX_DISPLAYS];
76 drmModeConnector *connectors[MAX_DISPLAYS];
77 } drm;
79 struct drm_fb {
80 struct gbm_bo *bo;
81 uint32_t fb_id;
82 };
84 static int init_drm(void)
85 {
86 static const char *modules[] = {
87 "omapdrm", "tilcdc", "i915", "radeon", "nouveau", "vmwgfx", "exynos"
88 };
89 drmModeRes *resources;
90 drmModeConnector *connector = NULL;
91 drmModeEncoder *encoder = NULL;
92 drmModeCrtc *crtc = NULL;
94 int i, j, k;
95 uint32_t maxRes, curRes;
97 for (i = 0; i < ARRAY_SIZE(modules); i++) {
98 printf("trying to load module %s...", modules[i]);
99 drm.fd = drmOpen(modules[i], NULL);
100 if (drm.fd < 0) {
101 printf("failed.\n");
102 } else {
103 printf("success.\n");
104 break;
105 }
106 }
108 if (drm.fd < 0) {
109 printf("could not open drm device\n");
110 return -1;
111 }
113 resources = drmModeGetResources(drm.fd);
114 if (!resources) {
115 printf("drmModeGetResources failed: %s\n", strerror(errno));
116 return -1;
117 }
118 drm.resource_id = (uint32_t) resources;
120 /* find a connected connector: */
121 for (i = 0; i < resources->count_connectors; i++) {
122 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
123 if (connector->connection == DRM_MODE_CONNECTED) {
125 /* find the matched encoders */
126 for (j=0; j<connector->count_encoders; j++) {
127 encoder = drmModeGetEncoder(drm.fd, connector->encoders[j]);
129 /* Take the fisrt one, if none is assigned */
130 if (!connector->encoder_id)
131 {
132 connector->encoder_id = encoder->encoder_id;
133 }
135 if (encoder->encoder_id == connector->encoder_id)
136 {
137 /* find the first valid CRTC if not assigned */
138 if (!encoder->crtc_id)
139 {
140 for (k = 0; k < resources->count_crtcs; ++k) {
141 /* check whether this CRTC works with the encoder */
142 if (!(encoder->possible_crtcs & (1 << k)))
143 continue;
145 encoder->crtc_id = resources->crtcs[k];
146 break;
147 }
149 if (!encoder->crtc_id)
150 {
151 printf("Encoder(%d): no CRTC find!\n", encoder->encoder_id);
152 drmModeFreeEncoder(encoder);
153 encoder = NULL;
154 continue;
155 }
156 }
158 break;
159 }
161 drmModeFreeEncoder(encoder);
162 encoder = NULL;
163 }
165 if (!encoder) {
166 printf("Connector (%d): no encoder!\n", connector->connector_id);
167 drmModeFreeConnector(connector);
168 continue;
169 }
171 /* choose the current or first supported mode */
172 crtc = drmModeGetCrtc(drm.fd, encoder->crtc_id);
173 for (j = 0; j < connector->count_modes; j++)
174 {
175 if (crtc->mode_valid)
176 {
177 if ((connector->modes[j].hdisplay == crtc->width) &&
178 (connector->modes[j].vdisplay == crtc->height))
179 {
180 drm.mode[drm.ndisp] = &connector->modes[j];
181 break;
182 }
183 }
184 else
185 {
186 if ((connector->modes[j].hdisplay == crtc->x) &&
187 (connector->modes[j].vdisplay == crtc->y))
188 {
189 drm.mode[drm.ndisp] = &connector->modes[j];
190 break;
191 }
192 }
193 }
195 if(j >= connector->count_modes)
196 drm.mode[drm.ndisp] = &connector->modes[0];
198 drm.connector_id[drm.ndisp] = connector->connector_id;
200 drm.encoder[drm.ndisp] = (uint32_t) encoder;
201 drm.crtc_id[drm.ndisp] = encoder->crtc_id;
202 drm.connectors[drm.ndisp] = connector;
204 printf("### Display [%d]: CRTC = %d, Connector = %d\n", drm.ndisp, drm.crtc_id[drm.ndisp], drm.connector_id[drm.ndisp]);
205 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);
206 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);
207 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);
209 /* If a connector_id is specified, use the corresponding display */
210 if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
211 DISP_ID = drm.ndisp;
213 /* If all displays are enabled, choose the connector with maximum
214 * resolution as the primary display */
215 if (all_display) {
216 maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
217 curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;
219 if (curRes > maxRes)
220 DISP_ID = drm.ndisp;
221 }
223 drm.ndisp++;
224 } else {
225 drmModeFreeConnector(connector);
226 }
227 }
229 if (drm.ndisp == 0) {
230 /* we could be fancy and listen for hotplug events and wait for
231 * a connector..
232 */
233 printf("no connected connector!\n");
234 return -1;
235 }
237 return 0;
238 }
240 static int init_gbm(void)
241 {
242 gbm.dev = gbm_create_device(drm.fd);
244 gbm.surface = gbm_surface_create(gbm.dev,
245 drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay,
246 GBM_FORMAT_XRGB8888,
247 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
248 if (!gbm.surface) {
249 printf("failed to create gbm surface\n");
250 return -1;
251 }
253 return 0;
254 }
256 static int init_gl(void)
257 {
258 EGLint major, minor, n;
259 GLint ret;
261 static const GLfloat vVertices[] = {
262 // front
263 -1.0f, -1.0f, +1.0f, // point blue
264 +1.0f, -1.0f, +1.0f, // point magenta
265 -1.0f, +1.0f, +1.0f, // point cyan
266 +1.0f, +1.0f, +1.0f, // point white
267 // back
268 +1.0f, -1.0f, -1.0f, // point red
269 -1.0f, -1.0f, -1.0f, // point black
270 +1.0f, +1.0f, -1.0f, // point yellow
271 -1.0f, +1.0f, -1.0f, // point green
272 // right
273 +1.0f, -1.0f, +1.0f, // point magenta
274 +1.0f, -1.0f, -1.0f, // point red
275 +1.0f, +1.0f, +1.0f, // point white
276 +1.0f, +1.0f, -1.0f, // point yellow
277 // left
278 -1.0f, -1.0f, -1.0f, // point black
279 -1.0f, -1.0f, +1.0f, // point blue
280 -1.0f, +1.0f, -1.0f, // point green
281 -1.0f, +1.0f, +1.0f, // point cyan
282 // top
283 -1.0f, +1.0f, +1.0f, // point cyan
284 +1.0f, +1.0f, +1.0f, // point white
285 -1.0f, +1.0f, -1.0f, // point green
286 +1.0f, +1.0f, -1.0f, // point yellow
287 // bottom
288 -1.0f, -1.0f, -1.0f, // point black
289 +1.0f, -1.0f, -1.0f, // point red
290 -1.0f, -1.0f, +1.0f, // point blue
291 +1.0f, -1.0f, +1.0f // point magenta
292 };
294 static const GLfloat vColors[] = {
295 // front
296 0.0f, 0.0f, 1.0f, // blue
297 1.0f, 0.0f, 1.0f, // magenta
298 0.0f, 1.0f, 1.0f, // cyan
299 1.0f, 1.0f, 1.0f, // white
300 // back
301 1.0f, 0.0f, 0.0f, // red
302 0.0f, 0.0f, 0.0f, // black
303 1.0f, 1.0f, 0.0f, // yellow
304 0.0f, 1.0f, 0.0f, // green
305 // right
306 1.0f, 0.0f, 1.0f, // magenta
307 1.0f, 0.0f, 0.0f, // red
308 1.0f, 1.0f, 1.0f, // white
309 1.0f, 1.0f, 0.0f, // yellow
310 // left
311 0.0f, 0.0f, 0.0f, // black
312 0.0f, 0.0f, 1.0f, // blue
313 0.0f, 1.0f, 0.0f, // green
314 0.0f, 1.0f, 1.0f, // cyan
315 // top
316 0.0f, 1.0f, 1.0f, // cyan
317 1.0f, 1.0f, 1.0f, // white
318 0.0f, 1.0f, 0.0f, // green
319 1.0f, 1.0f, 0.0f, // yellow
320 // bottom
321 0.0f, 0.0f, 0.0f, // black
322 1.0f, 0.0f, 0.0f, // red
323 0.0f, 0.0f, 1.0f, // blue
324 1.0f, 0.0f, 1.0f // magenta
325 };
327 static const GLfloat vNormals[] = {
328 // front
329 +0.0f, +0.0f, +1.0f, // forward
330 +0.0f, +0.0f, +1.0f, // forward
331 +0.0f, +0.0f, +1.0f, // forward
332 +0.0f, +0.0f, +1.0f, // forward
333 // back
334 +0.0f, +0.0f, -1.0f, // backbard
335 +0.0f, +0.0f, -1.0f, // backbard
336 +0.0f, +0.0f, -1.0f, // backbard
337 +0.0f, +0.0f, -1.0f, // backbard
338 // right
339 +1.0f, +0.0f, +0.0f, // right
340 +1.0f, +0.0f, +0.0f, // right
341 +1.0f, +0.0f, +0.0f, // right
342 +1.0f, +0.0f, +0.0f, // right
343 // left
344 -1.0f, +0.0f, +0.0f, // left
345 -1.0f, +0.0f, +0.0f, // left
346 -1.0f, +0.0f, +0.0f, // left
347 -1.0f, +0.0f, +0.0f, // left
348 // top
349 +0.0f, +1.0f, +0.0f, // up
350 +0.0f, +1.0f, +0.0f, // up
351 +0.0f, +1.0f, +0.0f, // up
352 +0.0f, +1.0f, +0.0f, // up
353 // bottom
354 +0.0f, -1.0f, +0.0f, // down
355 +0.0f, -1.0f, +0.0f, // down
356 +0.0f, -1.0f, +0.0f, // down
357 +0.0f, -1.0f, +0.0f // down
358 };
360 static const EGLint context_attribs[] = {
361 EGL_CONTEXT_CLIENT_VERSION, 2,
362 EGL_NONE
363 };
365 static const EGLint config_attribs[] = {
366 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
367 EGL_RED_SIZE, 1,
368 EGL_GREEN_SIZE, 1,
369 EGL_BLUE_SIZE, 1,
370 EGL_ALPHA_SIZE, 0,
371 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
372 EGL_NONE
373 };
375 static const char *vertex_shader_source =
376 "uniform mat4 modelviewMatrix; \n"
377 "uniform mat4 modelviewprojectionMatrix;\n"
378 "uniform mat3 normalMatrix; \n"
379 " \n"
380 "attribute vec4 in_position; \n"
381 "attribute vec3 in_normal; \n"
382 "attribute vec4 in_color; \n"
383 "\n"
384 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
385 " \n"
386 "varying vec4 vVaryingColor; \n"
387 " \n"
388 "void main() \n"
389 "{ \n"
390 " gl_Position = modelviewprojectionMatrix * in_position;\n"
391 " vec3 vEyeNormal = normalMatrix * in_normal;\n"
392 " vec4 vPosition4 = modelviewMatrix * in_position;\n"
393 " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
394 " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
395 " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
396 " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
397 "} \n";
399 static const char *fragment_shader_source =
400 "precision mediump float; \n"
401 " \n"
402 "varying vec4 vVaryingColor; \n"
403 " \n"
404 "void main() \n"
405 "{ \n"
406 " gl_FragColor = vVaryingColor; \n"
407 "} \n";
409 gl.display = eglGetDisplay(gbm.dev);
411 if (!eglInitialize(gl.display, &major, &minor)) {
412 printf("failed to initialize\n");
413 return -1;
414 }
416 printf("Using display %p with EGL version %d.%d\n",
417 gl.display, major, minor);
419 printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
420 printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
421 printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
423 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
424 printf("failed to bind api EGL_OPENGL_ES_API\n");
425 return -1;
426 }
428 if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
429 printf("failed to choose config: %d\n", n);
430 return -1;
431 }
433 gl.context = eglCreateContext(gl.display, gl.config,
434 EGL_NO_CONTEXT, context_attribs);
435 if (gl.context == NULL) {
436 printf("failed to create context\n");
437 return -1;
438 }
440 gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
441 if (gl.surface == EGL_NO_SURFACE) {
442 printf("failed to create egl surface\n");
443 return -1;
444 }
446 /* connect the context to the surface */
447 eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
450 gl.vertex_shader = glCreateShader(GL_VERTEX_SHADER);
452 glShaderSource(gl.vertex_shader, 1, &vertex_shader_source, NULL);
453 glCompileShader(gl.vertex_shader);
455 glGetShaderiv(gl.vertex_shader, GL_COMPILE_STATUS, &ret);
456 if (!ret) {
457 char *log;
459 printf("vertex shader compilation failed!:\n");
460 glGetShaderiv(gl.vertex_shader, GL_INFO_LOG_LENGTH, &ret);
461 if (ret > 1) {
462 log = malloc(ret);
463 glGetShaderInfoLog(gl.vertex_shader, ret, NULL, log);
464 printf("%s", log);
465 }
467 return -1;
468 }
470 gl.fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
472 glShaderSource(gl.fragment_shader, 1, &fragment_shader_source, NULL);
473 glCompileShader(gl.fragment_shader);
475 glGetShaderiv(gl.fragment_shader, GL_COMPILE_STATUS, &ret);
476 if (!ret) {
477 char *log;
479 printf("fragment shader compilation failed!:\n");
480 glGetShaderiv(gl.fragment_shader, GL_INFO_LOG_LENGTH, &ret);
482 if (ret > 1) {
483 log = malloc(ret);
484 glGetShaderInfoLog(gl.fragment_shader, ret, NULL, log);
485 printf("%s", log);
486 }
488 return -1;
489 }
491 gl.program = glCreateProgram();
493 glAttachShader(gl.program, gl.vertex_shader);
494 glAttachShader(gl.program, gl.fragment_shader);
496 glBindAttribLocation(gl.program, 0, "in_position");
497 glBindAttribLocation(gl.program, 1, "in_normal");
498 glBindAttribLocation(gl.program, 2, "in_color");
500 glLinkProgram(gl.program);
502 glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
503 if (!ret) {
504 char *log;
506 printf("program linking failed!:\n");
507 glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
509 if (ret > 1) {
510 log = malloc(ret);
511 glGetProgramInfoLog(gl.program, ret, NULL, log);
512 printf("%s", log);
513 }
515 return -1;
516 }
518 glUseProgram(gl.program);
520 gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
521 gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
522 gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
524 glViewport(0, 0, drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay);
525 glEnable(GL_CULL_FACE);
527 gl.positionsoffset = 0;
528 gl.colorsoffset = sizeof(vVertices);
529 gl.normalsoffset = sizeof(vVertices) + sizeof(vColors);
530 glGenBuffers(1, &gl.vbo);
531 glBindBuffer(GL_ARRAY_BUFFER, gl.vbo);
532 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
533 glBufferSubData(GL_ARRAY_BUFFER, gl.positionsoffset, sizeof(vVertices), &vVertices[0]);
534 glBufferSubData(GL_ARRAY_BUFFER, gl.colorsoffset, sizeof(vColors), &vColors[0]);
535 glBufferSubData(GL_ARRAY_BUFFER, gl.normalsoffset, sizeof(vNormals), &vNormals[0]);
536 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.positionsoffset);
537 glEnableVertexAttribArray(0);
538 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.normalsoffset);
539 glEnableVertexAttribArray(1);
540 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.colorsoffset);
541 glEnableVertexAttribArray(2);
543 return 0;
544 }
546 static void exit_gbm(void)
547 {
548 gbm_surface_destroy(gbm.surface);
549 gbm_device_destroy(gbm.dev);
550 return;
551 }
553 static void exit_gl(void)
554 {
555 glDeleteProgram(gl.program);
556 glDeleteBuffers(1, &gl.vbo);
557 glDeleteShader(gl.fragment_shader);
558 glDeleteShader(gl.vertex_shader);
559 eglDestroySurface(gl.display, gl.surface);
560 eglDestroyContext(gl.display, gl.context);
561 eglTerminate(gl.display);
562 return;
563 }
565 static void exit_drm(void)
566 {
568 drmModeRes *resources;
569 int i;
571 resources = (drmModeRes *)drm.resource_id;
572 for (i = 0; i < resources->count_connectors; i++) {
573 drmModeFreeEncoder(drm.encoder[i]);
574 drmModeFreeConnector(drm.connectors[i]);
575 }
576 drmModeFreeResources(drm.resource_id);
577 drmClose(drm.fd);
578 return;
579 }
581 void cleanup_kmscube(void)
582 {
583 exit_gl();
584 exit_gbm();
585 exit_drm();
586 printf("Cleanup of GL, GBM and DRM completed\n");
587 return;
588 }
590 static void draw(uint32_t i)
591 {
592 ESMatrix modelview;
594 /* clear the color buffer */
595 glClearColor(0.5, 0.5, 0.5, 1.0);
596 glClear(GL_COLOR_BUFFER_BIT);
598 esMatrixLoadIdentity(&modelview);
599 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
600 esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
601 esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
602 esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
604 GLfloat aspect = (GLfloat)(drm.mode[DISP_ID]->vdisplay) / (GLfloat)(drm.mode[DISP_ID]->hdisplay);
606 ESMatrix projection;
607 esMatrixLoadIdentity(&projection);
608 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
610 ESMatrix modelviewprojection;
611 esMatrixLoadIdentity(&modelviewprojection);
612 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
614 float normal[9];
615 normal[0] = modelview.m[0][0];
616 normal[1] = modelview.m[0][1];
617 normal[2] = modelview.m[0][2];
618 normal[3] = modelview.m[1][0];
619 normal[4] = modelview.m[1][1];
620 normal[5] = modelview.m[1][2];
621 normal[6] = modelview.m[2][0];
622 normal[7] = modelview.m[2][1];
623 normal[8] = modelview.m[2][2];
625 glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
626 glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
627 glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
629 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
630 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
631 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
632 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
633 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
634 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
635 }
637 static void
638 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
639 {
640 struct drm_fb *fb = data;
641 struct gbm_device *gbm = gbm_bo_get_device(bo);
643 if (fb->fb_id)
644 drmModeRmFB(drm.fd, fb->fb_id);
646 free(fb);
647 }
649 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
650 {
651 struct drm_fb *fb = gbm_bo_get_user_data(bo);
652 uint32_t width, height, stride, handle;
653 int ret;
655 if (fb)
656 return fb;
658 fb = calloc(1, sizeof *fb);
659 fb->bo = bo;
661 width = gbm_bo_get_width(bo);
662 height = gbm_bo_get_height(bo);
663 stride = gbm_bo_get_stride(bo);
664 handle = gbm_bo_get_handle(bo).u32;
666 ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
667 if (ret) {
668 printf("failed to create fb: %s\n", strerror(errno));
669 free(fb);
670 return NULL;
671 }
673 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
675 return fb;
676 }
678 static void page_flip_handler(int fd, unsigned int frame,
679 unsigned int sec, unsigned int usec, void *data)
680 {
681 int *waiting_for_flip = data;
682 *waiting_for_flip = 0;
683 }
685 void print_usage()
686 {
687 printf("Usage : kmscube <options>\n");
688 printf("\t-h : Help\n");
689 printf("\t-a : Enable all displays\n");
690 printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
691 printf("\t-n <number> (optional): Number of frames to render\n");
692 }
694 int kms_signalhandler(int signum)
695 {
696 switch(signum) {
697 case SIGINT:
698 case SIGTERM:
699 /* Allow the pending page flip requests to be completed before
700 * the teardown sequence */
701 sleep(1);
702 printf("Handling signal number = %d\n", signum);
703 cleanup_kmscube();
704 break;
705 default:
706 printf("Unknown signal\n");
707 break;
708 }
709 exit(1);
710 }
712 int main(int argc, char *argv[])
713 {
714 fd_set fds;
715 drmEventContext evctx = {
716 .version = DRM_EVENT_CONTEXT_VERSION,
717 .page_flip_handler = page_flip_handler,
718 };
719 struct gbm_bo *bo;
720 struct drm_fb *fb;
721 uint32_t i = 0;
722 int ret;
723 int opt;
724 int frame_count = -1;
726 signal(SIGINT, kms_signalhandler);
727 signal(SIGTERM, kms_signalhandler);
729 while ((opt = getopt(argc, argv, "ahc:n:")) != -1) {
730 switch(opt) {
731 case 'a':
732 all_display = 1;
733 break;
735 case 'h':
736 print_usage();
737 return 0;
739 case 'c':
740 connector_id = atoi(optarg);
741 break;
742 case 'n':
743 frame_count = atoi(optarg);
744 break;
747 default:
748 printf("Undefined option %s\n", argv[optind]);
749 print_usage();
750 return -1;
751 }
752 }
754 if (all_display) {
755 printf("### Enabling all displays\n");
756 connector_id = -1;
757 }
759 ret = init_drm();
760 if (ret) {
761 printf("failed to initialize DRM\n");
762 return ret;
763 }
764 printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",
765 drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,
766 drm.mode[DISP_ID]->vdisplay);
768 FD_ZERO(&fds);
769 FD_SET(drm.fd, &fds);
771 ret = init_gbm();
772 if (ret) {
773 printf("failed to initialize GBM\n");
774 return ret;
775 }
777 ret = init_gl();
778 if (ret) {
779 printf("failed to initialize EGL\n");
780 return ret;
781 }
783 /* clear the color buffer */
784 glClearColor(0.5, 0.5, 0.5, 1.0);
785 glClear(GL_COLOR_BUFFER_BIT);
786 eglSwapBuffers(gl.display, gl.surface);
787 bo = gbm_surface_lock_front_buffer(gbm.surface);
788 fb = drm_fb_get_from_bo(bo);
790 /* set mode: */
791 if (all_display) {
792 for (i=0; i<drm.ndisp; i++) {
793 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0,
794 &drm.connector_id[i], 1, drm.mode[i]);
795 if (ret) {
796 printf("display %d failed to set mode: %s\n", i, strerror(errno));
797 return ret;
798 }
799 }
800 } else {
801 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
802 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
803 if (ret) {
804 printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
805 return ret;
806 }
807 }
809 while (frame_count != 0) {
810 struct gbm_bo *next_bo;
811 int waiting_for_flip = 1;
813 draw(i++);
815 eglSwapBuffers(gl.display, gl.surface);
816 next_bo = gbm_surface_lock_front_buffer(gbm.surface);
817 fb = drm_fb_get_from_bo(next_bo);
819 /*
820 * Here you could also update drm plane layers if you want
821 * hw composition
822 */
824 ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
825 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
826 if (ret) {
827 printf("failed to queue page flip: %s\n", strerror(errno));
828 return -1;
829 }
831 while (waiting_for_flip) {
832 ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
833 if (ret < 0) {
834 printf("select err: %s\n", strerror(errno));
835 return ret;
836 } else if (ret == 0) {
837 printf("select timeout!\n");
838 return -1;
839 } else if (FD_ISSET(0, &fds)) {
840 continue;
841 }
842 drmHandleEvent(drm.fd, &evctx);
843 }
845 /* release last buffer to render on again: */
846 gbm_surface_release_buffer(gbm.surface, bo);
847 bo = next_bo;
849 if(frame_count >= 0)
850 frame_count--;
851 }
853 cleanup_kmscube();
854 printf("\n Exiting kmscube \n");
856 return ret;
857 }