display-kmscube: Render full video buffer
[glsdk/omapdrmtest.git] / util / display-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) 2012 Vincent StehlĂ© <v-stehle@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  *
25  * Based on kmscube application by Rob Clark, which was based on a egl cube
26  * test app originally written by Arvin Schnell.  And based on display-kms by
27  * Rob Clark.  And also based on xbmc eglImageExternal and
28  * TIRawVideoEGLImageHandle additions by Rob Clark.
29  *
30  * This display 'kmscube' draws a rotating 3d cube with EGL on kms display,
31  * with video mapped as a texture on the cube faces. This display has the
32  * particularity to have no framebuffer, per se, as we expect to render with
33  * EGL/GL ES, rather.
34  *
35  * TODO:
36  * - Remove static structs
37  * - Factorize kms code with display-kms
38  * - Enable from command line only; no auto detect at open like x11/kms
39  * - Cleanup display_kmscube options
40  * - Implement the "post" functions so that cube faces are updated
41  * - Do the necessary cleanup in the close function
42  * - Remove the extra level of structure inside display_kmscube
43  * - Cleanup commented out code
44  * - Revisit disp pointer in struct drm_fb
45  * - Better handling of rounding and alignment when allocating single planar
46  *   YUV buffers.
47  * - Remove unused vertex colors.
48  * - Fix warnings
49  * - Allow selecting the mode from CLI, like display-kms
50  * - Better handling of cropping
51  * - Interactive keyboard keys for FOV, distance... with OSD?
52  */
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
58 #include "util.h"
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <fcntl.h>
63 #include <string.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <errno.h>
68 #include <xf86drm.h>
69 #include <xf86drmMode.h>
70 #include <gbm.h>
71 #include <EGL/egl.h>
72 #include <EGL/eglext.h>
73 #include <GLES/gl.h>
74 #include <GLES/glext.h>
75 #include <pthread.h>
77 #include "esUtil.h"
79 #define MAX_FACES 6
81 typedef EGLImageKHR (eglCreateImageKHR_t)(EGLDisplay dpy, EGLContext ctx,
82                         EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
84 typedef EGLBoolean (eglDestroyImageKHR_t)(EGLDisplay dpy, EGLImageKHR image);
86 typedef void (glEGLImageTargetTexture2DOES_t)(GLenum target, GLeglImageOES image);
89 #define to_display_kmscube(x) container_of(x, struct display_kmscube, base)
90 struct display_kmscube {
91         struct display base;
92         uint32_t bo_flags,
93                 i;              // This is used to animate the cube.
95         // GL.
96         struct {
97                 EGLDisplay display;
98                 EGLConfig config;
99                 EGLContext context;
100                 EGLSurface surface;
101                 GLuint program;
102                 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix, uniform_texture;
103                 GLuint texture_name;
104                 eglCreateImageKHR_t *eglCreateImageKHR;
105                 eglDestroyImageKHR_t *eglDestroyImageKHR;
106                 glEGLImageTargetTexture2DOES_t *glEGLImageTargetTexture2DOES;
107                 float distance, fov;
108         } gl;
110         // GBM.
111         struct {
112                 struct gbm_device *dev;
113                 struct gbm_surface *surface;
114         } gbm;
116         // DRM.
117         struct {
118                 // Note: fd is in base display
119                 drmModeModeInfo *mode;
120                 uint32_t crtc_id;
121                 uint32_t connector_id;
122                 drmModePlaneRes *plane_resources;
123         } drm;
125         //user specified connector id
126         uint32_t user_connector_id;
128         int num_faces;
129         struct buffer *current_buffers[MAX_FACES];
130         pthread_t renderThread;
131         pthread_mutex_t lock[MAX_FACES];
132         pthread_mutex_t init_lock;
133 };
135 /* All our buffers are only vid buffers, and they all have an EGLImage. */
136 #define to_buffer_kmscube(x) container_of(x, struct buffer_kmscube, base)
137 struct buffer_kmscube {
138         struct buffer base;
139         uint32_t fb_id;
140         EGLImageKHR egl_img;
141         GLuint texture_name;
142         int face_no;
143 };
145 struct drm_fb {
146         struct gbm_bo *bo;
147         struct display_kmscube *disp_kmsc;
148         uint32_t fb_id;
149 };
151 static void *render_thread(void *data);
152 static int create_texture(struct display_kmscube *disp_kmsc, struct buffer *buf);
153 static int get_texture(struct display_kmscube *disp_kmsc, int face);
155 static int init_drm(struct display_kmscube *disp_kmsc)
157         drmModeRes *resources;
158         drmModeConnector *connector = NULL;
159         drmModeEncoder *encoder = NULL;
160         int i, area;
162         resources = drmModeGetResources(disp_kmsc->base.fd);
163         if (!resources) {
164                 ERROR("drmModeGetResources failed: %s", strerror(errno));
165                 return -1;
166         }
168         /* find a connected connector: */
169         for (i = 0; i < resources->count_connectors; i++) {
170                 connector = drmModeGetConnector(disp_kmsc->base.fd, resources->connectors[i]);
171                 if (connector->connection == DRM_MODE_CONNECTED && \
172                     connector->connector_id == disp_kmsc->user_connector_id) {
173                         /* it's connected, let's use this! */
174                         break;
175                 }
176                 drmModeFreeConnector(connector);
177                 connector = NULL;
178         }
180         if (!connector) {
181                 /* we could be fancy and listen for hotplug events and wait for
182                  * a connector..
183                  */
184                 ERROR("no connected connector!");
185                 return -1;
186         }
188         /* find highest resolution mode: */
189         for (i = 0, area = 0; i < connector->count_modes; i++) {
190                 drmModeModeInfo *current_mode = &connector->modes[i];
191                 int current_area = current_mode->hdisplay * current_mode->vdisplay;
192                 if (current_area > area) {
193                         disp_kmsc->drm.mode = current_mode;
194                         area = current_area;
195                 }
196         }
198         if (!disp_kmsc->drm.mode) {
199                 ERROR("could not find mode!");
200                 return -1;
201         }
203         /* find encoder: */
204         for (i = 0; i < resources->count_encoders; i++) {
205                 encoder = drmModeGetEncoder(disp_kmsc->base.fd, resources->encoders[i]);
206                 if (encoder->encoder_id == connector->encoder_id)
207                         break;
208                 drmModeFreeEncoder(encoder);
209                 encoder = NULL;
210         }
212         if (!encoder) {
213                 ERROR("no encoder!");
214                 return -1;
215         }
217         disp_kmsc->drm.crtc_id = encoder->crtc_id;
218         disp_kmsc->drm.connector_id = connector->connector_id;
219         printf("Chosen Connector ID = %d\n", disp_kmsc->drm.connector_id);
221         return 0;
224 static int init_gbm(struct display_kmscube *disp_kmsc)
226         disp_kmsc->gbm.dev = gbm_create_device(disp_kmsc->base.fd);
228         disp_kmsc->gbm.surface = gbm_surface_create(disp_kmsc->gbm.dev,
229                         disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay,
230                         GBM_FORMAT_XRGB8888,
231                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
232         if (!disp_kmsc->gbm.surface) {
233                 ERROR("failed to create gbm surface");
234                 return -1;
235         }
237         return 0;
240 static int init_gl(struct display_kmscube *disp_kmsc)
242         EGLint major, minor, n;
243         GLuint vertex_shader, fragment_shader;
244         GLint ret;
246         static const EGLint context_attribs[] = {
247                 EGL_CONTEXT_CLIENT_VERSION, 2,
248                 EGL_NONE
249         };
251         static const EGLint config_attribs[] = {
252                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
253                 EGL_RED_SIZE, 1,
254                 EGL_GREEN_SIZE, 1,
255                 EGL_BLUE_SIZE, 1,
256                 EGL_ALPHA_SIZE, 0,
257                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
258                 EGL_NONE
259         };
261         static const char *vertex_shader_source =
262                         "uniform mat4 modelviewMatrix;      \n"
263                         "uniform mat4 modelviewprojectionMatrix;\n"
264                         "uniform mat3 normalMatrix;         \n"
265                         "                                   \n"
266                         "attribute vec4 in_position;        \n"
267                         "attribute vec3 in_normal;          \n"
268                         "attribute vec4 in_color;           \n"
269                         "attribute vec2 in_texuv;           \n"
270                         "\n"
271                         "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
272                         "                                   \n"
273                         "varying float VaryingLight;        \n"
274                         "varying vec2 vVaryingTexUV;        \n"
275                         "                                   \n"
276                         "void main()                        \n"
277                         "{                                  \n"
278                         "    gl_Position = modelviewprojectionMatrix * in_position;\n"
279                         "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
280                         "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
281                         "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
282                         "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
283                         "    VaryingLight = max(0.0, dot(vEyeNormal, vLightDir));\n"
284                         "    vVaryingTexUV = in_texuv;      \n"
285                         "}                                  \n";
287         static const char *fragment_shader_source =
288                         "#extension GL_OES_EGL_image_external : require\n"
289                         "                                   \n"
290                         "precision mediump float;           \n"
291                         "                                   \n"
292                         "uniform samplerExternalOES texture;\n"
293                         "                                   \n"
294                         "varying float VaryingLight;        \n"
295                         "varying vec2 vVaryingTexUV;        \n"
296                         "                                   \n"
297                         "void main()                        \n"
298                         "{                                  \n"
299                         "    vec4 t = texture2D(texture, vVaryingTexUV);\n"
300                         "    gl_FragColor = vec4(VaryingLight * t.rgb, 1.0);\n"
301                         "}                                  \n";
303         disp_kmsc->gl.display = eglGetDisplay(disp_kmsc->gbm.dev);
305         if (!eglInitialize(disp_kmsc->gl.display, &major, &minor)) {
306                 ERROR("failed to initialize");
307                 return -1;
308         }
310         printf("Using display %p with EGL version %d.%d\n",
311                         disp_kmsc->gl.display, major, minor);
313         printf("EGL Version \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VERSION));
314         printf("EGL Vendor \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VENDOR));
315         printf("EGL Extensions \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_EXTENSIONS));
317         if (!eglBindAPI(EGL_OPENGL_ES_API)) {
318                 ERROR("failed to bind api EGL_OPENGL_ES_API");
319                 return -1;
320         }
322         if (!eglChooseConfig(disp_kmsc->gl.display, config_attribs, &disp_kmsc->gl.config, 1, &n) || n != 1) {
323                 ERROR("failed to choose config: %d", n);
324                 return -1;
325         }
327         disp_kmsc->gl.context = eglCreateContext(disp_kmsc->gl.display, disp_kmsc->gl.config,
328                         EGL_NO_CONTEXT, context_attribs);
329         if (disp_kmsc->gl.context == NULL) {
330                 ERROR("failed to create context");
331                 return -1;
332         }
334         disp_kmsc->gl.surface = eglCreateWindowSurface(disp_kmsc->gl.display,
335                 disp_kmsc->gl.config, disp_kmsc->gbm.surface, NULL);
336         if (disp_kmsc->gl.surface == EGL_NO_SURFACE) {
337                 ERROR("failed to create egl surface");
338                 return -1;
339         }
341         /* connect the context to the surface */
342         eglMakeCurrent(disp_kmsc->gl.display, disp_kmsc->gl.surface,
343                         disp_kmsc->gl.surface, disp_kmsc->gl.context);
345         // EGL Image.
346         if (!(disp_kmsc->gl.eglCreateImageKHR = eglGetProcAddress("eglCreateImageKHR"))) {
347                 ERROR("No eglCreateImageKHR?!");
348                 return -1;
349         }
351         if (!(disp_kmsc->gl.eglDestroyImageKHR = eglGetProcAddress("eglDestroyImageKHR"))) {
352                 ERROR("No eglDestroyImageKHR?!");
353                 return -1;
354         }
356         if (!(disp_kmsc->gl.glEGLImageTargetTexture2DOES = eglGetProcAddress("glEGLImageTargetTexture2DOES"))) {
357                 ERROR("No glEGLImageTargetTexture2DOES?!");
358                 return -1;
359         }
361         const char *exts = glGetString(GL_EXTENSIONS);
362         printf("GL Extensions \"%s\"\n", exts);
364         if (!strstr(exts, "GL_TI_image_external_raw_video")) {
365                 ERROR("No GL_TI_image_external_raw_video extension?!");
366                 return -1;
367         }
369         vertex_shader = glCreateShader(GL_VERTEX_SHADER);
371         glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
372         glCompileShader(vertex_shader);
374         glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
375         if (!ret) {
376                 char *log;
378                 ERROR("vertex shader compilation failed!:");
379                 glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
380                 if (ret > 1) {
381                         log = malloc(ret);
382                         glGetShaderInfoLog(vertex_shader, ret, NULL, log);
383                         ERROR("%s", log);
384                 }
386                 return -1;
387         }
389         fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
391         glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
392         glCompileShader(fragment_shader);
394         glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
395         if (!ret) {
396                 char *log;
398                 ERROR("fragment shader compilation failed!:");
399                 glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
401                 if (ret > 1) {
402                         log = malloc(ret);
403                         glGetShaderInfoLog(fragment_shader, ret, NULL, log);
404                         ERROR("%s", log);
405                 }
407                 return -1;
408         }
410         disp_kmsc->gl.program = glCreateProgram();
412         glAttachShader(disp_kmsc->gl.program, vertex_shader);
413         glAttachShader(disp_kmsc->gl.program, fragment_shader);
415         glBindAttribLocation(disp_kmsc->gl.program, 0, "in_position");
416         glBindAttribLocation(disp_kmsc->gl.program, 1, "in_normal");
417         glBindAttribLocation(disp_kmsc->gl.program, 2, "in_color");
418         glBindAttribLocation(disp_kmsc->gl.program, 3, "in_texuv");
420         glLinkProgram(disp_kmsc->gl.program);
422         glGetProgramiv(disp_kmsc->gl.program, GL_LINK_STATUS, &ret);
423         if (!ret) {
424                 char *log;
426                 ERROR("program linking failed!:");
427                 glGetProgramiv(disp_kmsc->gl.program, GL_INFO_LOG_LENGTH, &ret);
429                 if (ret > 1) {
430                         log = malloc(ret);
431                         glGetProgramInfoLog(disp_kmsc->gl.program, ret, NULL, log);
432                         ERROR("%s", log);
433                 }
435                 return -1;
436         }
438         glUseProgram(disp_kmsc->gl.program);
440         disp_kmsc->gl.modelviewmatrix = glGetUniformLocation(disp_kmsc->gl.program, "modelviewMatrix");
441         disp_kmsc->gl.modelviewprojectionmatrix =
442                 glGetUniformLocation(disp_kmsc->gl.program, "modelviewprojectionMatrix");
443         disp_kmsc->gl.normalmatrix = glGetUniformLocation(disp_kmsc->gl.program, "normalMatrix");
444         disp_kmsc->gl.uniform_texture = glGetUniformLocation(disp_kmsc->gl.program, "uniform_texture");
446         glViewport(0, 0, disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay);
448         /* Textures would be created at runtime by render thread */
450         return 0;
453 static void draw(struct display_kmscube *disp_kmsc)
455         int face, tex_name;
456         ESMatrix modelview;
457         static const GLfloat vVertices[] = {
458                         // front
459                         -1.0f, -1.0f, +1.0f, // point blue
460                         +1.0f, -1.0f, +1.0f, // point magenta
461                         -1.0f, +1.0f, +1.0f, // point cyan
462                         +1.0f, +1.0f, +1.0f, // point white
463                         // back
464                         +1.0f, -1.0f, -1.0f, // point red
465                         -1.0f, -1.0f, -1.0f, // point black
466                         +1.0f, +1.0f, -1.0f, // point yellow
467                         -1.0f, +1.0f, -1.0f, // point green
468                         // right
469                         +1.0f, -1.0f, +1.0f, // point magenta
470                         +1.0f, -1.0f, -1.0f, // point red
471                         +1.0f, +1.0f, +1.0f, // point white
472                         +1.0f, +1.0f, -1.0f, // point yellow
473                         // left
474                         -1.0f, -1.0f, -1.0f, // point black
475                         -1.0f, -1.0f, +1.0f, // point blue
476                         -1.0f, +1.0f, -1.0f, // point green
477                         -1.0f, +1.0f, +1.0f, // point cyan
478                         // top
479                         -1.0f, +1.0f, +1.0f, // point cyan
480                         +1.0f, +1.0f, +1.0f, // point white
481                         -1.0f, +1.0f, -1.0f, // point green
482                         +1.0f, +1.0f, -1.0f, // point yellow
483                         // bottom
484                         -1.0f, -1.0f, -1.0f, // point black
485                         +1.0f, -1.0f, -1.0f, // point red
486                         -1.0f, -1.0f, +1.0f, // point blue
487                         +1.0f, -1.0f, +1.0f  // point magenta
488         };
490         static const GLfloat vColors[] = {
491                         // front
492                         0.0f,  0.0f,  1.0f, // blue
493                         1.0f,  0.0f,  1.0f, // magenta
494                         0.0f,  1.0f,  1.0f, // cyan
495                         1.0f,  1.0f,  1.0f, // white
496                         // back
497                         1.0f,  0.0f,  0.0f, // red
498                         0.0f,  0.0f,  0.0f, // black
499                         1.0f,  1.0f,  0.0f, // yellow
500                         0.0f,  1.0f,  0.0f, // green
501                         // right
502                         1.0f,  0.0f,  1.0f, // magenta
503                         1.0f,  0.0f,  0.0f, // red
504                         1.0f,  1.0f,  1.0f, // white
505                         1.0f,  1.0f,  0.0f, // yellow
506                         // left
507                         0.0f,  0.0f,  0.0f, // black
508                         0.0f,  0.0f,  1.0f, // blue
509                         0.0f,  1.0f,  0.0f, // green
510                         0.0f,  1.0f,  1.0f, // cyan
511                         // top
512                         0.0f,  1.0f,  1.0f, // cyan
513                         1.0f,  1.0f,  1.0f, // white
514                         0.0f,  1.0f,  0.0f, // green
515                         1.0f,  1.0f,  0.0f, // yellow
516                         // bottom
517                         0.0f,  0.0f,  0.0f, // black
518                         1.0f,  0.0f,  0.0f, // red
519                         0.0f,  0.0f,  1.0f, // blue
520                         1.0f,  0.0f,  1.0f  // magenta
521         };
523         static const GLfloat vNormals[] = {
524                         // front
525                         +0.0f, +0.0f, +1.0f, // forward
526                         +0.0f, +0.0f, +1.0f, // forward
527                         +0.0f, +0.0f, +1.0f, // forward
528                         +0.0f, +0.0f, +1.0f, // forward
529                         // back
530                         +0.0f, +0.0f, -1.0f, // backbard
531                         +0.0f, +0.0f, -1.0f, // backbard
532                         +0.0f, +0.0f, -1.0f, // backbard
533                         +0.0f, +0.0f, -1.0f, // backbard
534                         // right
535                         +1.0f, +0.0f, +0.0f, // right
536                         +1.0f, +0.0f, +0.0f, // right
537                         +1.0f, +0.0f, +0.0f, // right
538                         +1.0f, +0.0f, +0.0f, // right
539                         // left
540                         -1.0f, +0.0f, +0.0f, // left
541                         -1.0f, +0.0f, +0.0f, // left
542                         -1.0f, +0.0f, +0.0f, // left
543                         -1.0f, +0.0f, +0.0f, // left
544                         // top
545                         +0.0f, +1.0f, +0.0f, // up
546                         +0.0f, +1.0f, +0.0f, // up
547                         +0.0f, +1.0f, +0.0f, // up
548                         +0.0f, +1.0f, +0.0f, // up
549                         // bottom
550                         +0.0f, -1.0f, +0.0f, // down
551                         +0.0f, -1.0f, +0.0f, // down
552                         +0.0f, -1.0f, +0.0f, // down
553                         +0.0f, -1.0f, +0.0f  // down
554         };
556         static const GLfloat vTexUVs[] = {
557                         // front
558                         0.0f,  1.0f,
559                         1.0f,  1.0f,
560                         0.0f,  0.0f,
561                         1.0f,  0.0f,
562                         // back
563                         0.0f,  1.0f,
564                         1.0f,  1.0f,
565                         0.0f,  0.0f,
566                         1.0f,  0.0f,
567                         // right
568                         0.0f,  1.0f,
569                         1.0f,  1.0f,
570                         0.0f,  0.0f,
571                         1.0f,  0.0f,
572                         // left
573                         0.0f,  1.0f,
574                         1.0f,  1.0f,
575                         0.0f,  0.0f,
576                         1.0f,  0.0f,
577                         // top
578                         0.0f,  1.0f,
579                         1.0f,  1.0f,
580                         0.0f,  0.0f,
581                         1.0f,  0.0f,
582                         // bottom
583                         0.0f,  1.0f,
584                         1.0f,  1.0f,
585                         0.0f,  0.0f,
586                         1.0f,  0.0f,
587         };
589         /* clear the color buffer */
590         glClearColor(0.5, 0.5, 0.5, 1.0);
591         glClear(GL_COLOR_BUFFER_BIT);
593         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
594         glEnableVertexAttribArray(0);
596         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vNormals);
597         glEnableVertexAttribArray(1);
599         glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, vColors);
600         glEnableVertexAttribArray(2);
602         glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, vTexUVs);
603         glEnableVertexAttribArray(3);
605         esMatrixLoadIdentity(&modelview);
606         esTranslate(&modelview, 0.0f, 0.0f, -disp_kmsc->gl.distance);
607         esRotate(&modelview, 45.0f + (0.25f * disp_kmsc->i), 1.0f, 0.0f, 0.0f);
608         esRotate(&modelview, 45.0f - (0.5f * disp_kmsc->i), 0.0f, 1.0f, 0.0f);
609         esRotate(&modelview, 10.0f + (0.15f * disp_kmsc->i), 0.0f, 0.0f, 1.0f);
611         GLfloat aspect = (GLfloat)(disp_kmsc->drm.mode->hdisplay) / (GLfloat)(disp_kmsc->drm.mode->vdisplay);
613         ESMatrix projection;
614         esMatrixLoadIdentity(&projection);
615         esPerspective(&projection, disp_kmsc->gl.fov, aspect, 1.0f, 10.0f);
617         ESMatrix modelviewprojection;
618         esMatrixLoadIdentity(&modelviewprojection);
619         esMatrixMultiply(&modelviewprojection, &modelview, &projection);
621         float normal[9];
622         normal[0] = modelview.m[0][0];
623         normal[1] = modelview.m[0][1];
624         normal[2] = modelview.m[0][2];
625         normal[3] = modelview.m[1][0];
626         normal[4] = modelview.m[1][1];
627         normal[5] = modelview.m[1][2];
628         normal[6] = modelview.m[2][0];
629         normal[7] = modelview.m[2][1];
630         normal[8] = modelview.m[2][2];
632         glUniformMatrix4fv(disp_kmsc->gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
633         glUniformMatrix4fv(disp_kmsc->gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
634         glUniformMatrix3fv(disp_kmsc->gl.normalmatrix, 1, GL_FALSE, normal);
636         glEnable(GL_CULL_FACE);
638         for(face=0; face<MAX_FACES; face++) {
639                 glActiveTexture(GL_TEXTURE0);
640                 tex_name = get_texture(disp_kmsc, face);
641                 glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex_name);
642                 if (glGetError() != GL_NO_ERROR)
643                         printf("glBindTexture failed render for face %d!\n", face);
644                 glUniform1i(disp_kmsc->gl.uniform_texture, 0);
645                 glDrawArrays(GL_TRIANGLE_STRIP, face * 4, 4);
646         }
649 static void
650 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
652         struct drm_fb *fb = data;
653 //      struct gbm_device *gbm = gbm_bo_get_device(bo);
654         struct display_kmscube *disp_kmsc = fb->disp_kmsc;
656         if (fb->fb_id)
657                 drmModeRmFB(disp_kmsc->base.fd, fb->fb_id);
659         free(fb);
662 static struct drm_fb * drm_fb_get_from_bo(struct display_kmscube *disp_kmsc, struct gbm_bo *bo)
664         struct drm_fb *fb = gbm_bo_get_user_data(bo);
665         uint32_t width, height, stride, handle;
666         int ret;
668         if (fb)
669                 return fb;
671         fb = calloc(1, sizeof(*fb));
672         fb->bo = bo;
673         fb->disp_kmsc = disp_kmsc;
675         width = gbm_bo_get_width(bo);
676         height = gbm_bo_get_height(bo);
677         stride = gbm_bo_get_stride(bo);
678         handle = gbm_bo_get_handle(bo).u32;
680         ret = drmModeAddFB(disp_kmsc->base.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
681         if (ret) {
682                 ERROR("failed to create fb: %s", strerror(errno));
683                 free(fb);
684                 return NULL;
685         }
687         gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
689         return fb;
692 static void page_flip_handler(int fd, unsigned int frame,
693                   unsigned int sec, unsigned int usec, void *data)
695         int *waiting_for_flip = data;
696         *waiting_for_flip = 0;
699 static struct omap_bo *
700 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
701                 uint32_t *bo_handle, uint32_t *pitch)
703         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
704         struct omap_bo *bo;
705         uint32_t bo_flags = disp_kmsc->bo_flags;
707         if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
708                 bo_flags &= ~OMAP_BO_TILED;
709                 if (bpp == 8) {
710                         bo_flags |= OMAP_BO_TILED_8;
711                 } else if (bpp == 16) {
712                         bo_flags |= OMAP_BO_TILED_16;
713                 } else if (bpp == 32) {
714                         bo_flags |= OMAP_BO_TILED_32;
715                 }
716         }
718         bo_flags |= OMAP_BO_WC;
720         if (bo_flags & OMAP_BO_TILED) {
721                 bo = omap_bo_new_tiled(disp->dev, width, height, bo_flags);
722         } else {
723                 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
724         }
726         if (bo) {
727                 *bo_handle = omap_bo_handle(bo);
728                 *pitch = width * bpp / 8;
729                 if (bo_flags & OMAP_BO_TILED)
730                         *pitch = ALIGN2(*pitch, PAGE_SHIFT);
731         }
733         return bo;
736 /* We allocate single planar buffers, always. This, for EGLImage. Also, we
737  * create on EGLImageKHR per buffer. */
738 static struct buffer *
739 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
741         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
742         struct buffer_kmscube *buf_kmsc;
743         struct buffer *buf;
744         uint32_t bo_handles[4] = {0}, offsets[4] = {0};
746         buf_kmsc = calloc(1, sizeof(*buf_kmsc));
747         if (!buf_kmsc) {
748                 ERROR("allocation failed");
749                 return NULL;
750         }
751         buf = &buf_kmsc->base;
753         buf->fourcc = fourcc;
754         buf->width = w;
755         buf->height = h;
756         buf->multiplanar = true;
758         buf->nbo = 1;
760         if (!fourcc)
761                 fourcc = FOURCC('A','R','2','4');
763         switch(fourcc) {
764         case FOURCC('A','R','2','4'):
765                 buf->nbo = 1;
766                 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
767                                 &bo_handles[0], &buf->pitches[0]);
768                 break;
769         case FOURCC('U','Y','V','Y'):
770         case FOURCC('Y','U','Y','V'):
771                 buf->nbo = 1;
772                 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
773                                 &bo_handles[0], &buf->pitches[0]);
774                 break;
775         case FOURCC('N','V','1','2'):
776                 if (disp->multiplanar) {
777                         buf->nbo = 2;
778                         buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
779                                         &bo_handles[0], &buf->pitches[0]);
780                         buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
781                         buf->bo[1] = alloc_bo(disp, 16, buf->width/2, buf->height/2,
782                                         &bo_handles[1], &buf->pitches[1]);
783                         buf->fd[1] = omap_bo_dmabuf(buf->bo[1]);
784                 } else {
785                         buf->nbo = 1;
786                         buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
787                                         &bo_handles[0], &buf->pitches[0]);
788                         buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
789                         bo_handles[1] = bo_handles[0];
790                         buf->pitches[1] = buf->pitches[0];
791                         offsets[1] = buf->width * buf->height;
792                         buf->multiplanar = false;
793                 }
794                 break;
795         case FOURCC('I','4','2','0'):
796                 buf->nbo = 1;
797                 buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
798                                 &bo_handles[0], &buf->pitches[0]);
799                 break;
800         default:
801                 ERROR("invalid format: 0x%08x", fourcc);
802                 goto fail;
803         }
805         buf_kmsc->face_no = disp_kmsc->num_faces;
806         /*
807          * EGL image for this buffer would be created by the render thread
808          * Nothing to be done here
809          */
810         return buf;
812 fail:
813         // XXX cleanup
814         return NULL;
817 static struct buffer **
818 alloc_buffers(struct display *disp, uint32_t n,
819                 uint32_t fourcc, uint32_t w, uint32_t h)
821         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
822         struct buffer **bufs;
823         uint32_t i = 0;
825         bufs = calloc(n, sizeof(*bufs));
826         if (!bufs) {
827                 ERROR("allocation failed");
828                 goto fail;
829         }
831         for (i = 0; i < n; i++) {
832                 bufs[i] = alloc_buffer(disp, fourcc, w, h);
833                 if (!bufs[i]) {
834                         ERROR("allocation failed");
835                         goto fail;
836                 }
837         }
839         /*
840          * All buffers allocated with one call to get_vid_buffers are marked
841          * with the same face number
842          */
843         disp_kmsc->num_faces++;
844         disp->buf = bufs;
845         return bufs;
847 fail:
848         // XXX cleanup
849         return NULL;
853 static void
854 free_buffers(struct display *disp, uint32_t n)
856         uint32_t i;
857         for (i = 0; i < n; i++) {
858                 if (disp->buf[i]) {
859                         close(disp->buf[i]->fd[0]);
860                         omap_bo_del(disp->buf[i]->bo[0]);
861                         if(disp->multiplanar){
862                                 close(disp->buf[i]->fd[1]);
863                                 omap_bo_del(disp->buf[i]->bo[1]);
864                         }
865                 }
866         }
867         free(disp->buf);
871 static struct buffer **
872 get_buffers(struct display *disp, uint32_t n)
874         MSG("get_buffers not supported!");
875         return NULL;
878 static struct buffer **
879 get_vid_buffers(struct display *disp, uint32_t n,
880                 uint32_t fourcc, uint32_t w, uint32_t h)
882         return alloc_buffers(disp, n, fourcc, w, h);
885 static int
886 post_buffer(struct display *disp, struct buffer *buf)
888         ERROR("post_buffer not supported!");
889         return -1;
892 static int
893 post_vid_buffer(struct display *disp, struct buffer *buf,
894                 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
896         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
897         struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
898         int face;
900         face = buf_kmsc->face_no;
901         /*
902          * No gl_ calls here, just update the buffer to be used for the
903          * the specific face to which this buffer belongs
904          */
906         pthread_mutex_lock(&disp_kmsc->lock[face]);
907         disp_kmsc->current_buffers[face] = buf;
908         pthread_mutex_unlock(&disp_kmsc->lock[face]);
910         return 0;
913 static void *
914 render_thread(void *data)
916         struct display_kmscube *disp_kmsc = to_display_kmscube(data);
918         fd_set fds;
919         drmEventContext evctx = {
920                         .version = DRM_EVENT_CONTEXT_VERSION,
921                         .page_flip_handler = page_flip_handler,
922         };
923         struct gbm_bo *bo;
924         struct drm_fb *fb;
925         int ret, face;
926         struct gbm_bo *next_bo;
927         int waiting_for_flip = 1;
929         if (init_gbm(disp_kmsc)) {
930                 ERROR("couldn't init gbm");
931                 return NULL;
932         }
933         if (init_gl(disp_kmsc)) {
934                 ERROR("couldn't init gl(es)");
935                 return NULL;
936         }
938         /* clear the color buffer */
939         glClearColor(0.5, 0.5, 0.5, 1.0);
940         glClear(GL_COLOR_BUFFER_BIT);
941         eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
942         bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
943         fb = drm_fb_get_from_bo(disp_kmsc, bo);
945         /* set mode: */
946         ret = drmModeSetCrtc(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id, 0, 0,
947                         &disp_kmsc->drm.connector_id, 1, disp_kmsc->drm.mode);
948         if (ret) {
949                 ERROR("failed to set mode: %s\n", strerror(errno));
950                 return NULL;
951         }
953         pthread_mutex_unlock(&disp_kmsc->init_lock);
955         while(1) {
957                 for(face = 0; face < MAX_FACES; face++) {
958                         pthread_mutex_lock(&disp_kmsc->lock[face]);
959                 }
961                 FD_ZERO(&fds);
962                 FD_SET(disp_kmsc->base.fd, &fds);
964                 // Draw cube.
965                 draw(disp_kmsc);
966                 (disp_kmsc->i)++;
968                 eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
969                 next_bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
970                 fb = drm_fb_get_from_bo(disp_kmsc, next_bo);
972                 for(face = 0; face < MAX_FACES; face++) {
973                         pthread_mutex_unlock(&disp_kmsc->lock[face]);
974                 }
976                 /*
977                  * Here you could also update drm plane layers if you want
978                  * hw composition
979                  */
981                 ret = drmModePageFlip(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id,
982                                 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
983                 if (ret) {
984                         ERROR("failed to queue page flip: %s\n", strerror(errno));
985                         return NULL;
986                 }
988                 while (waiting_for_flip) {
989                         ret = select(disp_kmsc->base.fd + 1, &fds, NULL, NULL, NULL);
990                         if (ret < 0) {
991                                 ERROR("select err: %s\n", strerror(errno));
992                                 return NULL;
993                         } else if (ret == 0) {
994                                 ERROR("select timeout!\n");
995                                 return NULL;
996                         } else if (FD_ISSET(0, &fds)) {
997                                 ERROR("user interrupted!\n");
998                                 break;
999                         }
1000                         drmHandleEvent(disp_kmsc->base.fd, &evctx);
1001                 }
1003                 /* release last buffer to render on again: */
1004                 gbm_surface_release_buffer(disp_kmsc->gbm.surface, bo);
1005                 bo = next_bo;
1007                 waiting_for_flip = 1;
1009         }
1010         return data;
1013 static int
1014 get_texture(struct display_kmscube *disp_kmsc, int face)
1017         struct buffer *buf;
1018         struct buffer_kmscube *buf_kmsc;
1019         int num_faces;
1021         num_faces = disp_kmsc->num_faces ? disp_kmsc->num_faces : 1;
1022         face = face % num_faces;
1023         buf = disp_kmsc->current_buffers[face];
1025         if(buf == NULL) {
1026                 return 0;
1027         }
1029         buf_kmsc = to_buffer_kmscube(buf);
1030         if(buf_kmsc->texture_name == 0) {
1031                 create_texture(disp_kmsc, buf);
1032         }
1034         return buf_kmsc->texture_name;
1037 static int
1038 create_texture(struct display_kmscube *disp_kmsc, struct buffer *buf)
1041         struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
1042         // Create EGLImage and return.
1043         // TODO: cropping attributes when this will be supported.
1044         EGLint attr[] = {
1045             EGL_GL_VIDEO_FOURCC_TI,      buf->fourcc,
1046             EGL_GL_VIDEO_WIDTH_TI,       buf->width,
1047             EGL_GL_VIDEO_HEIGHT_TI,      buf->height,
1048             EGL_GL_VIDEO_BYTE_SIZE_TI,   omap_bo_size(buf->bo[0]),
1049             EGL_GL_VIDEO_BYTE_STRIDE_TI,   buf->pitches[0],
1050             // TODO: pick proper YUV flags..
1051             EGL_GL_VIDEO_YUV_FLAGS_TI,   EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE | EGLIMAGE_FLAGS_YUV_BT601,
1052             EGL_NONE
1053         };
1055         int fd = omap_bo_dmabuf(buf->bo[0]);
1057         buf_kmsc->egl_img =
1058         disp_kmsc->gl.eglCreateImageKHR(disp_kmsc->gl.display, EGL_NO_CONTEXT,
1059                                         EGL_RAW_VIDEO_TI_DMABUF, (EGLClientBuffer)fd, attr);
1061         if (buf_kmsc->egl_img == EGL_NO_IMAGE_KHR) {
1062                 ERROR("eglCreateImageKHR failed!\n");
1063                 goto fail;
1064         }
1066         // Texture.
1067         glGenTextures(1, &buf_kmsc->texture_name);
1068         glBindTexture(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->texture_name);
1070         if (glGetError() != GL_NO_ERROR) {
1071                 ERROR("glBindTexture!");
1072                 goto fail;
1073         }
1075         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1076         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1078         // Update video texture / EGL Image.
1079         disp_kmsc->gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->egl_img);
1081         if (glGetError() != GL_NO_ERROR) {
1082                 ERROR("glEGLImageTargetTexture2DOES!\n");
1083                 goto fail;
1084         }
1085         return 0;
1087 fail:
1088         return -1;
1091 static void
1092 close_kmscube(struct display *disp)
1096 void
1097 disp_kmscube_usage(void)
1099         MSG("KMSCUBE Display Options:");
1100         MSG("\t--distance <float>\tset cube distance (default 8.0)");
1101         MSG("\t--fov <float>\tset field of vision (default 45.0)");
1102         MSG("\t--kmscube\tEnable display kmscube (default: disabled)");
1103         MSG("\t--connector <connector_id>\tset the connector ID (default: LCD)");
1106 struct display *
1107 disp_kmscube_open(int argc, char **argv)
1109         struct display_kmscube *disp_kmsc = NULL;
1110         struct display *disp;
1111         int ret, i, enabled = 0;
1112         float fov = 45, distance = 8, connector_id = 4;
1114         /* note: set args to NULL after we've parsed them so other modules know
1115          * that it is already parsed (since the arg parsing is decentralized)
1116          */
1117         for (i = 1; i < argc; i++) {
1118                 if (!argv[i]) {
1119                         continue;
1120                 }
1121                 if (!strcmp("--distance", argv[i])) {
1122                         argv[i++] = NULL;
1123                         if (sscanf(argv[i], "%f", &distance) != 1) {
1124                                 ERROR("invalid arg: %s", argv[i]);
1125                                 goto fail;
1126                         }
1127                 } else if (!strcmp("--fov", argv[i])) {
1128                         argv[i++] = NULL;
1129                         if (sscanf(argv[i], "%f", &fov) != 1) {
1130                                 ERROR("invalid arg: %s", argv[i]);
1131                                 goto fail;
1132                         }
1133                 } else if (!strcmp("--connector", argv[i])) {
1134                         argv[i++] = NULL;
1135                         if (sscanf(argv[i], "%f", &connector_id) != 1) {
1136                                 ERROR("invalid arg: %s", argv[i]);
1137                                 goto fail;
1138                         }
1139                 } else if (!strcmp("--kmscube", argv[i])) {
1140                         enabled = 1;
1141                 } else {
1142                         /* ignore */
1143                         continue;
1144                 }
1145                 argv[i] = NULL;
1146         }
1148         // If not explicitely enabled from command line, fail (and fallback to disp-kms).
1149         if (!enabled)
1150                 goto fail;
1152         disp_kmsc = calloc(1, sizeof(*disp_kmsc));
1153         if (!disp_kmsc) {
1154                 ERROR("allocation failed");
1155                 goto fail;
1156         }
1157         disp_kmsc->gl.distance = distance;
1158         disp_kmsc->gl.fov = fov;
1159         disp_kmsc->user_connector_id = connector_id;
1160         disp = &disp_kmsc->base;
1162         for (i=0; i<MAX_FACES; i++) {
1163                 pthread_mutex_init(&disp_kmsc->lock[i], NULL);
1164         }
1165         pthread_mutex_init(&disp_kmsc->init_lock, NULL);
1167         disp->fd = drmOpen("omapdrm", NULL);
1168         if (disp->fd < 0) {
1169                 ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
1170                 goto fail;
1171         }
1173         disp->dev = omap_device_new(disp->fd);
1174         if (!disp->dev) {
1175                 ERROR("couldn't create device");
1176                 goto fail;
1177         }
1179         disp->get_buffers = get_buffers;
1180         disp->get_vid_buffers = get_vid_buffers;
1181         disp->post_buffer = post_buffer;
1182         disp->post_vid_buffer = post_vid_buffer;
1183         disp->close = close_kmscube;
1184         disp->disp_free_buf = free_buffers;
1187         if (init_drm(disp_kmsc)) {
1188                 ERROR("couldn't init drm");
1189                 goto fail;
1190         }
1192         disp_kmsc->drm.plane_resources = drmModeGetPlaneResources(disp->fd);
1193         if (!disp_kmsc->drm.plane_resources) {
1194                 ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
1195                 goto fail;
1196         }
1198         disp->width = 0;
1199         disp->height = 0;
1200         disp->multiplanar = false;
1202         /*
1203          * Initialize everything from a render thread.
1204          */
1205         pthread_mutex_lock(&disp_kmsc->init_lock);
1207         ret = pthread_create(&disp_kmsc->renderThread, NULL,
1208                         render_thread, disp);
1209         if(ret) {
1210                 ERROR("Failed creating Render Thread: %s", strerror(errno));
1211         }
1213         /*
1214          * Wait till initialization is complete from render thread
1215          */
1216         pthread_mutex_lock(&disp_kmsc->init_lock);
1217         MSG("Display initialized, Render thread created");
1218         pthread_mutex_unlock(&disp_kmsc->init_lock);
1220         return disp;
1222 fail:
1223         // XXX cleanup
1224         return NULL;