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;
53 static struct {
54 EGLDisplay display;
55 EGLConfig config;
56 EGLContext context;
57 EGLSurface surface;
58 GLuint program;
59 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
60 GLuint vbo;
61 GLuint positionsoffset, colorsoffset, normalsoffset;
62 GLuint vertex_shader, fragment_shader;
63 } gl;
65 static struct {
66 struct gbm_device *dev;
67 struct gbm_surface *surface;
68 } gbm;
70 static struct {
71 int fd;
72 uint32_t ndisp;
73 uint32_t crtc_id[MAX_DISPLAYS];
74 uint32_t connector_id[MAX_DISPLAYS];
75 uint32_t resource_id;
76 uint32_t encoder[MAX_DISPLAYS];
77 uint32_t format[MAX_DISPLAYS];
78 drmModeModeInfo *mode[MAX_DISPLAYS];
79 drmModeConnector *connectors[MAX_DISPLAYS];
80 } drm;
82 struct drm_fb {
83 struct gbm_bo *bo;
84 uint32_t fb_id;
85 };
87 static uint32_t drm_fmt_to_gbm_fmt(uint32_t fmt)
88 {
89 switch (fmt) {
90 case DRM_FORMAT_XRGB8888:
91 return GBM_FORMAT_XRGB8888;
92 case DRM_FORMAT_ARGB8888:
93 return GBM_FORMAT_ARGB8888;
94 case DRM_FORMAT_RGB565:
95 return GBM_FORMAT_RGB565;
96 default:
97 printf("Unsupported DRM format: 0x%x", fmt);
98 return GBM_FORMAT_XRGB8888;
99 }
100 }
102 static bool search_plane_format(uint32_t desired_format, int formats_count, uint32_t* formats)
103 {
104 int i;
106 for ( i = 0; i < formats_count; i++)
107 {
108 if (desired_format == formats[i])
109 return true;
110 }
112 return false;
113 }
115 int get_drm_prop_val(int fd, drmModeObjectPropertiesPtr props,
116 const char *name, unsigned int *p_val) {
117 drmModePropertyPtr p;
118 unsigned int i, prop_id = 0; /* Property ID should always be > 0 */
120 for (i = 0; !prop_id && i < props->count_props; i++) {
121 p = drmModeGetProperty(fd, props->props[i]);
122 if (!strcmp(p->name, name)){
123 prop_id = p->prop_id;
124 break;
125 }
126 drmModeFreeProperty(p);
127 }
129 if (!prop_id) {
130 printf("Could not find %s property\n", name);
131 return(-1);
132 }
134 drmModeFreeProperty(p);
135 *p_val = props->prop_values[i];
136 return 0;
137 }
139 static bool set_drm_format(void)
140 {
141 /* desired DRM format in order */
142 static const uint32_t drm_formats[] = {DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_RGB565};
143 drmModePlaneRes *plane_res;
144 bool found = false;
145 int i,k;
147 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
149 plane_res = drmModeGetPlaneResources(drm.fd);
151 if (!plane_res) {
152 printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
153 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
154 return false;
155 }
157 /*
158 * find the plane connected to crtc_id (the primary plane) and then find the desired pixel format
159 * from the plane format list
160 */
161 for (i = 0; i < plane_res->count_planes; i++)
162 {
163 drmModePlane *plane = drmModeGetPlane(drm.fd, plane_res->planes[i]);
164 drmModeObjectProperties *props;
165 unsigned int plane_type;
167 if(plane == NULL)
168 continue;
170 props = drmModeObjectGetProperties(drm.fd, plane->plane_id, DRM_MODE_OBJECT_PLANE);
172 if(props == NULL){
173 printf("plane (%d) properties not found\n", plane->plane_id);
174 drmModeFreePlane(plane);
175 continue;
176 }
178 if(get_drm_prop_val(drm.fd, props, "type", &plane_type) < 0)
179 {
180 printf("plane (%d) type value not found\n", plane->plane_id);
181 drmModeFreeObjectProperties(props);
182 drmModeFreePlane(plane);
183 continue;
184 }
186 if (plane_type != DRM_PLANE_TYPE_PRIMARY)
187 {
188 drmModeFreeObjectProperties(props);
189 drmModeFreePlane(plane);
190 continue;
191 }
192 else if (!plane->crtc_id)
193 {
194 plane->crtc_id = drm.crtc_id[drm.ndisp];
195 }
197 drmModeFreeObjectProperties(props);
199 if (plane->crtc_id == drm.crtc_id[drm.ndisp])
200 {
201 for (k = 0; k < ARRAY_SIZE(drm_formats); k++)
202 {
203 if (search_plane_format(drm_formats[k], plane->count_formats, plane->formats))
204 {
205 drm.format[drm.ndisp] = drm_formats[k];
206 drmModeFreePlane(plane);
207 drmModeFreePlaneResources(plane_res);
208 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
209 return true;
210 }
211 }
212 }
214 drmModeFreePlane(plane);
215 }
217 drmModeFreePlaneResources(plane_res);
218 drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
219 return false;
220 }
222 static int init_drm(void)
223 {
224 static const char *modules[] = {
225 "omapdrm", "tilcdc", "i915", "radeon", "nouveau", "vmwgfx", "exynos"
226 };
227 drmModeRes *resources;
228 drmModeConnector *connector = NULL;
229 drmModeEncoder *encoder = NULL;
230 drmModeCrtc *crtc = NULL;
232 int i, j, k;
233 uint32_t maxRes, curRes;
235 for (i = 0; i < ARRAY_SIZE(modules); i++) {
236 printf("trying to load module %s...", modules[i]);
237 drm.fd = drmOpen(modules[i], NULL);
238 if (drm.fd < 0) {
239 printf("failed.\n");
240 } else {
241 printf("success.\n");
242 break;
243 }
244 }
246 if (drm.fd < 0) {
247 printf("could not open drm device\n");
248 return -1;
249 }
251 resources = drmModeGetResources(drm.fd);
252 if (!resources) {
253 printf("drmModeGetResources failed: %s\n", strerror(errno));
254 return -1;
255 }
256 drm.resource_id = (uint32_t) resources;
258 /* find a connected connector: */
259 for (i = 0; i < resources->count_connectors; i++) {
260 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
261 if (connector->connection == DRM_MODE_CONNECTED) {
263 /* find the matched encoders */
264 for (j=0; j<connector->count_encoders; j++) {
265 encoder = drmModeGetEncoder(drm.fd, connector->encoders[j]);
267 /* Take the fisrt one, if none is assigned */
268 if (!connector->encoder_id)
269 {
270 connector->encoder_id = encoder->encoder_id;
271 }
273 if (encoder->encoder_id == connector->encoder_id)
274 {
275 /* find the first valid CRTC if not assigned */
276 if (!encoder->crtc_id)
277 {
278 for (k = 0; k < resources->count_crtcs; ++k) {
279 /* check whether this CRTC works with the encoder */
280 if (!(encoder->possible_crtcs & (1 << k)))
281 continue;
283 encoder->crtc_id = resources->crtcs[k];
284 break;
285 }
287 if (!encoder->crtc_id)
288 {
289 printf("Encoder(%d): no CRTC find!\n", encoder->encoder_id);
290 drmModeFreeEncoder(encoder);
291 encoder = NULL;
292 continue;
293 }
294 }
296 break;
297 }
299 drmModeFreeEncoder(encoder);
300 encoder = NULL;
301 }
303 if (!encoder) {
304 printf("Connector (%d): no encoder!\n", connector->connector_id);
305 drmModeFreeConnector(connector);
306 continue;
307 }
309 /* choose the current or first supported mode */
310 crtc = drmModeGetCrtc(drm.fd, encoder->crtc_id);
311 for (j = 0; j < connector->count_modes; j++)
312 {
313 if (crtc->mode_valid)
314 {
315 if ((connector->modes[j].hdisplay == crtc->width) &&
316 (connector->modes[j].vdisplay == crtc->height))
317 {
318 drm.mode[drm.ndisp] = &connector->modes[j];
319 break;
320 }
321 }
322 else
323 {
324 if ((connector->modes[j].hdisplay == crtc->x) &&
325 (connector->modes[j].vdisplay == crtc->y))
326 {
327 drm.mode[drm.ndisp] = &connector->modes[j];
328 break;
329 }
330 }
331 }
333 if(j >= connector->count_modes)
334 drm.mode[drm.ndisp] = &connector->modes[0];
336 drm.connector_id[drm.ndisp] = connector->connector_id;
338 drm.encoder[drm.ndisp] = (uint32_t) encoder;
339 drm.crtc_id[drm.ndisp] = encoder->crtc_id;
340 drm.connectors[drm.ndisp] = connector;
342 if (!set_drm_format())
343 {
344 // Error handling
345 printf("No desired pixel format found!\n");
346 return -1;
347 }
349 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]);
350 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);
351 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);
352 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);
354 /* If a connector_id is specified, use the corresponding display */
355 if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
356 DISP_ID = drm.ndisp;
358 /* If all displays are enabled, choose the connector with maximum
359 * resolution as the primary display */
360 if (all_display) {
361 maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
362 curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;
364 if (curRes > maxRes)
365 DISP_ID = drm.ndisp;
366 }
368 drm.ndisp++;
369 } else {
370 drmModeFreeConnector(connector);
371 }
372 }
374 if (drm.ndisp == 0) {
375 /* we could be fancy and listen for hotplug events and wait for
376 * a connector..
377 */
378 printf("no connected connector!\n");
379 return -1;
380 }
382 return 0;
383 }
385 static int init_gbm(void)
386 {
387 gbm.dev = gbm_create_device(drm.fd);
389 gbm.surface = gbm_surface_create(gbm.dev,
390 drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay,
391 drm_fmt_to_gbm_fmt(drm.format[DISP_ID]),
392 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
393 if (!gbm.surface) {
394 printf("failed to create gbm surface\n");
395 return -1;
396 }
398 return 0;
399 }
401 static int init_gl(void)
402 {
403 EGLint major, minor, n;
404 GLint ret;
406 static const GLfloat vVertices[] = {
407 // front
408 -1.0f, -1.0f, +1.0f, // point blue
409 +1.0f, -1.0f, +1.0f, // point magenta
410 -1.0f, +1.0f, +1.0f, // point cyan
411 +1.0f, +1.0f, +1.0f, // point white
412 // back
413 +1.0f, -1.0f, -1.0f, // point red
414 -1.0f, -1.0f, -1.0f, // point black
415 +1.0f, +1.0f, -1.0f, // point yellow
416 -1.0f, +1.0f, -1.0f, // point green
417 // right
418 +1.0f, -1.0f, +1.0f, // point magenta
419 +1.0f, -1.0f, -1.0f, // point red
420 +1.0f, +1.0f, +1.0f, // point white
421 +1.0f, +1.0f, -1.0f, // point yellow
422 // left
423 -1.0f, -1.0f, -1.0f, // point black
424 -1.0f, -1.0f, +1.0f, // point blue
425 -1.0f, +1.0f, -1.0f, // point green
426 -1.0f, +1.0f, +1.0f, // point cyan
427 // top
428 -1.0f, +1.0f, +1.0f, // point cyan
429 +1.0f, +1.0f, +1.0f, // point white
430 -1.0f, +1.0f, -1.0f, // point green
431 +1.0f, +1.0f, -1.0f, // point yellow
432 // bottom
433 -1.0f, -1.0f, -1.0f, // point black
434 +1.0f, -1.0f, -1.0f, // point red
435 -1.0f, -1.0f, +1.0f, // point blue
436 +1.0f, -1.0f, +1.0f // point magenta
437 };
439 static const GLfloat vColors[] = {
440 // front
441 0.0f, 0.0f, 1.0f, // blue
442 1.0f, 0.0f, 1.0f, // magenta
443 0.0f, 1.0f, 1.0f, // cyan
444 1.0f, 1.0f, 1.0f, // white
445 // back
446 1.0f, 0.0f, 0.0f, // red
447 0.0f, 0.0f, 0.0f, // black
448 1.0f, 1.0f, 0.0f, // yellow
449 0.0f, 1.0f, 0.0f, // green
450 // right
451 1.0f, 0.0f, 1.0f, // magenta
452 1.0f, 0.0f, 0.0f, // red
453 1.0f, 1.0f, 1.0f, // white
454 1.0f, 1.0f, 0.0f, // yellow
455 // left
456 0.0f, 0.0f, 0.0f, // black
457 0.0f, 0.0f, 1.0f, // blue
458 0.0f, 1.0f, 0.0f, // green
459 0.0f, 1.0f, 1.0f, // cyan
460 // top
461 0.0f, 1.0f, 1.0f, // cyan
462 1.0f, 1.0f, 1.0f, // white
463 0.0f, 1.0f, 0.0f, // green
464 1.0f, 1.0f, 0.0f, // yellow
465 // bottom
466 0.0f, 0.0f, 0.0f, // black
467 1.0f, 0.0f, 0.0f, // red
468 0.0f, 0.0f, 1.0f, // blue
469 1.0f, 0.0f, 1.0f // magenta
470 };
472 static const GLfloat vNormals[] = {
473 // front
474 +0.0f, +0.0f, +1.0f, // forward
475 +0.0f, +0.0f, +1.0f, // forward
476 +0.0f, +0.0f, +1.0f, // forward
477 +0.0f, +0.0f, +1.0f, // forward
478 // back
479 +0.0f, +0.0f, -1.0f, // backbard
480 +0.0f, +0.0f, -1.0f, // backbard
481 +0.0f, +0.0f, -1.0f, // backbard
482 +0.0f, +0.0f, -1.0f, // backbard
483 // right
484 +1.0f, +0.0f, +0.0f, // right
485 +1.0f, +0.0f, +0.0f, // right
486 +1.0f, +0.0f, +0.0f, // right
487 +1.0f, +0.0f, +0.0f, // right
488 // left
489 -1.0f, +0.0f, +0.0f, // left
490 -1.0f, +0.0f, +0.0f, // left
491 -1.0f, +0.0f, +0.0f, // left
492 -1.0f, +0.0f, +0.0f, // left
493 // top
494 +0.0f, +1.0f, +0.0f, // up
495 +0.0f, +1.0f, +0.0f, // up
496 +0.0f, +1.0f, +0.0f, // up
497 +0.0f, +1.0f, +0.0f, // up
498 // bottom
499 +0.0f, -1.0f, +0.0f, // down
500 +0.0f, -1.0f, +0.0f, // down
501 +0.0f, -1.0f, +0.0f, // down
502 +0.0f, -1.0f, +0.0f // down
503 };
505 static const EGLint context_attribs[] = {
506 EGL_CONTEXT_CLIENT_VERSION, 2,
507 EGL_NONE
508 };
510 static const EGLint config_attribs[] = {
511 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
512 EGL_RED_SIZE, 1,
513 EGL_GREEN_SIZE, 1,
514 EGL_BLUE_SIZE, 1,
515 EGL_ALPHA_SIZE, 0,
516 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
517 EGL_NONE
518 };
520 static const char *vertex_shader_source =
521 "uniform mat4 modelviewMatrix; \n"
522 "uniform mat4 modelviewprojectionMatrix;\n"
523 "uniform mat3 normalMatrix; \n"
524 " \n"
525 "attribute vec4 in_position; \n"
526 "attribute vec3 in_normal; \n"
527 "attribute vec4 in_color; \n"
528 "\n"
529 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
530 " \n"
531 "varying vec4 vVaryingColor; \n"
532 " \n"
533 "void main() \n"
534 "{ \n"
535 " gl_Position = modelviewprojectionMatrix * in_position;\n"
536 " vec3 vEyeNormal = normalMatrix * in_normal;\n"
537 " vec4 vPosition4 = modelviewMatrix * in_position;\n"
538 " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
539 " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
540 " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
541 " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
542 "} \n";
544 static const char *fragment_shader_source =
545 "precision mediump float; \n"
546 " \n"
547 "varying vec4 vVaryingColor; \n"
548 " \n"
549 "void main() \n"
550 "{ \n"
551 " gl_FragColor = vVaryingColor; \n"
552 "} \n";
554 gl.display = eglGetDisplay(gbm.dev);
556 if (!eglInitialize(gl.display, &major, &minor)) {
557 printf("failed to initialize\n");
558 return -1;
559 }
561 printf("Using display %p with EGL version %d.%d\n",
562 gl.display, major, minor);
564 printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
565 printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
566 printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
568 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
569 printf("failed to bind api EGL_OPENGL_ES_API\n");
570 return -1;
571 }
573 if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
574 printf("failed to choose config: %d\n", n);
575 return -1;
576 }
578 gl.context = eglCreateContext(gl.display, gl.config,
579 EGL_NO_CONTEXT, context_attribs);
580 if (gl.context == NULL) {
581 printf("failed to create context\n");
582 return -1;
583 }
585 gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
586 if (gl.surface == EGL_NO_SURFACE) {
587 printf("failed to create egl surface\n");
588 return -1;
589 }
591 /* connect the context to the surface */
592 eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
595 gl.vertex_shader = glCreateShader(GL_VERTEX_SHADER);
597 glShaderSource(gl.vertex_shader, 1, &vertex_shader_source, NULL);
598 glCompileShader(gl.vertex_shader);
600 glGetShaderiv(gl.vertex_shader, GL_COMPILE_STATUS, &ret);
601 if (!ret) {
602 char *log;
604 printf("vertex shader compilation failed!:\n");
605 glGetShaderiv(gl.vertex_shader, GL_INFO_LOG_LENGTH, &ret);
606 if (ret > 1) {
607 log = malloc(ret);
608 glGetShaderInfoLog(gl.vertex_shader, ret, NULL, log);
609 printf("%s", log);
610 }
612 return -1;
613 }
615 gl.fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
617 glShaderSource(gl.fragment_shader, 1, &fragment_shader_source, NULL);
618 glCompileShader(gl.fragment_shader);
620 glGetShaderiv(gl.fragment_shader, GL_COMPILE_STATUS, &ret);
621 if (!ret) {
622 char *log;
624 printf("fragment shader compilation failed!:\n");
625 glGetShaderiv(gl.fragment_shader, GL_INFO_LOG_LENGTH, &ret);
627 if (ret > 1) {
628 log = malloc(ret);
629 glGetShaderInfoLog(gl.fragment_shader, ret, NULL, log);
630 printf("%s", log);
631 }
633 return -1;
634 }
636 gl.program = glCreateProgram();
638 glAttachShader(gl.program, gl.vertex_shader);
639 glAttachShader(gl.program, gl.fragment_shader);
641 glBindAttribLocation(gl.program, 0, "in_position");
642 glBindAttribLocation(gl.program, 1, "in_normal");
643 glBindAttribLocation(gl.program, 2, "in_color");
645 glLinkProgram(gl.program);
647 glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
648 if (!ret) {
649 char *log;
651 printf("program linking failed!:\n");
652 glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
654 if (ret > 1) {
655 log = malloc(ret);
656 glGetProgramInfoLog(gl.program, ret, NULL, log);
657 printf("%s", log);
658 }
660 return -1;
661 }
663 glUseProgram(gl.program);
665 gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
666 gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
667 gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
669 glViewport(0, 0, drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay);
670 glEnable(GL_CULL_FACE);
672 gl.positionsoffset = 0;
673 gl.colorsoffset = sizeof(vVertices);
674 gl.normalsoffset = sizeof(vVertices) + sizeof(vColors);
675 glGenBuffers(1, &gl.vbo);
676 glBindBuffer(GL_ARRAY_BUFFER, gl.vbo);
677 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
678 glBufferSubData(GL_ARRAY_BUFFER, gl.positionsoffset, sizeof(vVertices), &vVertices[0]);
679 glBufferSubData(GL_ARRAY_BUFFER, gl.colorsoffset, sizeof(vColors), &vColors[0]);
680 glBufferSubData(GL_ARRAY_BUFFER, gl.normalsoffset, sizeof(vNormals), &vNormals[0]);
681 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.positionsoffset);
682 glEnableVertexAttribArray(0);
683 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.normalsoffset);
684 glEnableVertexAttribArray(1);
685 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.colorsoffset);
686 glEnableVertexAttribArray(2);
688 return 0;
689 }
691 static void exit_gbm(void)
692 {
693 gbm_surface_destroy(gbm.surface);
694 gbm_device_destroy(gbm.dev);
695 return;
696 }
698 static void exit_gl(void)
699 {
700 glDeleteProgram(gl.program);
701 glDeleteBuffers(1, &gl.vbo);
702 glDeleteShader(gl.fragment_shader);
703 glDeleteShader(gl.vertex_shader);
704 eglDestroySurface(gl.display, gl.surface);
705 eglDestroyContext(gl.display, gl.context);
706 eglTerminate(gl.display);
707 return;
708 }
710 static void exit_drm(void)
711 {
713 drmModeRes *resources;
714 int i;
716 resources = (drmModeRes *)drm.resource_id;
717 for (i = 0; i < resources->count_connectors; i++) {
718 drmModeFreeEncoder(drm.encoder[i]);
719 drmModeFreeConnector(drm.connectors[i]);
720 }
721 drmModeFreeResources(drm.resource_id);
722 drmClose(drm.fd);
723 return;
724 }
726 void cleanup_kmscube(void)
727 {
728 exit_gl();
729 exit_gbm();
730 exit_drm();
731 printf("Cleanup of GL, GBM and DRM completed\n");
732 return;
733 }
735 static void draw(uint32_t i)
736 {
737 ESMatrix modelview;
739 /* clear the color buffer */
740 glClearColor(0.5, 0.5, 0.5, 1.0);
741 glClear(GL_COLOR_BUFFER_BIT);
743 esMatrixLoadIdentity(&modelview);
744 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
745 esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
746 esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
747 esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
749 GLfloat aspect = (GLfloat)(drm.mode[DISP_ID]->vdisplay) / (GLfloat)(drm.mode[DISP_ID]->hdisplay);
751 ESMatrix projection;
752 esMatrixLoadIdentity(&projection);
753 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
755 ESMatrix modelviewprojection;
756 esMatrixLoadIdentity(&modelviewprojection);
757 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
759 float normal[9];
760 normal[0] = modelview.m[0][0];
761 normal[1] = modelview.m[0][1];
762 normal[2] = modelview.m[0][2];
763 normal[3] = modelview.m[1][0];
764 normal[4] = modelview.m[1][1];
765 normal[5] = modelview.m[1][2];
766 normal[6] = modelview.m[2][0];
767 normal[7] = modelview.m[2][1];
768 normal[8] = modelview.m[2][2];
770 glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
771 glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
772 glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
774 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
775 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
776 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
777 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
778 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
779 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
780 }
782 static void
783 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
784 {
785 struct drm_fb *fb = data;
786 struct gbm_device *gbm = gbm_bo_get_device(bo);
788 if (fb->fb_id)
789 drmModeRmFB(drm.fd, fb->fb_id);
791 free(fb);
792 }
794 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
795 {
796 struct drm_fb *fb = gbm_bo_get_user_data(bo);
797 uint32_t width, height, format;
798 uint32_t bo_handles[4] = {0}, offsets[4] = {0}, pitches[4] = {0};
799 int ret;
801 if (fb)
802 return fb;
804 fb = calloc(1, sizeof *fb);
805 fb->bo = bo;
807 width = gbm_bo_get_width(bo);
808 height = gbm_bo_get_height(bo);
809 pitches[0] = gbm_bo_get_stride(bo);
810 bo_handles[0] = gbm_bo_get_handle(bo).u32;
811 format = gbm_bo_get_format(bo);
813 ret = drmModeAddFB2(drm.fd, width, height, format, bo_handles, pitches, offsets, &fb->fb_id, 0);
814 if (ret) {
815 printf("failed to create fb: %s\n", strerror(errno));
816 free(fb);
817 return NULL;
818 }
820 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
822 return fb;
823 }
825 static void page_flip_handler(int fd, unsigned int frame,
826 unsigned int sec, unsigned int usec, void *data)
827 {
828 int *waiting_for_flip = data;
829 *waiting_for_flip = 0;
830 }
832 void print_usage()
833 {
834 printf("Usage : kmscube <options>\n");
835 printf("\t-h : Help\n");
836 printf("\t-a : Enable all displays\n");
837 printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
838 printf("\t-n <number> (optional): Number of frames to render\n");
839 }
841 int kms_signalhandler(int signum)
842 {
843 switch(signum) {
844 case SIGINT:
845 case SIGTERM:
846 /* Allow the pending page flip requests to be completed before
847 * the teardown sequence */
848 sleep(1);
849 printf("Handling signal number = %d\n", signum);
850 cleanup_kmscube();
851 break;
852 default:
853 printf("Unknown signal\n");
854 break;
855 }
856 exit(1);
857 }
859 int main(int argc, char *argv[])
860 {
861 fd_set fds;
862 drmEventContext evctx = {
863 .version = DRM_EVENT_CONTEXT_VERSION,
864 .page_flip_handler = page_flip_handler,
865 };
866 struct gbm_bo *bo;
867 struct drm_fb *fb;
868 uint32_t i = 0;
869 int ret;
870 int opt;
871 int frame_count = -1;
873 signal(SIGINT, kms_signalhandler);
874 signal(SIGTERM, kms_signalhandler);
876 while ((opt = getopt(argc, argv, "ahc:n:")) != -1) {
877 switch(opt) {
878 case 'a':
879 all_display = 1;
880 break;
882 case 'h':
883 print_usage();
884 return 0;
886 case 'c':
887 connector_id = atoi(optarg);
888 break;
889 case 'n':
890 frame_count = atoi(optarg);
891 break;
894 default:
895 printf("Undefined option %s\n", argv[optind]);
896 print_usage();
897 return -1;
898 }
899 }
901 if (all_display) {
902 printf("### Enabling all displays\n");
903 connector_id = -1;
904 }
906 ret = init_drm();
907 if (ret) {
908 printf("failed to initialize DRM\n");
909 return ret;
910 }
911 printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",
912 drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,
913 drm.mode[DISP_ID]->vdisplay);
915 FD_ZERO(&fds);
916 FD_SET(drm.fd, &fds);
918 ret = init_gbm();
919 if (ret) {
920 printf("failed to initialize GBM\n");
921 return ret;
922 }
924 ret = init_gl();
925 if (ret) {
926 printf("failed to initialize EGL\n");
927 return ret;
928 }
930 /* clear the color buffer */
931 glClearColor(0.5, 0.5, 0.5, 1.0);
932 glClear(GL_COLOR_BUFFER_BIT);
933 eglSwapBuffers(gl.display, gl.surface);
934 bo = gbm_surface_lock_front_buffer(gbm.surface);
935 fb = drm_fb_get_from_bo(bo);
937 /* set mode: */
938 if (all_display) {
939 for (i=0; i<drm.ndisp; i++) {
940 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0,
941 &drm.connector_id[i], 1, drm.mode[i]);
942 if (ret) {
943 printf("display %d failed to set mode: %s\n", i, strerror(errno));
944 return ret;
945 }
946 }
947 } else {
948 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
949 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
950 if (ret) {
951 printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
952 return ret;
953 }
954 }
956 while (frame_count != 0) {
957 struct gbm_bo *next_bo;
958 int waiting_for_flip = 1;
960 draw(i++);
962 eglSwapBuffers(gl.display, gl.surface);
963 next_bo = gbm_surface_lock_front_buffer(gbm.surface);
964 fb = drm_fb_get_from_bo(next_bo);
966 /*
967 * Here you could also update drm plane layers if you want
968 * hw composition
969 */
971 ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
972 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
973 if (ret) {
974 printf("failed to queue page flip: %s\n", strerror(errno));
975 return -1;
976 }
978 while (waiting_for_flip) {
979 ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
980 if (ret < 0) {
981 printf("select err: %s\n", strerror(errno));
982 return ret;
983 } else if (ret == 0) {
984 printf("select timeout!\n");
985 return -1;
986 } else if (FD_ISSET(0, &fds)) {
987 continue;
988 }
989 drmHandleEvent(drm.fd, &evctx);
990 }
992 /* release last buffer to render on again: */
993 gbm_surface_release_buffer(gbm.surface, bo);
994 bo = next_bo;
996 if(frame_count >= 0)
997 frame_count--;
998 }
1000 cleanup_kmscube();
1001 printf("\n Exiting kmscube \n");
1003 return ret;
1004 }