Merge pull request #1 in PSDKLA/kmscube from ~X0038811/kmscube:bugfix/x0038811/kmscub...
[glsdk/kmscube.git] / kmscube.c
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         }
103 static bool search_plane_format(uint32_t desired_format, int formats_count, uint32_t* formats)
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;
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;
140 static bool set_drm_format(void)
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;
223 static int init_drm(void)
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;
374 static int init_gbm(void)
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;
390 static int init_gl(void)
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;
680 static void exit_gbm(void)
682         gbm_surface_destroy(gbm.surface);
683         gbm_device_destroy(gbm.dev);
684         return;
687 static void exit_gl(void)
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;
699 static void exit_drm(void)
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;
715 void cleanup_kmscube(void)
717         exit_gl();
718         exit_gbm();
719         exit_drm();
720         printf("Cleanup of GL, GBM and DRM completed\n");
721         return;
724 static void draw(uint32_t i)
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);
771 static void
772 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
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);
783 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
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;
814 static void page_flip_handler(int fd, unsigned int frame,
815                   unsigned int sec, unsigned int usec, void *data)
817         int *waiting_for_flip = data;
818         *waiting_for_flip = *waiting_for_flip - 1;
821 void print_usage()
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");
831 int kms_signalhandler(int signum)
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);
849 int main(int argc, char *argv[])
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;