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 <stdbool.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <signal.h>
39 #include <xf86drm.h>
40 #include <xf86drmMode.h>
41 #include <drm_fourcc.h>
42 #include <gbm.h>
44 #include "esUtil.h"
46 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
48 #define MAX_DISPLAYS (4)
49 uint8_t DISP_ID = 0;
50 uint8_t all_display = 0;
51 int8_t connector_id = -1;
52 char* device = "/dev/dri/card0";
54 static struct {
55 EGLDisplay display;
56 EGLConfig config;
57 EGLContext context;
58 EGLSurface surface;
59 GLuint program;
60 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
61 GLuint vbo;
62 GLuint positionsoffset, colorsoffset, normalsoffset;
63 GLuint vertex_shader, fragment_shader;
64 } gl;
66 static struct {
67 struct gbm_device *dev;
68 struct gbm_surface *surface;
69 } gbm;
71 static struct {
72 int fd;
73 uint32_t ndisp;
74 uint32_t crtc_id[MAX_DISPLAYS];
75 uint32_t connector_id[MAX_DISPLAYS];
76 uint32_t resource_id;
77 uint32_t encoder[MAX_DISPLAYS];
78 uint32_t format[MAX_DISPLAYS];
79 drmModeModeInfo *mode[MAX_DISPLAYS];
80 drmModeConnector *connectors[MAX_DISPLAYS];
81 } drm;
83 struct drm_fb {
84 struct gbm_bo *bo;
85 uint32_t fb_id;
86 };
88 static uint32_t drm_fmt_to_gbm_fmt(uint32_t fmt)
89 {
90 switch (fmt) {
91 case DRM_FORMAT_XRGB8888:
92 return GBM_FORMAT_XRGB8888;
93 case DRM_FORMAT_ARGB8888:
94 return GBM_FORMAT_ARGB8888;
95 case DRM_FORMAT_RGB565:
96 return GBM_FORMAT_RGB565;
97 default:
98 printf("Unsupported DRM format: 0x%x", fmt);
99 return GBM_FORMAT_XRGB8888;
100 }
101 }
103 static bool search_plane_format(uint32_t desired_format, int formats_count, uint32_t* formats)
104 {
105 int i;
107 for ( i = 0; i < formats_count; i++)
108 {
109 if (desired_format == formats[i])
110 return true;
111 }
113 return false;
114 }
116 int get_drm_prop_val(int fd, drmModeObjectPropertiesPtr props,
117 const char *name, unsigned int *p_val) {
118 drmModePropertyPtr p;
119 unsigned int i, prop_id = 0; /* Property ID should always be > 0 */
121 for (i = 0; !prop_id && i < props->count_props; i++) {
122 p = drmModeGetProperty(fd, props->props[i]);
123 if (!strcmp(p->name, name)){
124 prop_id = p->prop_id;
125 break;
126 }
127 drmModeFreeProperty(p);
128 }
130 if (!prop_id) {
131 printf("Could not find %s property\n", name);
132 return(-1);
133 }
135 drmModeFreeProperty(p);
136 *p_val = props->prop_values[i];
137 return 0;
138 }
140 static bool set_drm_format(void)
141 {
142 /* desired DRM format in order */
143 static const uint32_t drm_formats[] = {DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_RGB565};
144 drmModePlaneRes *plane_res;
145 bool found = false;
146 int i,k;
148 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
150 plane_res = drmModeGetPlaneResources(drm.fd);
152 if (!plane_res) {
153 printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
154 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
155 return false;
156 }
158 /*
159 * find the plane connected to crtc_id (the primary plane) and then find the desired pixel format
160 * from the plane format list
161 */
162 for (i = 0; i < plane_res->count_planes; i++)
163 {
164 drmModePlane *plane = drmModeGetPlane(drm.fd, plane_res->planes[i]);
165 drmModeObjectProperties *props;
166 unsigned int plane_type;
168 if(plane == NULL)
169 continue;
171 props = drmModeObjectGetProperties(drm.fd, plane->plane_id, DRM_MODE_OBJECT_PLANE);
173 if(props == NULL){
174 printf("plane (%d) properties not found\n", plane->plane_id);
175 drmModeFreePlane(plane);
176 continue;
177 }
179 if(get_drm_prop_val(drm.fd, props, "type", &plane_type) < 0)
180 {
181 printf("plane (%d) type value not found\n", plane->plane_id);
182 drmModeFreeObjectProperties(props);
183 drmModeFreePlane(plane);
184 continue;
185 }
187 if (plane_type != DRM_PLANE_TYPE_PRIMARY)
188 {
189 drmModeFreeObjectProperties(props);
190 drmModeFreePlane(plane);
191 continue;
192 }
193 else if (!plane->crtc_id)
194 {
195 plane->crtc_id = drm.crtc_id[drm.ndisp];
196 }
198 drmModeFreeObjectProperties(props);
200 if (plane->crtc_id == drm.crtc_id[drm.ndisp])
201 {
202 for (k = 0; k < ARRAY_SIZE(drm_formats); k++)
203 {
204 if (search_plane_format(drm_formats[k], plane->count_formats, plane->formats))
205 {
206 drm.format[drm.ndisp] = drm_formats[k];
207 drmModeFreePlane(plane);
208 drmModeFreePlaneResources(plane_res);
209 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
210 return true;
211 }
212 }
213 }
215 drmModeFreePlane(plane);
216 }
218 drmModeFreePlaneResources(plane_res);
219 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
220 return false;
221 }
223 static int init_drm(void)
224 {
225 drmModeRes *resources;
226 drmModeConnector *connector = NULL;
227 drmModeEncoder *encoder = NULL;
228 drmModeCrtc *crtc = NULL;
230 int i, j, k;
231 uint32_t maxRes, curRes;
233 /* Open default dri device */
234 drm.fd = open(device, O_RDWR | O_CLOEXEC);
235 if (drm.fd < 0) {
236 printf("could not open drm device %s\n", device);
237 return -1;
238 }
240 resources = drmModeGetResources(drm.fd);
241 if (!resources) {
242 printf("drmModeGetResources failed: %s\n", strerror(errno));
243 return -1;
244 }
245 drm.resource_id = (uint32_t) resources;
247 /* find a connected connector: */
248 for (i = 0; i < resources->count_connectors; i++) {
249 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
250 if (connector->connection == DRM_MODE_CONNECTED) {
252 /* find the matched encoders */
253 for (j=0; j<connector->count_encoders; j++) {
254 encoder = drmModeGetEncoder(drm.fd, connector->encoders[j]);
256 /* Take the fisrt one, if none is assigned */
257 if (!connector->encoder_id)
258 {
259 connector->encoder_id = encoder->encoder_id;
260 }
262 if (encoder->encoder_id == connector->encoder_id)
263 {
264 /* find the first valid CRTC if not assigned */
265 if (!encoder->crtc_id)
266 {
267 for (k = 0; k < resources->count_crtcs; ++k) {
268 /* check whether this CRTC works with the encoder */
269 if (!(encoder->possible_crtcs & (1 << k)))
270 continue;
272 encoder->crtc_id = resources->crtcs[k];
273 break;
274 }
276 if (!encoder->crtc_id)
277 {
278 printf("Encoder(%d): no CRTC find!\n", encoder->encoder_id);
279 drmModeFreeEncoder(encoder);
280 encoder = NULL;
281 continue;
282 }
283 }
285 break;
286 }
288 drmModeFreeEncoder(encoder);
289 encoder = NULL;
290 }
292 if (!encoder) {
293 printf("Connector (%d): no encoder!\n", connector->connector_id);
294 drmModeFreeConnector(connector);
295 continue;
296 }
298 /* choose the current or first supported mode */
299 crtc = drmModeGetCrtc(drm.fd, encoder->crtc_id);
300 for (j = 0; j < connector->count_modes; j++)
301 {
302 if (crtc->mode_valid)
303 {
304 if ((connector->modes[j].hdisplay == crtc->width) &&
305 (connector->modes[j].vdisplay == crtc->height))
306 {
307 drm.mode[drm.ndisp] = &connector->modes[j];
308 break;
309 }
310 }
311 else
312 {
313 if ((connector->modes[j].hdisplay == crtc->x) &&
314 (connector->modes[j].vdisplay == crtc->y))
315 {
316 drm.mode[drm.ndisp] = &connector->modes[j];
317 break;
318 }
319 }
320 }
322 if(j >= connector->count_modes)
323 drm.mode[drm.ndisp] = &connector->modes[0];
325 drm.connector_id[drm.ndisp] = connector->connector_id;
327 drm.encoder[drm.ndisp] = (uint32_t) encoder;
328 drm.crtc_id[drm.ndisp] = encoder->crtc_id;
329 drm.connectors[drm.ndisp] = connector;
331 if (!set_drm_format())
332 {
333 // Error handling
334 printf("No desired pixel format found!\n");
335 return -1;
336 }
338 printf("### Display [%d]: CRTC = %d, Connector = %d, format = 0x%x\n", drm.ndisp, drm.crtc_id[drm.ndisp], drm.connector_id[drm.ndisp], drm.format[drm.ndisp]);
339 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);
340 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);
341 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);
343 /* If a connector_id is specified, use the corresponding display */
344 if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
345 DISP_ID = drm.ndisp;
347 /* If all displays are enabled, choose the connector with maximum
348 * resolution as the primary display */
349 if (all_display) {
350 maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
351 curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;
353 if (curRes > maxRes)
354 DISP_ID = drm.ndisp;
355 }
357 drm.ndisp++;
358 } else {
359 drmModeFreeConnector(connector);
360 }
361 }
363 if (drm.ndisp == 0) {
364 /* we could be fancy and listen for hotplug events and wait for
365 * a connector..
366 */
367 printf("no connected connector!\n");
368 return -1;
369 }
371 return 0;
372 }
374 static int init_gbm(void)
375 {
376 gbm.dev = gbm_create_device(drm.fd);
378 gbm.surface = gbm_surface_create(gbm.dev,
379 drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay,
380 drm_fmt_to_gbm_fmt(drm.format[DISP_ID]),
381 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
382 if (!gbm.surface) {
383 printf("failed to create gbm surface\n");
384 return -1;
385 }
387 return 0;
388 }
390 static int init_gl(void)
391 {
392 EGLint major, minor, n;
393 GLint ret;
395 static const GLfloat vVertices[] = {
396 // front
397 -1.0f, -1.0f, +1.0f, // point blue
398 +1.0f, -1.0f, +1.0f, // point magenta
399 -1.0f, +1.0f, +1.0f, // point cyan
400 +1.0f, +1.0f, +1.0f, // point white
401 // back
402 +1.0f, -1.0f, -1.0f, // point red
403 -1.0f, -1.0f, -1.0f, // point black
404 +1.0f, +1.0f, -1.0f, // point yellow
405 -1.0f, +1.0f, -1.0f, // point green
406 // right
407 +1.0f, -1.0f, +1.0f, // point magenta
408 +1.0f, -1.0f, -1.0f, // point red
409 +1.0f, +1.0f, +1.0f, // point white
410 +1.0f, +1.0f, -1.0f, // point yellow
411 // left
412 -1.0f, -1.0f, -1.0f, // point black
413 -1.0f, -1.0f, +1.0f, // point blue
414 -1.0f, +1.0f, -1.0f, // point green
415 -1.0f, +1.0f, +1.0f, // point cyan
416 // top
417 -1.0f, +1.0f, +1.0f, // point cyan
418 +1.0f, +1.0f, +1.0f, // point white
419 -1.0f, +1.0f, -1.0f, // point green
420 +1.0f, +1.0f, -1.0f, // point yellow
421 // bottom
422 -1.0f, -1.0f, -1.0f, // point black
423 +1.0f, -1.0f, -1.0f, // point red
424 -1.0f, -1.0f, +1.0f, // point blue
425 +1.0f, -1.0f, +1.0f // point magenta
426 };
428 static const GLfloat vColors[] = {
429 // front
430 0.0f, 0.0f, 1.0f, // blue
431 1.0f, 0.0f, 1.0f, // magenta
432 0.0f, 1.0f, 1.0f, // cyan
433 1.0f, 1.0f, 1.0f, // white
434 // back
435 1.0f, 0.0f, 0.0f, // red
436 0.0f, 0.0f, 0.0f, // black
437 1.0f, 1.0f, 0.0f, // yellow
438 0.0f, 1.0f, 0.0f, // green
439 // right
440 1.0f, 0.0f, 1.0f, // magenta
441 1.0f, 0.0f, 0.0f, // red
442 1.0f, 1.0f, 1.0f, // white
443 1.0f, 1.0f, 0.0f, // yellow
444 // left
445 0.0f, 0.0f, 0.0f, // black
446 0.0f, 0.0f, 1.0f, // blue
447 0.0f, 1.0f, 0.0f, // green
448 0.0f, 1.0f, 1.0f, // cyan
449 // top
450 0.0f, 1.0f, 1.0f, // cyan
451 1.0f, 1.0f, 1.0f, // white
452 0.0f, 1.0f, 0.0f, // green
453 1.0f, 1.0f, 0.0f, // yellow
454 // bottom
455 0.0f, 0.0f, 0.0f, // black
456 1.0f, 0.0f, 0.0f, // red
457 0.0f, 0.0f, 1.0f, // blue
458 1.0f, 0.0f, 1.0f // magenta
459 };
461 static const GLfloat vNormals[] = {
462 // front
463 +0.0f, +0.0f, +1.0f, // forward
464 +0.0f, +0.0f, +1.0f, // forward
465 +0.0f, +0.0f, +1.0f, // forward
466 +0.0f, +0.0f, +1.0f, // forward
467 // back
468 +0.0f, +0.0f, -1.0f, // backbard
469 +0.0f, +0.0f, -1.0f, // backbard
470 +0.0f, +0.0f, -1.0f, // backbard
471 +0.0f, +0.0f, -1.0f, // backbard
472 // right
473 +1.0f, +0.0f, +0.0f, // right
474 +1.0f, +0.0f, +0.0f, // right
475 +1.0f, +0.0f, +0.0f, // right
476 +1.0f, +0.0f, +0.0f, // right
477 // left
478 -1.0f, +0.0f, +0.0f, // left
479 -1.0f, +0.0f, +0.0f, // left
480 -1.0f, +0.0f, +0.0f, // left
481 -1.0f, +0.0f, +0.0f, // left
482 // top
483 +0.0f, +1.0f, +0.0f, // up
484 +0.0f, +1.0f, +0.0f, // up
485 +0.0f, +1.0f, +0.0f, // up
486 +0.0f, +1.0f, +0.0f, // up
487 // bottom
488 +0.0f, -1.0f, +0.0f, // down
489 +0.0f, -1.0f, +0.0f, // down
490 +0.0f, -1.0f, +0.0f, // down
491 +0.0f, -1.0f, +0.0f // down
492 };
494 static const EGLint context_attribs[] = {
495 EGL_CONTEXT_CLIENT_VERSION, 2,
496 EGL_NONE
497 };
499 static const EGLint config_attribs[] = {
500 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
501 EGL_RED_SIZE, 1,
502 EGL_GREEN_SIZE, 1,
503 EGL_BLUE_SIZE, 1,
504 EGL_ALPHA_SIZE, 0,
505 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
506 EGL_NONE
507 };
509 static const char *vertex_shader_source =
510 "uniform mat4 modelviewMatrix; \n"
511 "uniform mat4 modelviewprojectionMatrix;\n"
512 "uniform mat3 normalMatrix; \n"
513 " \n"
514 "attribute vec4 in_position; \n"
515 "attribute vec3 in_normal; \n"
516 "attribute vec4 in_color; \n"
517 "\n"
518 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
519 " \n"
520 "varying vec4 vVaryingColor; \n"
521 " \n"
522 "void main() \n"
523 "{ \n"
524 " gl_Position = modelviewprojectionMatrix * in_position;\n"
525 " vec3 vEyeNormal = normalMatrix * in_normal;\n"
526 " vec4 vPosition4 = modelviewMatrix * in_position;\n"
527 " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
528 " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
529 " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
530 " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
531 "} \n";
533 static const char *fragment_shader_source =
534 "precision mediump float; \n"
535 " \n"
536 "varying vec4 vVaryingColor; \n"
537 " \n"
538 "void main() \n"
539 "{ \n"
540 " gl_FragColor = vVaryingColor; \n"
541 "} \n";
543 gl.display = eglGetDisplay(gbm.dev);
545 if (!eglInitialize(gl.display, &major, &minor)) {
546 printf("failed to initialize\n");
547 return -1;
548 }
550 printf("Using display %p with EGL version %d.%d\n",
551 gl.display, major, minor);
553 printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
554 printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
555 printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
557 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
558 printf("failed to bind api EGL_OPENGL_ES_API\n");
559 return -1;
560 }
562 if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
563 printf("failed to choose config: %d\n", n);
564 return -1;
565 }
567 gl.context = eglCreateContext(gl.display, gl.config,
568 EGL_NO_CONTEXT, context_attribs);
569 if (gl.context == NULL) {
570 printf("failed to create context\n");
571 return -1;
572 }
574 gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
575 if (gl.surface == EGL_NO_SURFACE) {
576 printf("failed to create egl surface\n");
577 return -1;
578 }
580 /* connect the context to the surface */
581 eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
584 gl.vertex_shader = glCreateShader(GL_VERTEX_SHADER);
586 glShaderSource(gl.vertex_shader, 1, &vertex_shader_source, NULL);
587 glCompileShader(gl.vertex_shader);
589 glGetShaderiv(gl.vertex_shader, GL_COMPILE_STATUS, &ret);
590 if (!ret) {
591 char *log;
593 printf("vertex shader compilation failed!:\n");
594 glGetShaderiv(gl.vertex_shader, GL_INFO_LOG_LENGTH, &ret);
595 if (ret > 1) {
596 log = malloc(ret);
597 glGetShaderInfoLog(gl.vertex_shader, ret, NULL, log);
598 printf("%s", log);
599 }
601 return -1;
602 }
604 gl.fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
606 glShaderSource(gl.fragment_shader, 1, &fragment_shader_source, NULL);
607 glCompileShader(gl.fragment_shader);
609 glGetShaderiv(gl.fragment_shader, GL_COMPILE_STATUS, &ret);
610 if (!ret) {
611 char *log;
613 printf("fragment shader compilation failed!:\n");
614 glGetShaderiv(gl.fragment_shader, GL_INFO_LOG_LENGTH, &ret);
616 if (ret > 1) {
617 log = malloc(ret);
618 glGetShaderInfoLog(gl.fragment_shader, ret, NULL, log);
619 printf("%s", log);
620 }
622 return -1;
623 }
625 gl.program = glCreateProgram();
627 glAttachShader(gl.program, gl.vertex_shader);
628 glAttachShader(gl.program, gl.fragment_shader);
630 glBindAttribLocation(gl.program, 0, "in_position");
631 glBindAttribLocation(gl.program, 1, "in_normal");
632 glBindAttribLocation(gl.program, 2, "in_color");
634 glLinkProgram(gl.program);
636 glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
637 if (!ret) {
638 char *log;
640 printf("program linking failed!:\n");
641 glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
643 if (ret > 1) {
644 log = malloc(ret);
645 glGetProgramInfoLog(gl.program, ret, NULL, log);
646 printf("%s", log);
647 }
649 return -1;
650 }
652 glUseProgram(gl.program);
654 gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
655 gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
656 gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
658 glViewport(0, 0, drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay);
659 glEnable(GL_CULL_FACE);
661 gl.positionsoffset = 0;
662 gl.colorsoffset = sizeof(vVertices);
663 gl.normalsoffset = sizeof(vVertices) + sizeof(vColors);
664 glGenBuffers(1, &gl.vbo);
665 glBindBuffer(GL_ARRAY_BUFFER, gl.vbo);
666 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
667 glBufferSubData(GL_ARRAY_BUFFER, gl.positionsoffset, sizeof(vVertices), &vVertices[0]);
668 glBufferSubData(GL_ARRAY_BUFFER, gl.colorsoffset, sizeof(vColors), &vColors[0]);
669 glBufferSubData(GL_ARRAY_BUFFER, gl.normalsoffset, sizeof(vNormals), &vNormals[0]);
670 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.positionsoffset);
671 glEnableVertexAttribArray(0);
672 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.normalsoffset);
673 glEnableVertexAttribArray(1);
674 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.colorsoffset);
675 glEnableVertexAttribArray(2);
677 return 0;
678 }
680 static void exit_gbm(void)
681 {
682 gbm_surface_destroy(gbm.surface);
683 gbm_device_destroy(gbm.dev);
684 return;
685 }
687 static void exit_gl(void)
688 {
689 glDeleteProgram(gl.program);
690 glDeleteBuffers(1, &gl.vbo);
691 glDeleteShader(gl.fragment_shader);
692 glDeleteShader(gl.vertex_shader);
693 eglDestroySurface(gl.display, gl.surface);
694 eglDestroyContext(gl.display, gl.context);
695 eglTerminate(gl.display);
696 return;
697 }
699 static void exit_drm(void)
700 {
702 drmModeRes *resources;
703 int i;
705 resources = (drmModeRes *)drm.resource_id;
706 for (i = 0; i < resources->count_connectors; i++) {
707 drmModeFreeEncoder(drm.encoder[i]);
708 drmModeFreeConnector(drm.connectors[i]);
709 }
710 drmModeFreeResources(drm.resource_id);
711 close(drm.fd);
712 return;
713 }
715 void cleanup_kmscube(void)
716 {
717 exit_gl();
718 exit_gbm();
719 exit_drm();
720 printf("Cleanup of GL, GBM and DRM completed\n");
721 return;
722 }
724 static void draw(uint32_t i)
725 {
726 ESMatrix modelview;
728 /* clear the color buffer */
729 glClearColor(0.5, 0.5, 0.5, 1.0);
730 glClear(GL_COLOR_BUFFER_BIT);
732 esMatrixLoadIdentity(&modelview);
733 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
734 esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
735 esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
736 esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
738 GLfloat aspect = (GLfloat)(drm.mode[DISP_ID]->vdisplay) / (GLfloat)(drm.mode[DISP_ID]->hdisplay);
740 ESMatrix projection;
741 esMatrixLoadIdentity(&projection);
742 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
744 ESMatrix modelviewprojection;
745 esMatrixLoadIdentity(&modelviewprojection);
746 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
748 float normal[9];
749 normal[0] = modelview.m[0][0];
750 normal[1] = modelview.m[0][1];
751 normal[2] = modelview.m[0][2];
752 normal[3] = modelview.m[1][0];
753 normal[4] = modelview.m[1][1];
754 normal[5] = modelview.m[1][2];
755 normal[6] = modelview.m[2][0];
756 normal[7] = modelview.m[2][1];
757 normal[8] = modelview.m[2][2];
759 glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
760 glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
761 glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
763 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
764 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
765 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
766 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
767 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
768 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
769 }
771 static void
772 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
773 {
774 struct drm_fb *fb = data;
775 struct gbm_device *gbm = gbm_bo_get_device(bo);
777 if (fb->fb_id)
778 drmModeRmFB(drm.fd, fb->fb_id);
780 free(fb);
781 }
783 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
784 {
785 struct drm_fb *fb = gbm_bo_get_user_data(bo);
786 uint32_t width, height, format;
787 uint32_t bo_handles[4] = {0}, offsets[4] = {0}, pitches[4] = {0};
788 int ret;
790 if (fb)
791 return fb;
793 fb = calloc(1, sizeof *fb);
794 fb->bo = bo;
796 width = gbm_bo_get_width(bo);
797 height = gbm_bo_get_height(bo);
798 pitches[0] = gbm_bo_get_stride(bo);
799 bo_handles[0] = gbm_bo_get_handle(bo).u32;
800 format = gbm_bo_get_format(bo);
802 ret = drmModeAddFB2(drm.fd, width, height, format, bo_handles, pitches, offsets, &fb->fb_id, 0);
803 if (ret) {
804 printf("failed to create fb: %s\n", strerror(errno));
805 free(fb);
806 return NULL;
807 }
809 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
811 return fb;
812 }
814 static void page_flip_handler(int fd, unsigned int frame,
815 unsigned int sec, unsigned int usec, void *data)
816 {
817 int *waiting_for_flip = data;
818 *waiting_for_flip = *waiting_for_flip - 1;
819 }
821 void print_usage()
822 {
823 printf("Usage : kmscube <options>\n");
824 printf("\t-h : Help\n");
825 printf("\t-a : Enable all displays\n");
826 printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
827 printf("\t-d /dev/dri/cardX : DRM device to be used. [If not specified, use /dev/dri/card0]\n");
828 printf("\t-n <number> (optional): Number of frames to render\n");
829 }
831 int kms_signalhandler(int signum)
832 {
833 switch(signum) {
834 case SIGINT:
835 case SIGTERM:
836 /* Allow the pending page flip requests to be completed before
837 * the teardown sequence */
838 sleep(1);
839 printf("Handling signal number = %d\n", signum);
840 cleanup_kmscube();
841 break;
842 default:
843 printf("Unknown signal\n");
844 break;
845 }
846 exit(1);
847 }
849 int main(int argc, char *argv[])
850 {
851 fd_set fds;
852 drmEventContext evctx = {
853 .version = DRM_EVENT_CONTEXT_VERSION,
854 .page_flip_handler = page_flip_handler,
855 };
856 struct gbm_bo *bo;
857 struct drm_fb *fb;
858 uint32_t i = 0;
859 int ret;
860 int opt;
861 int frame_count = -1;
863 signal(SIGINT, kms_signalhandler);
864 signal(SIGTERM, kms_signalhandler);
866 while ((opt = getopt(argc, argv, "ahc:d:n:")) != -1) {
867 switch(opt) {
868 case 'a':
869 all_display = 1;
870 break;
872 case 'h':
873 print_usage();
874 return 0;
876 case 'c':
877 connector_id = atoi(optarg);
878 break;
879 case 'd':
880 device = optarg;
881 break;
882 case 'n':
883 frame_count = atoi(optarg);
884 break;
887 default:
888 printf("Undefined option %s\n", argv[optind]);
889 print_usage();
890 return -1;
891 }
892 }
894 if (all_display) {
895 printf("### Enabling all displays\n");
896 connector_id = -1;
897 }
899 ret = init_drm();
900 if (ret) {
901 printf("failed to initialize DRM\n");
902 return ret;
903 }
904 printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",
905 drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,
906 drm.mode[DISP_ID]->vdisplay);
908 FD_ZERO(&fds);
909 FD_SET(drm.fd, &fds);
911 ret = init_gbm();
912 if (ret) {
913 printf("failed to initialize GBM\n");
914 return ret;
915 }
917 ret = init_gl();
918 if (ret) {
919 printf("failed to initialize EGL\n");
920 return ret;
921 }
923 /* clear the color buffer */
924 glClearColor(0.5, 0.5, 0.5, 1.0);
925 glClear(GL_COLOR_BUFFER_BIT);
926 eglSwapBuffers(gl.display, gl.surface);
927 bo = gbm_surface_lock_front_buffer(gbm.surface);
928 fb = drm_fb_get_from_bo(bo);
930 /* set mode: */
931 if (all_display) {
932 for (i=0; i<drm.ndisp; i++) {
933 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0,
934 &drm.connector_id[i], 1, drm.mode[i]);
935 if (ret) {
936 printf("display %d failed to set mode: %s\n", i, strerror(errno));
937 return ret;
938 }
939 }
940 } else {
941 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
942 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
943 if (ret) {
944 printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
945 return ret;
946 }
947 }
949 while (frame_count != 0) {
950 struct gbm_bo *next_bo;
951 int waiting_for_flip;
952 int cc;
954 draw(i++);
956 eglSwapBuffers(gl.display, gl.surface);
957 next_bo = gbm_surface_lock_front_buffer(gbm.surface);
958 fb = drm_fb_get_from_bo(next_bo);
960 /*
961 * Here you could also update drm plane layers if you want
962 * hw composition
963 */
965 if (all_display) {
966 for (cc=0;cc<drm.ndisp; cc++) {
967 ret = drmModePageFlip(drm.fd, drm.crtc_id[cc], fb->fb_id,
968 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
969 if (ret) {
970 printf("failed to queue page flip: %s\n", strerror(errno));
971 return -1;
972 }
973 }
974 waiting_for_flip = drm.ndisp;
975 } else {
976 ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
977 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
978 if (ret) {
979 printf("failed to queue page flip: %s\n", strerror(errno));
980 return -1;
981 }
982 waiting_for_flip = 1;
983 }
985 while (waiting_for_flip) {
986 ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
987 if (ret < 0) {
988 printf("select err: %s\n", strerror(errno));
989 return ret;
990 } else if (ret == 0) {
991 printf("select timeout!\n");
992 return -1;
993 } else if (FD_ISSET(0, &fds)) {
994 continue;
995 }
996 drmHandleEvent(drm.fd, &evctx);
997 }
999 /* release last buffer to render on again: */
1000 gbm_surface_release_buffer(gbm.surface, bo);
1001 bo = next_bo;
1003 if(frame_count >= 0)
1004 frame_count--;
1005 }
1007 cleanup_kmscube();
1008 printf("\n Exiting kmscube \n");
1010 return ret;
1011 }