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