Addition of new demo "kmscube with video"
[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>
76 #include "esUtil.h"
78 #ifndef EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE
79 // XXX these should come from some egl header??
80 #define EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE (0 << 0)
81 #define EGLIMAGE_FLAGS_YUV_FULL_RANGE       (1 << 0)
82 #define EGLIMAGE_FLAGS_YUV_BT601            (0 << 1)
83 #define EGLIMAGE_FLAGS_YUV_BT709            (1 << 1)
84 #endif
85 #ifndef EGL_TI_raw_video
86 #  define EGL_TI_raw_video 1
87 #  define EGL_RAW_VIDEO_TI            0x333A  /* eglCreateImageKHR target */
88 #  define EGL_RAW_VIDEO_TI2           0x333B  /* eglCreateImageKHR target */
89 #  define EGL_GL_VIDEO_FOURCC_TI        0x3331  /* eglCreateImageKHR attribute */
90 #  define EGL_GL_VIDEO_WIDTH_TI         0x3332  /* eglCreateImageKHR attribute */
91 #  define EGL_GL_VIDEO_HEIGHT_TI        0x3333  /* eglCreateImageKHR attribute */
92 #  define EGL_GL_VIDEO_BYTE_STRIDE_TI     0x3334  /* eglCreateImageKHR attribute */
93 #  define EGL_GL_VIDEO_BYTE_SIZE_TI       0x3335  /* eglCreateImageKHR attribute */
94 #  define EGL_GL_VIDEO_YUV_FLAGS_TI       0x3336  /* eglCreateImageKHR attribute */
95 #endif
97 typedef EGLImageKHR (eglCreateImageKHR_t)(EGLDisplay dpy, EGLContext ctx,
98                         EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
100 typedef EGLBoolean (eglDestroyImageKHR_t)(EGLDisplay dpy, EGLImageKHR image);
102 typedef void (glEGLImageTargetTexture2DOES_t)(GLenum target, GLeglImageOES image);
105 #define to_display_kmscube(x) container_of(x, struct display_kmscube, base)
106 struct display_kmscube {
107         struct display base;
108         uint32_t bo_flags,
109                 i;              // This is used to animate the cube.
111         // GL.
112         struct {
113                 EGLDisplay display;
114                 EGLConfig config;
115                 EGLContext context;
116                 EGLSurface surface;
117                 GLuint program;
118                 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix, uniform_texture;
119                 GLuint texture_name;
120                 eglCreateImageKHR_t *eglCreateImageKHR;
121                 eglDestroyImageKHR_t *eglDestroyImageKHR;
122                 glEGLImageTargetTexture2DOES_t *glEGLImageTargetTexture2DOES;
123                 float distance, fov;
124         } gl;
126         // GBM.
127         struct {
128                 struct gbm_device *dev;
129                 struct gbm_surface *surface;
130         } gbm;
132         // DRM.
133         struct {
134                 // Note: fd is in base display
135                 drmModeModeInfo *mode;
136                 uint32_t crtc_id;
137                 uint32_t connector_id;
138                 drmModePlaneRes *plane_resources;
139         } drm;
141         //user specified connector id
142         uint32_t user_connector_id;
143 };
145 /* All our buffers are only vid buffers, and they all have an EGLImage. */
146 #define to_buffer_kmscube(x) container_of(x, struct buffer_kmscube, base)
147 struct buffer_kmscube {
148         struct buffer base;
149         uint32_t fb_id;
150         EGLImageKHR egl_img;
151 };
153 struct drm_fb {
154         struct gbm_bo *bo;
155         struct display_kmscube *disp_kmsc;
156         uint32_t fb_id;
157 };
159 static int init_drm(struct display_kmscube *disp_kmsc)
161         drmModeRes *resources;
162         drmModeConnector *connector = NULL;
163         drmModeEncoder *encoder = NULL;
164         int i, area;
166         resources = drmModeGetResources(disp_kmsc->base.fd);
167         if (!resources) {
168                 ERROR("drmModeGetResources failed: %s", strerror(errno));
169                 return -1;
170         }
172         /* find a connected connector: */
173         for (i = 0; i < resources->count_connectors; i++) {
174                 connector = drmModeGetConnector(disp_kmsc->base.fd, resources->connectors[i]);
175                 if (connector->connection == DRM_MODE_CONNECTED && \
176                     connector->connector_id == disp_kmsc->user_connector_id) {
177                         /* it's connected, let's use this! */
178                         break;
179                 }
180                 drmModeFreeConnector(connector);
181                 connector = NULL;
182         }
184         if (!connector) {
185                 /* we could be fancy and listen for hotplug events and wait for
186                  * a connector..
187                  */
188                 ERROR("no connected connector!");
189                 return -1;
190         }
192         /* find highest resolution mode: */
193         for (i = 0, area = 0; i < connector->count_modes; i++) {
194                 drmModeModeInfo *current_mode = &connector->modes[i];
195                 int current_area = current_mode->hdisplay * current_mode->vdisplay;
196                 if (current_area > area) {
197                         disp_kmsc->drm.mode = current_mode;
198                         area = current_area;
199                 }
200         }
202         if (!disp_kmsc->drm.mode) {
203                 ERROR("could not find mode!");
204                 return -1;
205         }
207         /* find encoder: */
208         for (i = 0; i < resources->count_encoders; i++) {
209                 encoder = drmModeGetEncoder(disp_kmsc->base.fd, resources->encoders[i]);
210                 if (encoder->encoder_id == connector->encoder_id)
211                         break;
212                 drmModeFreeEncoder(encoder);
213                 encoder = NULL;
214         }
216         if (!encoder) {
217                 ERROR("no encoder!");
218                 return -1;
219         }
221         disp_kmsc->drm.crtc_id = encoder->crtc_id;
222         disp_kmsc->drm.connector_id = connector->connector_id;
223         printf("Chosen Connector ID = %d\n", disp_kmsc->drm.connector_id);
225         return 0;
228 static int init_gbm(struct display_kmscube *disp_kmsc)
230         disp_kmsc->gbm.dev = gbm_create_device(disp_kmsc->base.fd);
232         disp_kmsc->gbm.surface = gbm_surface_create(disp_kmsc->gbm.dev,
233                         disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay,
234                         GBM_FORMAT_XRGB8888,
235                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
236         if (!disp_kmsc->gbm.surface) {
237                 ERROR("failed to create gbm surface");
238                 return -1;
239         }
241         return 0;
244 static int init_gl(struct display_kmscube *disp_kmsc)
246         EGLint major, minor, n;
247         GLuint vertex_shader, fragment_shader;
248         GLint ret;
250         static const EGLint context_attribs[] = {
251                 EGL_CONTEXT_CLIENT_VERSION, 2,
252                 EGL_NONE
253         };
255         static const EGLint config_attribs[] = {
256                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
257                 EGL_RED_SIZE, 1,
258                 EGL_GREEN_SIZE, 1,
259                 EGL_BLUE_SIZE, 1,
260                 EGL_ALPHA_SIZE, 0,
261                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
262                 EGL_NONE
263         };
265         static const char *vertex_shader_source =
266                         "uniform mat4 modelviewMatrix;      \n"
267                         "uniform mat4 modelviewprojectionMatrix;\n"
268                         "uniform mat3 normalMatrix;         \n"
269                         "                                   \n"
270                         "attribute vec4 in_position;        \n"
271                         "attribute vec3 in_normal;          \n"
272                         "attribute vec4 in_color;           \n"
273                         "attribute vec2 in_texuv;           \n"
274                         "\n"
275                         "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
276                         "                                   \n"
277                         "varying float VaryingLight;        \n"
278                         "varying vec2 vVaryingTexUV;        \n"
279                         "                                   \n"
280                         "void main()                        \n"
281                         "{                                  \n"
282                         "    gl_Position = modelviewprojectionMatrix * in_position;\n"
283                         "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
284                         "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
285                         "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
286                         "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
287                         "    VaryingLight = max(0.0, dot(vEyeNormal, vLightDir));\n"
288                         "    vVaryingTexUV = in_texuv;      \n"
289                         "}                                  \n";
291         static const char *fragment_shader_source =
292                         "#extension GL_OES_EGL_image_external : require\n"
293                         "                                   \n"
294                         "precision mediump float;           \n"
295                         "                                   \n"
296                         "uniform samplerExternalOES texture;\n"
297                         "                                   \n"
298                         "varying float VaryingLight;        \n"
299                         "varying vec2 vVaryingTexUV;        \n"
300                         "                                   \n"
301                         "void main()                        \n"
302                         "{                                  \n"
303                         "    vec4 t = texture2D(texture, vVaryingTexUV);\n"
304                         "    gl_FragColor = vec4(VaryingLight * t.rgb, 1.0);\n"
305                         "}                                  \n";
307         disp_kmsc->gl.display = eglGetDisplay(disp_kmsc->gbm.dev);
309         if (!eglInitialize(disp_kmsc->gl.display, &major, &minor)) {
310                 ERROR("failed to initialize");
311                 return -1;
312         }
314         printf("Using display %p with EGL version %d.%d\n",
315                         disp_kmsc->gl.display, major, minor);
317         printf("EGL Version \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VERSION));
318         printf("EGL Vendor \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VENDOR));
319         printf("EGL Extensions \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_EXTENSIONS));
321         if (!eglBindAPI(EGL_OPENGL_ES_API)) {
322                 ERROR("failed to bind api EGL_OPENGL_ES_API");
323                 return -1;
324         }
326         if (!eglChooseConfig(disp_kmsc->gl.display, config_attribs, &disp_kmsc->gl.config, 1, &n) || n != 1) {
327                 ERROR("failed to choose config: %d", n);
328                 return -1;
329         }
331         disp_kmsc->gl.context = eglCreateContext(disp_kmsc->gl.display, disp_kmsc->gl.config,
332                         EGL_NO_CONTEXT, context_attribs);
333         if (disp_kmsc->gl.context == NULL) {
334                 ERROR("failed to create context");
335                 return -1;
336         }
338         disp_kmsc->gl.surface = eglCreateWindowSurface(disp_kmsc->gl.display,
339                 disp_kmsc->gl.config, disp_kmsc->gbm.surface, NULL);
340         if (disp_kmsc->gl.surface == EGL_NO_SURFACE) {
341                 ERROR("failed to create egl surface");
342                 return -1;
343         }
345         /* connect the context to the surface */
346         eglMakeCurrent(disp_kmsc->gl.display, disp_kmsc->gl.surface,
347                         disp_kmsc->gl.surface, disp_kmsc->gl.context);
349         // EGL Image.
350         if (!(disp_kmsc->gl.eglCreateImageKHR = eglGetProcAddress("eglCreateImageKHR"))) {
351                 ERROR("No eglCreateImageKHR?!");
352                 return -1;
353         }
355         if (!(disp_kmsc->gl.eglDestroyImageKHR = eglGetProcAddress("eglDestroyImageKHR"))) {
356                 ERROR("No eglDestroyImageKHR?!");
357                 return -1;
358         }
360         if (!(disp_kmsc->gl.glEGLImageTargetTexture2DOES = eglGetProcAddress("glEGLImageTargetTexture2DOES"))) {
361                 ERROR("No glEGLImageTargetTexture2DOES?!");
362                 return -1;
363         }
365         const char *exts = glGetString(GL_EXTENSIONS);
366         printf("GL Extensions \"%s\"\n", exts);
368         if (!strstr(exts, "GL_TI_image_external_raw_video")) {
369                 ERROR("No GL_TI_image_external_raw_video extension?!");
370                 return -1;
371         }
373         vertex_shader = glCreateShader(GL_VERTEX_SHADER);
375         glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
376         glCompileShader(vertex_shader);
378         glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
379         if (!ret) {
380                 char *log;
382                 ERROR("vertex shader compilation failed!:");
383                 glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
384                 if (ret > 1) {
385                         log = malloc(ret);
386                         glGetShaderInfoLog(vertex_shader, ret, NULL, log);
387                         ERROR("%s", log);
388                 }
390                 return -1;
391         }
393         fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
395         glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
396         glCompileShader(fragment_shader);
398         glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
399         if (!ret) {
400                 char *log;
402                 ERROR("fragment shader compilation failed!:");
403                 glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
405                 if (ret > 1) {
406                         log = malloc(ret);
407                         glGetShaderInfoLog(fragment_shader, ret, NULL, log);
408                         ERROR("%s", log);
409                 }
411                 return -1;
412         }
414         disp_kmsc->gl.program = glCreateProgram();
416         glAttachShader(disp_kmsc->gl.program, vertex_shader);
417         glAttachShader(disp_kmsc->gl.program, fragment_shader);
419         glBindAttribLocation(disp_kmsc->gl.program, 0, "in_position");
420         glBindAttribLocation(disp_kmsc->gl.program, 1, "in_normal");
421         glBindAttribLocation(disp_kmsc->gl.program, 2, "in_color");
422         glBindAttribLocation(disp_kmsc->gl.program, 3, "in_texuv");
424         glLinkProgram(disp_kmsc->gl.program);
426         glGetProgramiv(disp_kmsc->gl.program, GL_LINK_STATUS, &ret);
427         if (!ret) {
428                 char *log;
430                 ERROR("program linking failed!:");
431                 glGetProgramiv(disp_kmsc->gl.program, GL_INFO_LOG_LENGTH, &ret);
433                 if (ret > 1) {
434                         log = malloc(ret);
435                         glGetProgramInfoLog(disp_kmsc->gl.program, ret, NULL, log);
436                         ERROR("%s", log);
437                 }
439                 return -1;
440         }
442         glUseProgram(disp_kmsc->gl.program);
444         disp_kmsc->gl.modelviewmatrix = glGetUniformLocation(disp_kmsc->gl.program, "modelviewMatrix");
445         disp_kmsc->gl.modelviewprojectionmatrix =
446                 glGetUniformLocation(disp_kmsc->gl.program, "modelviewprojectionMatrix");
447         disp_kmsc->gl.normalmatrix = glGetUniformLocation(disp_kmsc->gl.program, "normalMatrix");
449         glViewport(0, 0, disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay);
451         // Texture.
452         glGenTextures(1, &disp_kmsc->gl.texture_name);
453         glBindTexture(GL_TEXTURE_EXTERNAL_OES, disp_kmsc->gl.texture_name);
455         if (glGetError() != GL_NO_ERROR) {
456                 ERROR("glBindTexture!");
457                 return -1;
458         }
460         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
461         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
463         disp_kmsc->gl.uniform_texture = glGetUniformLocation(disp_kmsc->gl.program, "uniform_texture");
464         glUniform1i(disp_kmsc->gl.uniform_texture, 0);
466         return 0;
469 static void draw(struct display_kmscube *disp_kmsc)
471         ESMatrix modelview;
472         static const GLfloat vVertices[] = {
473                         // front
474                         -1.0f, -1.0f, +1.0f, // point blue
475                         +1.0f, -1.0f, +1.0f, // point magenta
476                         -1.0f, +1.0f, +1.0f, // point cyan
477                         +1.0f, +1.0f, +1.0f, // point white
478                         // back
479                         +1.0f, -1.0f, -1.0f, // point red
480                         -1.0f, -1.0f, -1.0f, // point black
481                         +1.0f, +1.0f, -1.0f, // point yellow
482                         -1.0f, +1.0f, -1.0f, // point green
483                         // right
484                         +1.0f, -1.0f, +1.0f, // point magenta
485                         +1.0f, -1.0f, -1.0f, // point red
486                         +1.0f, +1.0f, +1.0f, // point white
487                         +1.0f, +1.0f, -1.0f, // point yellow
488                         // left
489                         -1.0f, -1.0f, -1.0f, // point black
490                         -1.0f, -1.0f, +1.0f, // point blue
491                         -1.0f, +1.0f, -1.0f, // point green
492                         -1.0f, +1.0f, +1.0f, // point cyan
493                         // top
494                         -1.0f, +1.0f, +1.0f, // point cyan
495                         +1.0f, +1.0f, +1.0f, // point white
496                         -1.0f, +1.0f, -1.0f, // point green
497                         +1.0f, +1.0f, -1.0f, // point yellow
498                         // bottom
499                         -1.0f, -1.0f, -1.0f, // point black
500                         +1.0f, -1.0f, -1.0f, // point red
501                         -1.0f, -1.0f, +1.0f, // point blue
502                         +1.0f, -1.0f, +1.0f  // point magenta
503         };
505         static const GLfloat vColors[] = {
506                         // front
507                         0.0f,  0.0f,  1.0f, // blue
508                         1.0f,  0.0f,  1.0f, // magenta
509                         0.0f,  1.0f,  1.0f, // cyan
510                         1.0f,  1.0f,  1.0f, // white
511                         // back
512                         1.0f,  0.0f,  0.0f, // red
513                         0.0f,  0.0f,  0.0f, // black
514                         1.0f,  1.0f,  0.0f, // yellow
515                         0.0f,  1.0f,  0.0f, // green
516                         // right
517                         1.0f,  0.0f,  1.0f, // magenta
518                         1.0f,  0.0f,  0.0f, // red
519                         1.0f,  1.0f,  1.0f, // white
520                         1.0f,  1.0f,  0.0f, // yellow
521                         // left
522                         0.0f,  0.0f,  0.0f, // black
523                         0.0f,  0.0f,  1.0f, // blue
524                         0.0f,  1.0f,  0.0f, // green
525                         0.0f,  1.0f,  1.0f, // cyan
526                         // top
527                         0.0f,  1.0f,  1.0f, // cyan
528                         1.0f,  1.0f,  1.0f, // white
529                         0.0f,  1.0f,  0.0f, // green
530                         1.0f,  1.0f,  0.0f, // yellow
531                         // bottom
532                         0.0f,  0.0f,  0.0f, // black
533                         1.0f,  0.0f,  0.0f, // red
534                         0.0f,  0.0f,  1.0f, // blue
535                         1.0f,  0.0f,  1.0f  // magenta
536         };
538         static const GLfloat vNormals[] = {
539                         // front
540                         +0.0f, +0.0f, +1.0f, // forward
541                         +0.0f, +0.0f, +1.0f, // forward
542                         +0.0f, +0.0f, +1.0f, // forward
543                         +0.0f, +0.0f, +1.0f, // forward
544                         // back
545                         +0.0f, +0.0f, -1.0f, // backbard
546                         +0.0f, +0.0f, -1.0f, // backbard
547                         +0.0f, +0.0f, -1.0f, // backbard
548                         +0.0f, +0.0f, -1.0f, // backbard
549                         // right
550                         +1.0f, +0.0f, +0.0f, // right
551                         +1.0f, +0.0f, +0.0f, // right
552                         +1.0f, +0.0f, +0.0f, // right
553                         +1.0f, +0.0f, +0.0f, // right
554                         // left
555                         -1.0f, +0.0f, +0.0f, // left
556                         -1.0f, +0.0f, +0.0f, // left
557                         -1.0f, +0.0f, +0.0f, // left
558                         -1.0f, +0.0f, +0.0f, // left
559                         // top
560                         +0.0f, +1.0f, +0.0f, // up
561                         +0.0f, +1.0f, +0.0f, // up
562                         +0.0f, +1.0f, +0.0f, // up
563                         +0.0f, +1.0f, +0.0f, // up
564                         // bottom
565                         +0.0f, -1.0f, +0.0f, // down
566                         +0.0f, -1.0f, +0.0f, // down
567                         +0.0f, -1.0f, +0.0f, // down
568                         +0.0f, -1.0f, +0.0f  // down
569         };
571         static const GLfloat vTexUVs[] = {
572                         // front
573                         0.1f,  0.9f,
574                         0.9f,  0.9f,
575                         0.1f,  0.1f,
576                         0.9f,  0.1f,
577                         // back
578                         0.1f,  0.9f,
579                         0.9f,  0.9f,
580                         0.1f,  0.1f,
581                         0.9f,  0.1f,
582                         // right
583                         0.1f,  0.9f,
584                         0.9f,  0.9f,
585                         0.1f,  0.1f,
586                         0.9f,  0.1f,
587                         // left
588                         0.1f,  0.9f,
589                         0.9f,  0.9f,
590                         0.1f,  0.1f,
591                         0.9f,  0.1f,
592                         // top
593                         0.1f,  0.9f,
594                         0.9f,  0.9f,
595                         0.1f,  0.1f,
596                         0.9f,  0.1f,
597                         // bottom
598                         0.1f,  0.9f,
599                         0.9f,  0.9f,
600                         0.1f,  0.1f,
601                         0.9f,  0.1f,
602         };
604         /* clear the color buffer */
605         glClearColor(0.5, 0.5, 0.5, 1.0);
606         glClear(GL_COLOR_BUFFER_BIT);
608         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
609         glEnableVertexAttribArray(0);
611         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vNormals);
612         glEnableVertexAttribArray(1);
614         glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, vColors);
615         glEnableVertexAttribArray(2);
617         glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, vTexUVs);
618         glEnableVertexAttribArray(3);
620         esMatrixLoadIdentity(&modelview);
621         esTranslate(&modelview, 0.0f, 0.0f, -disp_kmsc->gl.distance);
622         esRotate(&modelview, 45.0f + (0.25f * disp_kmsc->i), 1.0f, 0.0f, 0.0f);
623         esRotate(&modelview, 45.0f - (0.5f * disp_kmsc->i), 0.0f, 1.0f, 0.0f);
624         esRotate(&modelview, 10.0f + (0.15f * disp_kmsc->i), 0.0f, 0.0f, 1.0f);
626         GLfloat aspect = (GLfloat)(disp_kmsc->drm.mode->hdisplay) / (GLfloat)(disp_kmsc->drm.mode->vdisplay);
628         ESMatrix projection;
629         esMatrixLoadIdentity(&projection);
630         esPerspective(&projection, disp_kmsc->gl.fov, aspect, 1.0f, 10.0f);
632         ESMatrix modelviewprojection;
633         esMatrixLoadIdentity(&modelviewprojection);
634         esMatrixMultiply(&modelviewprojection, &modelview, &projection);
636         float normal[9];
637         normal[0] = modelview.m[0][0];
638         normal[1] = modelview.m[0][1];
639         normal[2] = modelview.m[0][2];
640         normal[3] = modelview.m[1][0];
641         normal[4] = modelview.m[1][1];
642         normal[5] = modelview.m[1][2];
643         normal[6] = modelview.m[2][0];
644         normal[7] = modelview.m[2][1];
645         normal[8] = modelview.m[2][2];
647         glUniformMatrix4fv(disp_kmsc->gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
648         glUniformMatrix4fv(disp_kmsc->gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
649         glUniformMatrix3fv(disp_kmsc->gl.normalmatrix, 1, GL_FALSE, normal);
651         glEnable(GL_CULL_FACE);
653         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
654         glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
655         glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
656         glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
657         glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
658         glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
661 static void
662 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
664         struct drm_fb *fb = data;
665 //      struct gbm_device *gbm = gbm_bo_get_device(bo);
666         struct display_kmscube *disp_kmsc = fb->disp_kmsc;
668         if (fb->fb_id)
669                 drmModeRmFB(disp_kmsc->base.fd, fb->fb_id);
671         free(fb);
674 static struct drm_fb * drm_fb_get_from_bo(struct display_kmscube *disp_kmsc, struct gbm_bo *bo)
676         struct drm_fb *fb = gbm_bo_get_user_data(bo);
677         uint32_t width, height, stride, handle;
678         int ret;
680         if (fb)
681                 return fb;
683         fb = calloc(1, sizeof(*fb));
684         fb->bo = bo;
685         fb->disp_kmsc = disp_kmsc;
687         width = gbm_bo_get_width(bo);
688         height = gbm_bo_get_height(bo);
689         stride = gbm_bo_get_stride(bo);
690         handle = gbm_bo_get_handle(bo).u32;
692         ret = drmModeAddFB(disp_kmsc->base.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
693         if (ret) {
694                 ERROR("failed to create fb: %s", strerror(errno));
695                 free(fb);
696                 return NULL;
697         }
699         gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
701         return fb;
704 static void page_flip_handler(int fd, unsigned int frame,
705                   unsigned int sec, unsigned int usec, void *data)
707         int *waiting_for_flip = data;
708         *waiting_for_flip = 0;
711 static struct omap_bo *
712 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
713                 uint32_t *bo_handle, uint32_t *pitch)
715         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
716         struct omap_bo *bo;
717         uint32_t bo_flags = disp_kmsc->bo_flags;
719         if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
720                 bo_flags &= ~OMAP_BO_TILED;
721                 if (bpp == 8) {
722                         bo_flags |= OMAP_BO_TILED_8;
723                 } else if (bpp == 16) {
724                         bo_flags |= OMAP_BO_TILED_16;
725                 } else if (bpp == 32) {
726                         bo_flags |= OMAP_BO_TILED_32;
727                 }
728         }
730         bo_flags |= OMAP_BO_WC;
732         if (bo_flags & OMAP_BO_TILED) {
733                 bo = omap_bo_new_tiled(disp->dev, width, height, bo_flags);
734         } else {
735                 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
736         }
738         if (bo) {
739                 *bo_handle = omap_bo_handle(bo);
740                 *pitch = width * bpp / 8;
741                 if (bo_flags & OMAP_BO_TILED)
742                         *pitch = ALIGN2(*pitch, PAGE_SHIFT);
743         }
745         return bo;
748 /* We allocate single planar buffers, always. This, for EGLImage. Also, we
749  * create on EGLImageKHR per buffer. */
750 static struct buffer *
751 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
753         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
754         struct buffer_kmscube *buf_kmsc;
755         struct buffer *buf;
756         uint32_t bo_handles[4] = {0}, offsets[4] = {0};
757         int ret;
759         buf_kmsc = calloc(1, sizeof(*buf_kmsc));
760         if (!buf_kmsc) {
761                 ERROR("allocation failed");
762                 return NULL;
763         }
764         buf = &buf_kmsc->base;
766         buf->fourcc = fourcc;
767         buf->width = w;
768         buf->height = h;
769         buf->multiplanar = true;
771         buf->nbo = 1;
773         if (!fourcc)
774                 fourcc = FOURCC('A','R','2','4');
776         switch(fourcc) {
777         case FOURCC('A','R','2','4'):
778                 buf->nbo = 1;
779                 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
780                                 &bo_handles[0], &buf->pitches[0]);
781                 break;
782         case FOURCC('U','Y','V','Y'):
783         case FOURCC('Y','U','Y','V'):
784                 buf->nbo = 1;
785                 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
786                                 &bo_handles[0], &buf->pitches[0]);
787                 break;
788         case FOURCC('N','V','1','2'):
789                 if (disp->multiplanar) {
790                         buf->nbo = 2;
791                         buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
792                                         &bo_handles[0], &buf->pitches[0]);
793                         buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
794                         buf->bo[1] = alloc_bo(disp, 16, buf->width/2, buf->height/2,
795                                         &bo_handles[1], &buf->pitches[1]);
796                         buf->fd[1] = omap_bo_dmabuf(buf->bo[1]);
797                 } else {
798                         buf->nbo = 1;
799                         buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
800                                         &bo_handles[0], &buf->pitches[0]);
801                         buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
802                         bo_handles[1] = bo_handles[0];
803                         buf->pitches[1] = buf->pitches[0];
804                         offsets[1] = buf->width * buf->height;
805                         buf->multiplanar = false;
806                 }
807                 break;
808         case FOURCC('I','4','2','0'):
809                 buf->nbo = 1;
810                 buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
811                                 &bo_handles[0], &buf->pitches[0]);
812                 break;
813         default:
814                 ERROR("invalid format: 0x%08x", fourcc);
815                 goto fail;
816         }
818         // Create EGLImage and return.
819         // TODO: cropping attributes when this will be supported.
820         EGLint attr[] = {
821             EGL_GL_VIDEO_FOURCC_TI,      buf->fourcc,
822             EGL_GL_VIDEO_WIDTH_TI,       buf->width,
823             EGL_GL_VIDEO_HEIGHT_TI,      buf->height,
824             EGL_GL_VIDEO_BYTE_SIZE_TI,   omap_bo_size(buf->bo[0]),
825             // TODO: pick proper YUV flags..
826             EGL_GL_VIDEO_YUV_FLAGS_TI,   EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE | EGLIMAGE_FLAGS_YUV_BT601,
827             EGL_NONE
828         };
830         int fd = omap_bo_dmabuf(buf->bo[0]);
832         buf_kmsc->egl_img =
833         disp_kmsc->gl.eglCreateImageKHR(disp_kmsc->gl.display, EGL_NO_CONTEXT,
834                                         EGL_RAW_VIDEO_TI2, (EGLClientBuffer)fd, attr);
836         if (buf_kmsc->egl_img == EGL_NO_IMAGE_KHR) {
837                 ERROR("eglCreateImageKHR failed!\n");
838                 return NULL;
839         }
841         return buf;
843 fail:
844         // XXX cleanup
845         return NULL;
848 static struct buffer **
849 alloc_buffers(struct display *disp, uint32_t n,
850                 uint32_t fourcc, uint32_t w, uint32_t h)
852         struct buffer **bufs;
853         uint32_t i = 0;
855         bufs = calloc(n, sizeof(*bufs));
856         if (!bufs) {
857                 ERROR("allocation failed");
858                 goto fail;
859         }
861         for (i = 0; i < n; i++) {
862                 bufs[i] = alloc_buffer(disp, fourcc, w, h);
863                 if (!bufs[i]) {
864                         ERROR("allocation failed");
865                         goto fail;
866                 }
867         }
869         disp->buf = bufs;
870         return bufs;
872 fail:
873         // XXX cleanup
874         return NULL;
878 static void
879 free_buffers(struct display *disp, uint32_t n)
881         uint32_t i;
882         for (i = 0; i < n; i++) {
883                 if (disp->buf[i]) {
884                         close(disp->buf[i]->fd[0]);
885                         omap_bo_del(disp->buf[i]->bo[0]);
886                         if(disp->multiplanar){
887                                 close(disp->buf[i]->fd[1]);
888                                 omap_bo_del(disp->buf[i]->bo[1]);
889                         }
890                 }
891         }
892         free(disp->buf);
896 static struct buffer **
897 get_buffers(struct display *disp, uint32_t n)
899         MSG("get_buffers not supported!");
900         return NULL;
903 static struct buffer **
904 get_vid_buffers(struct display *disp, uint32_t n,
905                 uint32_t fourcc, uint32_t w, uint32_t h)
907         return alloc_buffers(disp, n, fourcc, w, h);
910 static int
911 post_buffer(struct display *disp, struct buffer *buf)
913         ERROR("post_buffer not supported!");
914         return -1;
917 static int
918 post_vid_buffer(struct display *disp, struct buffer *buf,
919                 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
921         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
922         struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
924 //      struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
925 //      int ret = 0;
926 //      uint32_t i, j;
927 //
928 //      /* ensure we have the overlay setup: */
929 //      for (i = 0; i < disp_kmsc->connectors_count; i++) {
930 //              struct connector *connector = &disp_kmsc->connector[i];
931 //              uint32_t used_planes = 0;
932 //              drmModeModeInfo *mode = connector->mode;
933 //
934 //              if (! mode) {
935 //                      continue;
936 //              }
937 //
938 //              if (! disp_kmsc->ovr[i]) {
939 //
940 //                      for (j = 0; j < disp_kmsc->plane_resources->count_planes; j++) {
941 //                              drmModePlane *ovr = drmModeGetPlane(disp->fd,
942 //                                              disp_kmsc->plane_resources->planes[j]);
943 //                              if ((ovr->possible_crtcs & (1 << connector->pipe)) &&
944 //                                              !(used_planes & (1 << j))) {
945 //                                      disp_kmsc->ovr[i] = ovr;
946 //                                      used_planes |= (1 << j);
947 //                                      break;
948 //                              }
949 //                      }
950 //              }
951 //
952 //              if (! disp_kmsc->ovr[i]) {
953 //                      MSG("Could not find plane for crtc %d", connector->crtc);
954 //                      ret = -1;
955 //                      /* carry on and see if we can find at least one usable plane */
956 //                      continue;
957 //              }
958 //
959 //              ret = drmModeSetPlane(disp->fd, disp_kmsc->ovr[i]->plane_id,
960 //                              connector->crtc, buf_kmsc->fb_id, 0,
961 //                              /* make video fullscreen: */
962 //                              0, 0, mode->hdisplay, mode->vdisplay,
963 //                              /* source/cropping coordinates are given in Q16 */
964 //                              x << 16, y << 16, w << 16, h << 16);
965 //              if (ret) {
966 //                      ERROR("failed to enable plane %d: %s",
967 //                                      disp_kmsc->ovr[i]->plane_id, strerror(errno));
968 //              }
969 //      }
970 //
971 //      return ret;
973         // TODO: For now, draw cube...
975         fd_set fds;
976         drmEventContext evctx = {
977                         .version = DRM_EVENT_CONTEXT_VERSION,
978                         .page_flip_handler = page_flip_handler,
979         };
980         struct gbm_bo *bo;
981         struct drm_fb *fb;
982         int ret;
983         struct gbm_bo *next_bo;
984         int waiting_for_flip = 1;
986         FD_ZERO(&fds);
987         FD_SET(0, &fds);
988         FD_SET(disp_kmsc->base.fd, &fds);
990         // Update video texture / EGL Image.
991         disp_kmsc->gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->egl_img);
993         if (glGetError() != GL_NO_ERROR) {
994                 ERROR("glEGLImageTargetTexture2DOES!\n");
995                 return -1;
996         }
998         // Draw cube.
999         draw(disp_kmsc);
1000         (disp_kmsc->i)++;
1002         eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
1003         next_bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
1004         fb = drm_fb_get_from_bo(disp_kmsc, next_bo);
1006         /*
1007          * Here you could also update drm plane layers if you want
1008          * hw composition
1009          */
1011         ret = drmModePageFlip(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id,
1012                         DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
1013         if (ret) {
1014                 ERROR("failed to queue page flip: %s\n", strerror(errno));
1015                 return -1;
1016         }
1018         while (waiting_for_flip) {
1019                 ret = select(disp_kmsc->base.fd + 1, &fds, NULL, NULL, NULL);
1020                 if (ret < 0) {
1021                         ERROR("select err: %s\n", strerror(errno));
1022                         return ret;
1023                 } else if (ret == 0) {
1024                         ERROR("select timeout!\n");
1025                         return -1;
1026                 } else if (FD_ISSET(0, &fds)) {
1027                         ERROR("user interrupted!\n");
1028                         break;
1029                 }
1030                 drmHandleEvent(disp_kmsc->base.fd, &evctx);
1031         }
1033         /* release last buffer to render on again: */
1034         gbm_surface_release_buffer(disp_kmsc->gbm.surface, bo);
1035         bo = next_bo;
1037         return 0;
1040 static void
1041 close_kmscube(struct display *disp)
1045 void
1046 disp_kmscube_usage(void)
1048         MSG("KMSCUBE Display Options:");
1049         MSG("\t--distance <float>\tset cube distance (default 8.0)");
1050         MSG("\t--fov <float>\tset field of vision (default 45.0)");
1051         MSG("\t--kmscube\tEnable display kmscube (default: disabled)");
1052         MSG("\t--connector <connector_id>\tset the connector ID (default: LCD)");
1055 struct display *
1056 disp_kmscube_open(int argc, char **argv)
1058         struct display_kmscube *disp_kmsc = NULL;
1059         struct display *disp;
1060         struct gbm_bo *bo;
1061         struct drm_fb *fb;
1062         int ret, i, enabled = 0;
1063         float fov = 45, distance = 8, connector_id = 4;
1065         /* note: set args to NULL after we've parsed them so other modules know
1066          * that it is already parsed (since the arg parsing is decentralized)
1067          */
1068         for (i = 1; i < argc; i++) {
1069                 if (!argv[i]) {
1070                         continue;
1071                 }
1072                 if (!strcmp("--distance", argv[i])) {
1073                         argv[i++] = NULL;
1074                         if (sscanf(argv[i], "%f", &distance) != 1) {
1075                                 ERROR("invalid arg: %s", argv[i]);
1076                                 goto fail;
1077                         }
1078                 } else if (!strcmp("--fov", argv[i])) {
1079                         argv[i++] = NULL;
1080                         if (sscanf(argv[i], "%f", &fov) != 1) {
1081                                 ERROR("invalid arg: %s", argv[i]);
1082                                 goto fail;
1083                         }
1084                 } else if (!strcmp("--connector", argv[i])) {
1085                         argv[i++] = NULL;
1086                         if (sscanf(argv[i], "%f", &connector_id) != 1) {
1087                                 ERROR("invalid arg: %s", argv[i]);
1088                                 goto fail;
1089                         }
1090                 } else if (!strcmp("--kmscube", argv[i])) {
1091                         enabled = 1;
1092                 } else {
1093                         /* ignore */
1094                         continue;
1095                 }
1096                 argv[i] = NULL;
1097         }
1099         // If not explicitely enabled from command line, fail (and fallback to disp-kms).
1100         if (!enabled)
1101                 goto fail;
1103         disp_kmsc = calloc(1, sizeof(*disp_kmsc));
1104         if (!disp_kmsc) {
1105                 ERROR("allocation failed");
1106                 goto fail;
1107         }
1108         disp_kmsc->gl.distance = distance;
1109         disp_kmsc->gl.fov = fov;
1110         disp_kmsc->user_connector_id = connector_id;
1111         disp = &disp_kmsc->base;
1113         disp->fd = drmOpen("omapdrm", NULL);
1114         if (disp->fd < 0) {
1115                 ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
1116                 goto fail;
1117         }
1119         disp->dev = omap_device_new(disp->fd);
1120         if (!disp->dev) {
1121                 ERROR("couldn't create device");
1122                 goto fail;
1123         }
1125         disp->get_buffers = get_buffers;
1126         disp->get_vid_buffers = get_vid_buffers;
1127         disp->post_buffer = post_buffer;
1128         disp->post_vid_buffer = post_vid_buffer;
1129         disp->close = close_kmscube;
1130         disp->disp_free_buf = free_buffers;
1133         if (init_drm(disp_kmsc)) {
1134                 ERROR("couldn't init drm");
1135                 goto fail;
1136         }
1138         disp_kmsc->drm.plane_resources = drmModeGetPlaneResources(disp->fd);
1139         if (!disp_kmsc->drm.plane_resources) {
1140                 ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
1141                 goto fail;
1142         }
1144         if (init_gbm(disp_kmsc)) {
1145                 ERROR("couldn't init gbm");
1146                 goto fail;
1147         }
1149         if (init_gl(disp_kmsc)) {
1150                 ERROR("couldn't init gl(es)");
1151                 goto fail;
1152         }
1154         /* clear the color buffer */
1155         glClearColor(0.5, 0.5, 0.5, 1.0);
1156         glClear(GL_COLOR_BUFFER_BIT);
1157         eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
1158         bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
1159         fb = drm_fb_get_from_bo(disp_kmsc, bo);
1161         /* set mode: */
1162         ret = drmModeSetCrtc(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id, 0, 0,
1163                         &disp_kmsc->drm.connector_id, 1, disp_kmsc->drm.mode);
1164         if (ret) {
1165                 ERROR("failed to set mode: %s\n", strerror(errno));
1166                 return ret;
1167         }
1169         disp->width = 0;
1170         disp->height = 0;
1171         disp->multiplanar = false;
1172 //      for (i = 0; i < (int)disp_kmsc->connectors_count; i++) {
1173 //              struct connector *c = &disp_kmsc->connector[i];
1174 //              connector_find_mode(disp, c);
1175 //              if (c->mode == NULL)
1176 //                      continue;
1177 //              /* setup side-by-side virtual display */
1178 //              disp->width += c->mode->hdisplay;
1179 //              if (disp->height < c->mode->vdisplay) {
1180 //                      disp->height = c->mode->vdisplay;
1181 //              }
1182 //      }
1183 //
1184 //      MSG("using %d connectors, %dx%d display, multiplanar: %d",
1185 //                      disp_kmsc->connectors_count, disp->width, disp->height, disp->multiplanar);
1187         return disp;
1189 fail:
1190         // XXX cleanup
1191         return NULL;