Hack! disp-kmscube: reduce u/v by 10%
[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;
140 };
142 /* All our buffers are only vid buffers, and they all have an EGLImage. */
143 #define to_buffer_kmscube(x) container_of(x, struct buffer_kmscube, base)
144 struct buffer_kmscube {
145         struct buffer base;
146         uint32_t fb_id;
147         EGLImageKHR egl_img;
148 };
150 struct drm_fb {
151         struct gbm_bo *bo;
152         struct display_kmscube *disp_kmsc;
153         uint32_t fb_id;
154 };
156 static int init_drm(struct display_kmscube *disp_kmsc)
158         drmModeRes *resources;
159         drmModeConnector *connector = NULL;
160         drmModeEncoder *encoder = NULL;
161         int i, area;
163         resources = drmModeGetResources(disp_kmsc->base.fd);
164         if (!resources) {
165                 ERROR("drmModeGetResources failed: %s", strerror(errno));
166                 return -1;
167         }
169         /* find a connected connector: */
170         for (i = 0; i < resources->count_connectors; i++) {
171                 connector = drmModeGetConnector(disp_kmsc->base.fd, resources->connectors[i]);
172                 if (connector->connection == DRM_MODE_CONNECTED) {
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;
220         return 0;
223 static int init_gbm(struct display_kmscube *disp_kmsc)
225         disp_kmsc->gbm.dev = gbm_create_device(disp_kmsc->base.fd);
227         disp_kmsc->gbm.surface = gbm_surface_create(disp_kmsc->gbm.dev,
228                         disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay,
229                         GBM_FORMAT_XRGB8888,
230                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
231         if (!disp_kmsc->gbm.surface) {
232                 ERROR("failed to create gbm surface");
233                 return -1;
234         }
236         return 0;
239 static int init_gl(struct display_kmscube *disp_kmsc)
241         EGLint major, minor, n;
242         GLuint vertex_shader, fragment_shader;
243         GLint ret;
245         static const EGLint context_attribs[] = {
246                 EGL_CONTEXT_CLIENT_VERSION, 2,
247                 EGL_NONE
248         };
250         static const EGLint config_attribs[] = {
251                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
252                 EGL_RED_SIZE, 1,
253                 EGL_GREEN_SIZE, 1,
254                 EGL_BLUE_SIZE, 1,
255                 EGL_ALPHA_SIZE, 0,
256                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
257                 EGL_NONE
258         };
260         static const char *vertex_shader_source =
261                         "uniform mat4 modelviewMatrix;      \n"
262                         "uniform mat4 modelviewprojectionMatrix;\n"
263                         "uniform mat3 normalMatrix;         \n"
264                         "                                   \n"
265                         "attribute vec4 in_position;        \n"
266                         "attribute vec3 in_normal;          \n"
267                         "attribute vec4 in_color;           \n"
268                         "attribute vec2 in_texuv;           \n"
269                         "\n"
270                         "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
271                         "                                   \n"
272                         "varying float VaryingLight;        \n"
273                         "varying vec2 vVaryingTexUV;        \n"
274                         "                                   \n"
275                         "void main()                        \n"
276                         "{                                  \n"
277                         "    gl_Position = modelviewprojectionMatrix * in_position;\n"
278                         "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
279                         "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
280                         "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
281                         "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
282                         "    VaryingLight = max(0.0, dot(vEyeNormal, vLightDir));\n"
283                         "    vVaryingTexUV = in_texuv;      \n"
284                         "}                                  \n";
286         static const char *fragment_shader_source =
287                         "#extension GL_OES_EGL_image_external : require\n"
288                         "                                   \n"
289                         "precision mediump float;           \n"
290                         "                                   \n"
291                         "uniform samplerExternalOES texture;\n"
292                         "                                   \n"
293                         "varying float VaryingLight;        \n"
294                         "varying vec2 vVaryingTexUV;        \n"
295                         "                                   \n"
296                         "void main()                        \n"
297                         "{                                  \n"
298                         "    vec4 t = texture2D(texture, vVaryingTexUV);\n"
299                         "    gl_FragColor = vec4(VaryingLight * t.rgb, 1.0);\n"
300                         "}                                  \n";
302         disp_kmsc->gl.display = eglGetDisplay(disp_kmsc->gbm.dev);
304         if (!eglInitialize(disp_kmsc->gl.display, &major, &minor)) {
305                 ERROR("failed to initialize");
306                 return -1;
307         }
309         printf("Using display %p with EGL version %d.%d\n",
310                         disp_kmsc->gl.display, major, minor);
312         printf("EGL Version \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VERSION));
313         printf("EGL Vendor \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VENDOR));
314         printf("EGL Extensions \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_EXTENSIONS));
316         if (!eglBindAPI(EGL_OPENGL_ES_API)) {
317                 ERROR("failed to bind api EGL_OPENGL_ES_API");
318                 return -1;
319         }
321         if (!eglChooseConfig(disp_kmsc->gl.display, config_attribs, &disp_kmsc->gl.config, 1, &n) || n != 1) {
322                 ERROR("failed to choose config: %d", n);
323                 return -1;
324         }
326         disp_kmsc->gl.context = eglCreateContext(disp_kmsc->gl.display, disp_kmsc->gl.config,
327                         EGL_NO_CONTEXT, context_attribs);
328         if (disp_kmsc->gl.context == NULL) {
329                 ERROR("failed to create context");
330                 return -1;
331         }
333         disp_kmsc->gl.surface = eglCreateWindowSurface(disp_kmsc->gl.display,
334                 disp_kmsc->gl.config, disp_kmsc->gbm.surface, NULL);
335         if (disp_kmsc->gl.surface == EGL_NO_SURFACE) {
336                 ERROR("failed to create egl surface");
337                 return -1;
338         }
340         /* connect the context to the surface */
341         eglMakeCurrent(disp_kmsc->gl.display, disp_kmsc->gl.surface,
342                         disp_kmsc->gl.surface, disp_kmsc->gl.context);
344         // EGL Image.
345         if (!(disp_kmsc->gl.eglCreateImageKHR = eglGetProcAddress("eglCreateImageKHR"))) {
346                 ERROR("No eglCreateImageKHR?!");
347                 return -1;
348         }
350         if (!(disp_kmsc->gl.eglDestroyImageKHR = eglGetProcAddress("eglDestroyImageKHR"))) {
351                 ERROR("No eglDestroyImageKHR?!");
352                 return -1;
353         }
355         if (!(disp_kmsc->gl.glEGLImageTargetTexture2DOES = eglGetProcAddress("glEGLImageTargetTexture2DOES"))) {
356                 ERROR("No glEGLImageTargetTexture2DOES?!");
357                 return -1;
358         }
360         const char *exts = glGetString(GL_EXTENSIONS);
361         printf("GL Extensions \"%s\"\n", exts);
363         if (!strstr(exts, "GL_TI_image_external_raw_video")) {
364                 ERROR("No GL_TI_image_external_raw_video extension?!");
365                 return -1;
366         }
368         vertex_shader = glCreateShader(GL_VERTEX_SHADER);
370         glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
371         glCompileShader(vertex_shader);
373         glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
374         if (!ret) {
375                 char *log;
377                 ERROR("vertex shader compilation failed!:");
378                 glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
379                 if (ret > 1) {
380                         log = malloc(ret);
381                         glGetShaderInfoLog(vertex_shader, ret, NULL, log);
382                         ERROR("%s", log);
383                 }
385                 return -1;
386         }
388         fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
390         glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
391         glCompileShader(fragment_shader);
393         glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
394         if (!ret) {
395                 char *log;
397                 ERROR("fragment shader compilation failed!:");
398                 glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
400                 if (ret > 1) {
401                         log = malloc(ret);
402                         glGetShaderInfoLog(fragment_shader, ret, NULL, log);
403                         ERROR("%s", log);
404                 }
406                 return -1;
407         }
409         disp_kmsc->gl.program = glCreateProgram();
411         glAttachShader(disp_kmsc->gl.program, vertex_shader);
412         glAttachShader(disp_kmsc->gl.program, fragment_shader);
414         glBindAttribLocation(disp_kmsc->gl.program, 0, "in_position");
415         glBindAttribLocation(disp_kmsc->gl.program, 1, "in_normal");
416         glBindAttribLocation(disp_kmsc->gl.program, 2, "in_color");
417         glBindAttribLocation(disp_kmsc->gl.program, 3, "in_texuv");
419         glLinkProgram(disp_kmsc->gl.program);
421         glGetProgramiv(disp_kmsc->gl.program, GL_LINK_STATUS, &ret);
422         if (!ret) {
423                 char *log;
425                 ERROR("program linking failed!:");
426                 glGetProgramiv(disp_kmsc->gl.program, GL_INFO_LOG_LENGTH, &ret);
428                 if (ret > 1) {
429                         log = malloc(ret);
430                         glGetProgramInfoLog(disp_kmsc->gl.program, ret, NULL, log);
431                         ERROR("%s", log);
432                 }
434                 return -1;
435         }
437         glUseProgram(disp_kmsc->gl.program);
439         disp_kmsc->gl.modelviewmatrix = glGetUniformLocation(disp_kmsc->gl.program, "modelviewMatrix");
440         disp_kmsc->gl.modelviewprojectionmatrix =
441                 glGetUniformLocation(disp_kmsc->gl.program, "modelviewprojectionMatrix");
442         disp_kmsc->gl.normalmatrix = glGetUniformLocation(disp_kmsc->gl.program, "normalMatrix");
444         glViewport(0, 0, disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay);
446         // Texture.
447         glGenTextures(1, &disp_kmsc->gl.texture_name);
448         glBindTexture(GL_TEXTURE_EXTERNAL_OES, disp_kmsc->gl.texture_name);
450         if (glGetError() != GL_NO_ERROR) {
451                 ERROR("glBindTexture!");
452                 return -1;
453         }
455         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
456         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
458         disp_kmsc->gl.uniform_texture = glGetUniformLocation(disp_kmsc->gl.program, "uniform_texture");
459         glUniform1i(disp_kmsc->gl.uniform_texture, 0);
461         return 0;
464 static void draw(struct display_kmscube *disp_kmsc)
466         ESMatrix modelview;
467         static const GLfloat vVertices[] = {
468                         // front
469                         -1.0f, -1.0f, +1.0f, // point blue
470                         +1.0f, -1.0f, +1.0f, // point magenta
471                         -1.0f, +1.0f, +1.0f, // point cyan
472                         +1.0f, +1.0f, +1.0f, // point white
473                         // back
474                         +1.0f, -1.0f, -1.0f, // point red
475                         -1.0f, -1.0f, -1.0f, // point black
476                         +1.0f, +1.0f, -1.0f, // point yellow
477                         -1.0f, +1.0f, -1.0f, // point green
478                         // right
479                         +1.0f, -1.0f, +1.0f, // point magenta
480                         +1.0f, -1.0f, -1.0f, // point red
481                         +1.0f, +1.0f, +1.0f, // point white
482                         +1.0f, +1.0f, -1.0f, // point yellow
483                         // left
484                         -1.0f, -1.0f, -1.0f, // point black
485                         -1.0f, -1.0f, +1.0f, // point blue
486                         -1.0f, +1.0f, -1.0f, // point green
487                         -1.0f, +1.0f, +1.0f, // point cyan
488                         // top
489                         -1.0f, +1.0f, +1.0f, // point cyan
490                         +1.0f, +1.0f, +1.0f, // point white
491                         -1.0f, +1.0f, -1.0f, // point green
492                         +1.0f, +1.0f, -1.0f, // point yellow
493                         // bottom
494                         -1.0f, -1.0f, -1.0f, // point black
495                         +1.0f, -1.0f, -1.0f, // point red
496                         -1.0f, -1.0f, +1.0f, // point blue
497                         +1.0f, -1.0f, +1.0f  // point magenta
498         };
500         static const GLfloat vColors[] = {
501                         // front
502                         0.0f,  0.0f,  1.0f, // blue
503                         1.0f,  0.0f,  1.0f, // magenta
504                         0.0f,  1.0f,  1.0f, // cyan
505                         1.0f,  1.0f,  1.0f, // white
506                         // back
507                         1.0f,  0.0f,  0.0f, // red
508                         0.0f,  0.0f,  0.0f, // black
509                         1.0f,  1.0f,  0.0f, // yellow
510                         0.0f,  1.0f,  0.0f, // green
511                         // right
512                         1.0f,  0.0f,  1.0f, // magenta
513                         1.0f,  0.0f,  0.0f, // red
514                         1.0f,  1.0f,  1.0f, // white
515                         1.0f,  1.0f,  0.0f, // yellow
516                         // left
517                         0.0f,  0.0f,  0.0f, // black
518                         0.0f,  0.0f,  1.0f, // blue
519                         0.0f,  1.0f,  0.0f, // green
520                         0.0f,  1.0f,  1.0f, // cyan
521                         // top
522                         0.0f,  1.0f,  1.0f, // cyan
523                         1.0f,  1.0f,  1.0f, // white
524                         0.0f,  1.0f,  0.0f, // green
525                         1.0f,  1.0f,  0.0f, // yellow
526                         // bottom
527                         0.0f,  0.0f,  0.0f, // black
528                         1.0f,  0.0f,  0.0f, // red
529                         0.0f,  0.0f,  1.0f, // blue
530                         1.0f,  0.0f,  1.0f  // magenta
531         };
533         static const GLfloat vNormals[] = {
534                         // front
535                         +0.0f, +0.0f, +1.0f, // forward
536                         +0.0f, +0.0f, +1.0f, // forward
537                         +0.0f, +0.0f, +1.0f, // forward
538                         +0.0f, +0.0f, +1.0f, // forward
539                         // back
540                         +0.0f, +0.0f, -1.0f, // backbard
541                         +0.0f, +0.0f, -1.0f, // backbard
542                         +0.0f, +0.0f, -1.0f, // backbard
543                         +0.0f, +0.0f, -1.0f, // backbard
544                         // right
545                         +1.0f, +0.0f, +0.0f, // right
546                         +1.0f, +0.0f, +0.0f, // right
547                         +1.0f, +0.0f, +0.0f, // right
548                         +1.0f, +0.0f, +0.0f, // right
549                         // left
550                         -1.0f, +0.0f, +0.0f, // left
551                         -1.0f, +0.0f, +0.0f, // left
552                         -1.0f, +0.0f, +0.0f, // left
553                         -1.0f, +0.0f, +0.0f, // left
554                         // top
555                         +0.0f, +1.0f, +0.0f, // up
556                         +0.0f, +1.0f, +0.0f, // up
557                         +0.0f, +1.0f, +0.0f, // up
558                         +0.0f, +1.0f, +0.0f, // up
559                         // bottom
560                         +0.0f, -1.0f, +0.0f, // down
561                         +0.0f, -1.0f, +0.0f, // down
562                         +0.0f, -1.0f, +0.0f, // down
563                         +0.0f, -1.0f, +0.0f  // down
564         };
566         static const GLfloat vTexUVs[] = {
567                         // front
568                         0.1f,  0.9f,
569                         0.9f,  0.9f,
570                         0.1f,  0.1f,
571                         0.9f,  0.1f,
572                         // back
573                         0.1f,  0.9f,
574                         0.9f,  0.9f,
575                         0.1f,  0.1f,
576                         0.9f,  0.1f,
577                         // right
578                         0.1f,  0.9f,
579                         0.9f,  0.9f,
580                         0.1f,  0.1f,
581                         0.9f,  0.1f,
582                         // left
583                         0.1f,  0.9f,
584                         0.9f,  0.9f,
585                         0.1f,  0.1f,
586                         0.9f,  0.1f,
587                         // top
588                         0.1f,  0.9f,
589                         0.9f,  0.9f,
590                         0.1f,  0.1f,
591                         0.9f,  0.1f,
592                         // bottom
593                         0.1f,  0.9f,
594                         0.9f,  0.9f,
595                         0.1f,  0.1f,
596                         0.9f,  0.1f,
597         };
599         /* clear the color buffer */
600         glClearColor(0.5, 0.5, 0.5, 1.0);
601         glClear(GL_COLOR_BUFFER_BIT);
603         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
604         glEnableVertexAttribArray(0);
606         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vNormals);
607         glEnableVertexAttribArray(1);
609         glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, vColors);
610         glEnableVertexAttribArray(2);
612         glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, vTexUVs);
613         glEnableVertexAttribArray(3);
615         esMatrixLoadIdentity(&modelview);
616         esTranslate(&modelview, 0.0f, 0.0f, -disp_kmsc->gl.distance);
617         esRotate(&modelview, 45.0f + (0.25f * disp_kmsc->i), 1.0f, 0.0f, 0.0f);
618         esRotate(&modelview, 45.0f - (0.5f * disp_kmsc->i), 0.0f, 1.0f, 0.0f);
619         esRotate(&modelview, 10.0f + (0.15f * disp_kmsc->i), 0.0f, 0.0f, 1.0f);
621         GLfloat aspect = (GLfloat)(disp_kmsc->drm.mode->hdisplay) / (GLfloat)(disp_kmsc->drm.mode->vdisplay);
623         ESMatrix projection;
624         esMatrixLoadIdentity(&projection);
625         esPerspective(&projection, disp_kmsc->gl.fov, aspect, 1.0f, 10.0f);
627         ESMatrix modelviewprojection;
628         esMatrixLoadIdentity(&modelviewprojection);
629         esMatrixMultiply(&modelviewprojection, &modelview, &projection);
631         float normal[9];
632         normal[0] = modelview.m[0][0];
633         normal[1] = modelview.m[0][1];
634         normal[2] = modelview.m[0][2];
635         normal[3] = modelview.m[1][0];
636         normal[4] = modelview.m[1][1];
637         normal[5] = modelview.m[1][2];
638         normal[6] = modelview.m[2][0];
639         normal[7] = modelview.m[2][1];
640         normal[8] = modelview.m[2][2];
642         glUniformMatrix4fv(disp_kmsc->gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
643         glUniformMatrix4fv(disp_kmsc->gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
644         glUniformMatrix3fv(disp_kmsc->gl.normalmatrix, 1, GL_FALSE, normal);
646         glEnable(GL_CULL_FACE);
648         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
649         glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
650         glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
651         glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
652         glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
653         glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
656 static void
657 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
659         struct drm_fb *fb = data;
660 //      struct gbm_device *gbm = gbm_bo_get_device(bo);
661         struct display_kmscube *disp_kmsc = fb->disp_kmsc;
663         if (fb->fb_id)
664                 drmModeRmFB(disp_kmsc->base.fd, fb->fb_id);
666         free(fb);
669 static struct drm_fb * drm_fb_get_from_bo(struct display_kmscube *disp_kmsc, struct gbm_bo *bo)
671         struct drm_fb *fb = gbm_bo_get_user_data(bo);
672         uint32_t width, height, stride, handle;
673         int ret;
675         if (fb)
676                 return fb;
678         fb = calloc(1, sizeof(*fb));
679         fb->bo = bo;
680         fb->disp_kmsc = disp_kmsc;
682         width = gbm_bo_get_width(bo);
683         height = gbm_bo_get_height(bo);
684         stride = gbm_bo_get_stride(bo);
685         handle = gbm_bo_get_handle(bo).u32;
687         ret = drmModeAddFB(disp_kmsc->base.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
688         if (ret) {
689                 ERROR("failed to create fb: %s", strerror(errno));
690                 free(fb);
691                 return NULL;
692         }
694         gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
696         return fb;
699 static void page_flip_handler(int fd, unsigned int frame,
700                   unsigned int sec, unsigned int usec, void *data)
702         int *waiting_for_flip = data;
703         *waiting_for_flip = 0;
706 static struct omap_bo *
707 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
708                 uint32_t *bo_handle, uint32_t *pitch)
710         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
711         struct omap_bo *bo;
712         uint32_t bo_flags = disp_kmsc->bo_flags;
714         if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
715                 bo_flags &= ~OMAP_BO_TILED;
716                 if (bpp == 8) {
717                         bo_flags |= OMAP_BO_TILED_8;
718                 } else if (bpp == 16) {
719                         bo_flags |= OMAP_BO_TILED_16;
720                 } else if (bpp == 32) {
721                         bo_flags |= OMAP_BO_TILED_32;
722                 }
723         }
725         bo_flags |= OMAP_BO_WC;
727         if (bo_flags & OMAP_BO_TILED) {
728                 bo = omap_bo_new_tiled(disp->dev, width, height, bo_flags);
729         } else {
730                 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
731         }
733         if (bo) {
734                 *bo_handle = omap_bo_handle(bo);
735                 *pitch = width * bpp / 8;
736                 if (bo_flags & OMAP_BO_TILED)
737                         *pitch = ALIGN2(*pitch, PAGE_SHIFT);
738         }
740         return bo;
743 /* We allocate single planar buffers, always. This, for EGLImage. Also, we
744  * create on EGLImageKHR per buffer. */
745 static struct buffer *
746 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
748         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
749         struct buffer_kmscube *buf_kmsc;
750         struct buffer *buf;
751         uint32_t bo_handles[4] = {0}, offsets[4] = {0};
752         int ret;
754         buf_kmsc = calloc(1, sizeof(*buf_kmsc));
755         if (!buf_kmsc) {
756                 ERROR("allocation failed");
757                 return NULL;
758         }
759         buf = &buf_kmsc->base;
761         buf->fourcc = fourcc;
762         buf->width = w;
763         buf->height = h;
764         buf->multiplanar = false;
766         buf->nbo = 1;
768         if (!fourcc)
769                 fourcc = FOURCC('A','R','2','4');
771         switch(fourcc) {
772         case FOURCC('A','R','2','4'):
773                 buf->nbo = 1;
774                 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
775                                 &bo_handles[0], &buf->pitches[0]);
776                 break;
777         case FOURCC('U','Y','V','Y'):
778         case FOURCC('Y','U','Y','V'):
779                 buf->nbo = 1;
780                 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
781                                 &bo_handles[0], &buf->pitches[0]);
782                 break;
783         case FOURCC('N','V','1','2'):
784                 buf->nbo = 1;
785                 buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
786                                 &bo_handles[0], &buf->pitches[0]);
787                 break;
788         case FOURCC('I','4','2','0'):
789                 buf->nbo = 1;
790                 buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
791                                 &bo_handles[0], &buf->pitches[0]);
792                 break;
793         default:
794                 ERROR("invalid format: 0x%08x", fourcc);
795                 goto fail;
796         }
798         // Create EGLImage and return.
799         // TODO: cropping attributes when this will be supported.
800         EGLint attr[] = {
801             EGL_GL_VIDEO_FOURCC_TI,      buf->fourcc,
802             EGL_GL_VIDEO_WIDTH_TI,       buf->width,
803             EGL_GL_VIDEO_HEIGHT_TI,      buf->height,
804             EGL_GL_VIDEO_BYTE_SIZE_TI,   omap_bo_size(buf->bo[0]),
805             // TODO: pick proper YUV flags..
806             EGL_GL_VIDEO_YUV_FLAGS_TI,   EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE | EGLIMAGE_FLAGS_YUV_BT601,
807             EGL_NONE
808         };
810         int fd = omap_bo_dmabuf(buf->bo[0]);
812         buf_kmsc->egl_img =
813         disp_kmsc->gl.eglCreateImageKHR(disp_kmsc->gl.display, EGL_NO_CONTEXT,
814                                         EGL_RAW_VIDEO_TI2, (EGLClientBuffer)fd, attr);
816         if (buf_kmsc->egl_img == EGL_NO_IMAGE_KHR) {
817                 ERROR("eglCreateImageKHR failed!\n");
818                 return NULL;
819         }
821         return buf;
823 fail:
824         // XXX cleanup
825         return NULL;
828 static struct buffer **
829 alloc_buffers(struct display *disp, uint32_t n,
830                 uint32_t fourcc, uint32_t w, uint32_t h)
832         struct buffer **bufs;
833         uint32_t i = 0;
835         bufs = calloc(n, sizeof(*bufs));
836         if (!bufs) {
837                 ERROR("allocation failed");
838                 goto fail;
839         }
841         for (i = 0; i < n; i++) {
842                 bufs[i] = alloc_buffer(disp, fourcc, w, h);
843                 if (!bufs[i]) {
844                         ERROR("allocation failed");
845                         goto fail;
846                 }
847         }
849         return bufs;
851 fail:
852         // XXX cleanup
853         return NULL;
856 static struct buffer **
857 get_buffers(struct display *disp, uint32_t n)
859         MSG("get_buffers not supported!");
860         return NULL;
863 static struct buffer **
864 get_vid_buffers(struct display *disp, uint32_t n,
865                 uint32_t fourcc, uint32_t w, uint32_t h)
867         return alloc_buffers(disp, n, fourcc, w, h);
870 static int
871 post_buffer(struct display *disp, struct buffer *buf)
873         ERROR("post_buffer not supported!");
874         return -1;
877 static int
878 post_vid_buffer(struct display *disp, struct buffer *buf,
879                 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
881         struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
882         struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
884 //      struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
885 //      int ret = 0;
886 //      uint32_t i, j;
887 //
888 //      /* ensure we have the overlay setup: */
889 //      for (i = 0; i < disp_kmsc->connectors_count; i++) {
890 //              struct connector *connector = &disp_kmsc->connector[i];
891 //              uint32_t used_planes = 0;
892 //              drmModeModeInfo *mode = connector->mode;
893 //
894 //              if (! mode) {
895 //                      continue;
896 //              }
897 //
898 //              if (! disp_kmsc->ovr[i]) {
899 //
900 //                      for (j = 0; j < disp_kmsc->plane_resources->count_planes; j++) {
901 //                              drmModePlane *ovr = drmModeGetPlane(disp->fd,
902 //                                              disp_kmsc->plane_resources->planes[j]);
903 //                              if ((ovr->possible_crtcs & (1 << connector->pipe)) &&
904 //                                              !(used_planes & (1 << j))) {
905 //                                      disp_kmsc->ovr[i] = ovr;
906 //                                      used_planes |= (1 << j);
907 //                                      break;
908 //                              }
909 //                      }
910 //              }
911 //
912 //              if (! disp_kmsc->ovr[i]) {
913 //                      MSG("Could not find plane for crtc %d", connector->crtc);
914 //                      ret = -1;
915 //                      /* carry on and see if we can find at least one usable plane */
916 //                      continue;
917 //              }
918 //
919 //              ret = drmModeSetPlane(disp->fd, disp_kmsc->ovr[i]->plane_id,
920 //                              connector->crtc, buf_kmsc->fb_id, 0,
921 //                              /* make video fullscreen: */
922 //                              0, 0, mode->hdisplay, mode->vdisplay,
923 //                              /* source/cropping coordinates are given in Q16 */
924 //                              x << 16, y << 16, w << 16, h << 16);
925 //              if (ret) {
926 //                      ERROR("failed to enable plane %d: %s",
927 //                                      disp_kmsc->ovr[i]->plane_id, strerror(errno));
928 //              }
929 //      }
930 //
931 //      return ret;
933         // TODO: For now, draw cube...
935         fd_set fds;
936         drmEventContext evctx = {
937                         .version = DRM_EVENT_CONTEXT_VERSION,
938                         .page_flip_handler = page_flip_handler,
939         };
940         struct gbm_bo *bo;
941         struct drm_fb *fb;
942         int ret;
943         struct gbm_bo *next_bo;
944         int waiting_for_flip = 1;
946         FD_ZERO(&fds);
947         FD_SET(0, &fds);
948         FD_SET(disp_kmsc->base.fd, &fds);
950         // Update video texture / EGL Image.
951         disp_kmsc->gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->egl_img);
953         if (glGetError() != GL_NO_ERROR) {
954                 ERROR("glEGLImageTargetTexture2DOES!\n");
955                 return -1;
956         }
958         // Draw cube.
959         draw(disp_kmsc);
960         (disp_kmsc->i)++;
962         eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
963         next_bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
964         fb = drm_fb_get_from_bo(disp_kmsc, next_bo);
966         /*
967          * Here you could also update drm plane layers if you want
968          * hw composition
969          */
971         ret = drmModePageFlip(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id,
972                         DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
973         if (ret) {
974                 ERROR("failed to queue page flip: %s\n", strerror(errno));
975                 return -1;
976         }
978         while (waiting_for_flip) {
979                 ret = select(disp_kmsc->base.fd + 1, &fds, NULL, NULL, NULL);
980                 if (ret < 0) {
981                         ERROR("select err: %s\n", strerror(errno));
982                         return ret;
983                 } else if (ret == 0) {
984                         ERROR("select timeout!\n");
985                         return -1;
986                 } else if (FD_ISSET(0, &fds)) {
987                         ERROR("user interrupted!\n");
988                         break;
989                 }
990                 drmHandleEvent(disp_kmsc->base.fd, &evctx);
991         }
993         /* release last buffer to render on again: */
994         gbm_surface_release_buffer(disp_kmsc->gbm.surface, bo);
995         bo = next_bo;
997         return 0;
1000 static void
1001 close_kmscube(struct display *disp)
1005 void
1006 disp_kmscube_usage(void)
1008         MSG("KMSCUBE Display Options:");
1009         MSG("\t--distance <float>\tset cube distance (default 8.0)");
1010         MSG("\t--fov <float>\tset field of vision (default 45.0)");
1011         MSG("\t--kmscube\tEnable display kmscube (default: disabled)");
1014 struct display *
1015 disp_kmscube_open(int argc, char **argv)
1017         struct display_kmscube *disp_kmsc = NULL;
1018         struct display *disp;
1019         struct gbm_bo *bo;
1020         struct drm_fb *fb;
1021         int ret, i, enabled = 0;
1022         float fov = 45, distance = 8;
1024         /* note: set args to NULL after we've parsed them so other modules know
1025          * that it is already parsed (since the arg parsing is decentralized)
1026          */
1027         for (i = 1; i < argc; i++) {
1028                 if (!argv[i]) {
1029                         continue;
1030                 }
1031                 if (!strcmp("--distance", argv[i])) {
1032                         argv[i++] = NULL;
1033                         if (sscanf(argv[i], "%f", &distance) != 1) {
1034                                 ERROR("invalid arg: %s", argv[i]);
1035                                 goto fail;
1036                         }
1037                 } else if (!strcmp("--fov", argv[i])) {
1038                         argv[i++] = NULL;
1039                         if (sscanf(argv[i], "%f", &fov) != 1) {
1040                                 ERROR("invalid arg: %s", argv[i]);
1041                                 goto fail;
1042                         }
1043                 } else if (!strcmp("--kmscube", argv[i])) {
1044                         enabled = 1;
1045                 } else {
1046                         /* ignore */
1047                         continue;
1048                 }
1049                 argv[i] = NULL;
1050         }
1052         // If not explicitely enabled from command line, fail (and fallback to disp-kms).
1053         if (!enabled)
1054                 goto fail;
1056         disp_kmsc = calloc(1, sizeof(*disp_kmsc));
1057         if (!disp_kmsc) {
1058                 ERROR("allocation failed");
1059                 goto fail;
1060         }
1061         disp_kmsc->gl.distance = distance;
1062         disp_kmsc->gl.fov = fov;
1063         disp = &disp_kmsc->base;
1065         disp->fd = drmOpen("omapdrm", NULL);
1066         if (disp->fd < 0) {
1067                 ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
1068                 goto fail;
1069         }
1071         disp->dev = omap_device_new(disp->fd);
1072         if (!disp->dev) {
1073                 ERROR("couldn't create device");
1074                 goto fail;
1075         }
1077         disp->get_buffers = get_buffers;
1078         disp->get_vid_buffers = get_vid_buffers;
1079         disp->post_buffer = post_buffer;
1080         disp->post_vid_buffer = post_vid_buffer;
1081         disp->close = close_kmscube;
1083         if (init_drm(disp_kmsc)) {
1084                 ERROR("couldn't init drm");
1085                 goto fail;
1086         }
1088         disp_kmsc->drm.plane_resources = drmModeGetPlaneResources(disp->fd);
1089         if (!disp_kmsc->drm.plane_resources) {
1090                 ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
1091                 goto fail;
1092         }
1094         if (init_gbm(disp_kmsc)) {
1095                 ERROR("couldn't init gbm");
1096                 goto fail;
1097         }
1099         if (init_gl(disp_kmsc)) {
1100                 ERROR("couldn't init gl(es)");
1101                 goto fail;
1102         }
1104         /* clear the color buffer */
1105         glClearColor(0.5, 0.5, 0.5, 1.0);
1106         glClear(GL_COLOR_BUFFER_BIT);
1107         eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
1108         bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
1109         fb = drm_fb_get_from_bo(disp_kmsc, bo);
1111         /* set mode: */
1112         ret = drmModeSetCrtc(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id, 0, 0,
1113                         &disp_kmsc->drm.connector_id, 1, disp_kmsc->drm.mode);
1114         if (ret) {
1115                 ERROR("failed to set mode: %s\n", strerror(errno));
1116                 return ret;
1117         }
1119         disp->width = 0;
1120         disp->height = 0;
1121         disp->multiplanar = false;
1122 //      for (i = 0; i < (int)disp_kmsc->connectors_count; i++) {
1123 //              struct connector *c = &disp_kmsc->connector[i];
1124 //              connector_find_mode(disp, c);
1125 //              if (c->mode == NULL)
1126 //                      continue;
1127 //              /* setup side-by-side virtual display */
1128 //              disp->width += c->mode->hdisplay;
1129 //              if (disp->height < c->mode->vdisplay) {
1130 //                      disp->height = c->mode->vdisplay;
1131 //              }
1132 //      }
1133 //
1134 //      MSG("using %d connectors, %dx%d display, multiplanar: %d",
1135 //                      disp_kmsc->connectors_count, disp->width, disp->height, disp->multiplanar);
1137         return disp;
1139 fail:
1140         // XXX cleanup
1141         return NULL;