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)
157 {
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;
221 }
223 static int init_gbm(struct display_kmscube *disp_kmsc)
224 {
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;
237 }
239 static int init_gl(struct display_kmscube *disp_kmsc)
240 {
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;
462 }
464 static void draw(struct display_kmscube *disp_kmsc)
465 {
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);
654 }
656 static void
657 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
658 {
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);
667 }
669 static struct drm_fb * drm_fb_get_from_bo(struct display_kmscube *disp_kmsc, struct gbm_bo *bo)
670 {
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;
697 }
699 static void page_flip_handler(int fd, unsigned int frame,
700 unsigned int sec, unsigned int usec, void *data)
701 {
702 int *waiting_for_flip = data;
703 *waiting_for_flip = 0;
704 }
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)
709 {
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;
741 }
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)
747 {
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;
826 }
828 static struct buffer **
829 alloc_buffers(struct display *disp, uint32_t n,
830 uint32_t fourcc, uint32_t w, uint32_t h)
831 {
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;
854 }
856 static struct buffer **
857 get_buffers(struct display *disp, uint32_t n)
858 {
859 MSG("get_buffers not supported!");
860 return NULL;
861 }
863 static struct buffer **
864 get_vid_buffers(struct display *disp, uint32_t n,
865 uint32_t fourcc, uint32_t w, uint32_t h)
866 {
867 return alloc_buffers(disp, n, fourcc, w, h);
868 }
870 static int
871 post_buffer(struct display *disp, struct buffer *buf)
872 {
873 ERROR("post_buffer not supported!");
874 return -1;
875 }
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)
880 {
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;
998 }
1000 static void
1001 close_kmscube(struct display *disp)
1002 {
1003 }
1005 void
1006 disp_kmscube_usage(void)
1007 {
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)");
1012 }
1014 struct display *
1015 disp_kmscube_open(int argc, char **argv)
1016 {
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;
1142 }