New display kmscube
authorVincent Stehlé <v-stehle@ti.com>
Mon, 24 Sep 2012 08:09:22 +0000 (10:09 +0200)
committerNikhil Devshatwar <a0132237@ti.com>
Wed, 22 May 2013 09:45:30 +0000 (15:15 +0530)
Add a new display 'kmscube', based on the kmscube application by Arvin Schnell
and Rob Clark.

This display 'kmscube' draws a rotating 3d cube with EGL on kms display, with
video mapped as a texture on the cube faces.

Signed-off-by: Vincent Stehlé <v-stehle@ti.com>
Makefile.am
README
configure.ac
util/Makefile.am
util/display-kmscube.c [new file with mode: 0644]
util/esTransform.c [new file with mode: 0644]
util/esUtil.h [new file with mode: 0644]
util/util.c

index a51373b89eb41846ff08afd883d7f6a94c4459d3..5bf2c6c7f2f4b777c2a88662280ee8738a86caed 100644 (file)
@@ -26,8 +26,8 @@ if ENABLE_DCE
 bin_PROGRAMS += viddec3test
 endif
 
-LDADD_COMMON = util/libutil.la @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@
-AM_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @WARN_CFLAGS@ -I$(top_srcdir)/util
+LDADD_COMMON = util/libutil.la @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@ @GBM_LIBS@ @EGL_LIBS@ @GLES2_LIBS@
+AM_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @GBM_CFLAGS@ @EGL_CFLAGS@ @GLES2_CFLAGS@ @WARN_CFLAGS@ -I$(top_srcdir)/util
 
 fliptest_SOURCES = fliptest.c
 fliptest_LDADD = $(LDADD_COMMON)
diff --git a/README b/README
index e0668abb26566208b8577596a46fc52e198b2d9a..975942aa13090a9aadc46e1ff3335669f69b6e41 100644 (file)
--- a/README
+++ b/README
@@ -6,3 +6,6 @@ sudo ./viddec3test -s 7:1920x1080 ~/Videos/h264/quantum_of_solace-tlr1_h1080p.mo
 Example to run with x11/dri2video for windowed display:
 export DISPLAY=:0.0
 ./viddec3test ~/Videos/h264/quantum_of_solace-tlr1_h1080p.mov
+
+Example to run with video on 3D cube in kms mode:
+sudo ./viddec3test --kmscube artbeats.mov --loop --fps --fov 20
index a87480907e535fddc67ae66f273899516a5ef7d3..332030478be8fa71afb975e1744a312922a20a37 100644 (file)
@@ -75,6 +75,19 @@ else
 fi
 AM_CONDITIONAL(ENABLE_X11, [test "x$HAVE_X11" = xyes])
 
+# Check optional KMSCUBE:
+AC_ARG_ENABLE([kmscube], AS_HELP_STRING([--disable-kmscube], [disable kmscube display support]))
+AS_IF([test "x$enable_kmscube" != "xno"], [PKG_CHECK_EXISTS(gbm egl glesv2, [HAVE_KMSCUBE=yes], [HAVE_KMSCUBE=no])])
+if test "x$HAVE_KMSCUBE" = "xyes"; then
+       AC_DEFINE(HAVE_KMSCUBE, 1, [Have KMSCUBE support])
+       PKG_CHECK_MODULES(GBM, gbm)
+       PKG_CHECK_MODULES(EGL, egl)
+       PKG_CHECK_MODULES(GLES2, glesv2)
+else
+       AC_MSG_WARN([No KMSCUBE support detected, disabling KMSCUBE support])
+fi
+AM_CONDITIONAL(ENABLE_KMSCUBE, [test "x$HAVE_KMSCUBE" = xyes])
+
 # Check for libdce and libav..
 PKG_CHECK_MODULES(DCE, libdce libavformat libavutil, [HAVE_DCE=yes], [HAVE_DCE=no])
 if test "x$HAVE_DCE" = "xyes"; then
index eb1341180d95fd5d24d6774178dc42a3ee294e82..e88215d5c81efe71d2531252ded1341aaeb0935a 100644 (file)
@@ -33,5 +33,9 @@ if ENABLE_DCE
 libutil_la_SOURCES += demux.c
 endif
 
-libutil_la_LIBADD = @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@
-libutil_la_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @WARN_CFLAGS@
+if ENABLE_KMSCUBE
+libutil_la_SOURCES += display-kmscube.c esTransform.c
+endif
+
+libutil_la_LIBADD = @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@ @GBM_LIBS@ @EGL_LIBS@ @GLES2_LIBS@
+libutil_la_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @WARN_CFLAGS@ @GBM_CFLAGS@ @EGL_CFLAGS@ @GLES2_CFLAGS@
diff --git a/util/display-kmscube.c b/util/display-kmscube.c
new file mode 100644 (file)
index 0000000..755205e
--- /dev/null
@@ -0,0 +1,1142 @@
+/*
+ * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
+ * Copyright (c) 2012 Rob Clark <rob@ti.com>
+ * Copyright (c) 2012 Vincent Stehlé <v-stehle@ti.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Based on kmscube application by Rob Clark, which was based on a egl cube
+ * test app originally written by Arvin Schnell.  And based on display-kms by
+ * Rob Clark.  And also based on xbmc eglImageExternal and
+ * TIRawVideoEGLImageHandle additions by Rob Clark.
+ *
+ * This display 'kmscube' draws a rotating 3d cube with EGL on kms display,
+ * with video mapped as a texture on the cube faces. This display has the
+ * particularity to have no framebuffer, per se, as we expect to render with
+ * EGL/GL ES, rather.
+ *
+ * TODO:
+ * - Remove static structs
+ * - Factorize kms code with display-kms
+ * - Enable from command line only; no auto detect at open like x11/kms
+ * - Cleanup display_kmscube options
+ * - Implement the "post" functions so that cube faces are updated
+ * - Do the necessary cleanup in the close function
+ * - Remove the extra level of structure inside display_kmscube
+ * - Cleanup commented out code
+ * - Revisit disp pointer in struct drm_fb
+ * - Better handling of rounding and alignment when allocating single planar
+ *   YUV buffers.
+ * - Remove unused vertex colors.
+ * - Fix warnings
+ * - Allow selecting the mode from CLI, like display-kms
+ * - Better handling of cropping
+ * - Interactive keyboard keys for FOV, distance... with OSD?
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <gbm.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include "esUtil.h"
+
+#ifndef EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE
+// XXX these should come from some egl header??
+#define EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE (0 << 0)
+#define EGLIMAGE_FLAGS_YUV_FULL_RANGE       (1 << 0)
+#define EGLIMAGE_FLAGS_YUV_BT601            (0 << 1)
+#define EGLIMAGE_FLAGS_YUV_BT709            (1 << 1)
+#endif
+#ifndef EGL_TI_raw_video
+#  define EGL_TI_raw_video 1
+#  define EGL_RAW_VIDEO_TI            0x333A  /* eglCreateImageKHR target */
+#  define EGL_RAW_VIDEO_TI2           0x333B  /* eglCreateImageKHR target */
+#  define EGL_GL_VIDEO_FOURCC_TI        0x3331  /* eglCreateImageKHR attribute */
+#  define EGL_GL_VIDEO_WIDTH_TI         0x3332  /* eglCreateImageKHR attribute */
+#  define EGL_GL_VIDEO_HEIGHT_TI        0x3333  /* eglCreateImageKHR attribute */
+#  define EGL_GL_VIDEO_BYTE_STRIDE_TI     0x3334  /* eglCreateImageKHR attribute */
+#  define EGL_GL_VIDEO_BYTE_SIZE_TI       0x3335  /* eglCreateImageKHR attribute */
+#  define EGL_GL_VIDEO_YUV_FLAGS_TI       0x3336  /* eglCreateImageKHR attribute */
+#endif
+
+typedef EGLImageKHR (eglCreateImageKHR_t)(EGLDisplay dpy, EGLContext ctx,
+                       EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
+
+typedef EGLBoolean (eglDestroyImageKHR_t)(EGLDisplay dpy, EGLImageKHR image);
+
+typedef void (glEGLImageTargetTexture2DOES_t)(GLenum target, GLeglImageOES image);
+
+
+#define to_display_kmscube(x) container_of(x, struct display_kmscube, base)
+struct display_kmscube {
+       struct display base;
+       uint32_t bo_flags,
+               i;              // This is used to animate the cube.
+
+       // GL.
+       struct {
+               EGLDisplay display;
+               EGLConfig config;
+               EGLContext context;
+               EGLSurface surface;
+               GLuint program;
+               GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix, uniform_texture;
+               GLuint texture_name;
+               eglCreateImageKHR_t *eglCreateImageKHR;
+               eglDestroyImageKHR_t *eglDestroyImageKHR;
+               glEGLImageTargetTexture2DOES_t *glEGLImageTargetTexture2DOES;
+               float distance, fov;
+       } gl;
+
+       // GBM.
+       struct {
+               struct gbm_device *dev;
+               struct gbm_surface *surface;
+       } gbm;
+
+       // DRM.
+       struct {
+               // Note: fd is in base display
+               drmModeModeInfo *mode;
+               uint32_t crtc_id;
+               uint32_t connector_id;
+               drmModePlaneRes *plane_resources;
+       } drm;
+};
+
+/* All our buffers are only vid buffers, and they all have an EGLImage. */
+#define to_buffer_kmscube(x) container_of(x, struct buffer_kmscube, base)
+struct buffer_kmscube {
+       struct buffer base;
+       uint32_t fb_id;
+       EGLImageKHR egl_img;
+};
+
+struct drm_fb {
+       struct gbm_bo *bo;
+       struct display_kmscube *disp_kmsc;
+       uint32_t fb_id;
+};
+
+static int init_drm(struct display_kmscube *disp_kmsc)
+{
+       drmModeRes *resources;
+       drmModeConnector *connector = NULL;
+       drmModeEncoder *encoder = NULL;
+       int i, area;
+
+       resources = drmModeGetResources(disp_kmsc->base.fd);
+       if (!resources) {
+               ERROR("drmModeGetResources failed: %s", strerror(errno));
+               return -1;
+       }
+
+       /* find a connected connector: */
+       for (i = 0; i < resources->count_connectors; i++) {
+               connector = drmModeGetConnector(disp_kmsc->base.fd, resources->connectors[i]);
+               if (connector->connection == DRM_MODE_CONNECTED) {
+                       /* it's connected, let's use this! */
+                       break;
+               }
+               drmModeFreeConnector(connector);
+               connector = NULL;
+       }
+
+       if (!connector) {
+               /* we could be fancy and listen for hotplug events and wait for
+                * a connector..
+                */
+               ERROR("no connected connector!");
+               return -1;
+       }
+
+       /* find highest resolution mode: */
+       for (i = 0, area = 0; i < connector->count_modes; i++) {
+               drmModeModeInfo *current_mode = &connector->modes[i];
+               int current_area = current_mode->hdisplay * current_mode->vdisplay;
+               if (current_area > area) {
+                       disp_kmsc->drm.mode = current_mode;
+                       area = current_area;
+               }
+       }
+
+       if (!disp_kmsc->drm.mode) {
+               ERROR("could not find mode!");
+               return -1;
+       }
+
+       /* find encoder: */
+       for (i = 0; i < resources->count_encoders; i++) {
+               encoder = drmModeGetEncoder(disp_kmsc->base.fd, resources->encoders[i]);
+               if (encoder->encoder_id == connector->encoder_id)
+                       break;
+               drmModeFreeEncoder(encoder);
+               encoder = NULL;
+       }
+
+       if (!encoder) {
+               ERROR("no encoder!");
+               return -1;
+       }
+
+       disp_kmsc->drm.crtc_id = encoder->crtc_id;
+       disp_kmsc->drm.connector_id = connector->connector_id;
+
+       return 0;
+}
+
+static int init_gbm(struct display_kmscube *disp_kmsc)
+{
+       disp_kmsc->gbm.dev = gbm_create_device(disp_kmsc->base.fd);
+
+       disp_kmsc->gbm.surface = gbm_surface_create(disp_kmsc->gbm.dev,
+                       disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay,
+                       GBM_FORMAT_XRGB8888,
+                       GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+       if (!disp_kmsc->gbm.surface) {
+               ERROR("failed to create gbm surface");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int init_gl(struct display_kmscube *disp_kmsc)
+{
+       EGLint major, minor, n;
+       GLuint vertex_shader, fragment_shader;
+       GLint ret;
+
+       static const EGLint context_attribs[] = {
+               EGL_CONTEXT_CLIENT_VERSION, 2,
+               EGL_NONE
+       };
+
+       static const EGLint config_attribs[] = {
+               EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+               EGL_RED_SIZE, 1,
+               EGL_GREEN_SIZE, 1,
+               EGL_BLUE_SIZE, 1,
+               EGL_ALPHA_SIZE, 0,
+               EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+               EGL_NONE
+       };
+
+       static const char *vertex_shader_source =
+                       "uniform mat4 modelviewMatrix;      \n"
+                       "uniform mat4 modelviewprojectionMatrix;\n"
+                       "uniform mat3 normalMatrix;         \n"
+                       "                                   \n"
+                       "attribute vec4 in_position;        \n"
+                       "attribute vec3 in_normal;          \n"
+                       "attribute vec4 in_color;           \n"
+                       "attribute vec2 in_texuv;           \n"
+                       "\n"
+                       "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
+                       "                                   \n"
+                       "varying float VaryingLight;        \n"
+                       "varying vec2 vVaryingTexUV;        \n"
+                       "                                   \n"
+                       "void main()                        \n"
+                       "{                                  \n"
+                       "    gl_Position = modelviewprojectionMatrix * in_position;\n"
+                       "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
+                       "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
+                       "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
+                       "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
+                       "    VaryingLight = max(0.0, dot(vEyeNormal, vLightDir));\n"
+                       "    vVaryingTexUV = in_texuv;      \n"
+                       "}                                  \n";
+
+       static const char *fragment_shader_source =
+                       "#extension GL_OES_EGL_image_external : require\n"
+                       "                                   \n"
+                       "precision mediump float;           \n"
+                       "                                   \n"
+                       "uniform samplerExternalOES texture;\n"
+                       "                                   \n"
+                       "varying float VaryingLight;        \n"
+                       "varying vec2 vVaryingTexUV;        \n"
+                       "                                   \n"
+                       "void main()                        \n"
+                       "{                                  \n"
+                       "    vec4 t = texture2D(texture, vVaryingTexUV);\n"
+                       "    gl_FragColor = vec4(VaryingLight * t.rgb, 1.0);\n"
+                       "}                                  \n";
+
+       disp_kmsc->gl.display = eglGetDisplay(disp_kmsc->gbm.dev);
+
+       if (!eglInitialize(disp_kmsc->gl.display, &major, &minor)) {
+               ERROR("failed to initialize");
+               return -1;
+       }
+
+       printf("Using display %p with EGL version %d.%d\n",
+                       disp_kmsc->gl.display, major, minor);
+
+       printf("EGL Version \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VERSION));
+       printf("EGL Vendor \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VENDOR));
+       printf("EGL Extensions \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_EXTENSIONS));
+
+       if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+               ERROR("failed to bind api EGL_OPENGL_ES_API");
+               return -1;
+       }
+
+       if (!eglChooseConfig(disp_kmsc->gl.display, config_attribs, &disp_kmsc->gl.config, 1, &n) || n != 1) {
+               ERROR("failed to choose config: %d", n);
+               return -1;
+       }
+
+       disp_kmsc->gl.context = eglCreateContext(disp_kmsc->gl.display, disp_kmsc->gl.config,
+                       EGL_NO_CONTEXT, context_attribs);
+       if (disp_kmsc->gl.context == NULL) {
+               ERROR("failed to create context");
+               return -1;
+       }
+
+       disp_kmsc->gl.surface = eglCreateWindowSurface(disp_kmsc->gl.display,
+               disp_kmsc->gl.config, disp_kmsc->gbm.surface, NULL);
+       if (disp_kmsc->gl.surface == EGL_NO_SURFACE) {
+               ERROR("failed to create egl surface");
+               return -1;
+       }
+
+       /* connect the context to the surface */
+       eglMakeCurrent(disp_kmsc->gl.display, disp_kmsc->gl.surface,
+                       disp_kmsc->gl.surface, disp_kmsc->gl.context);
+
+       // EGL Image.
+       if (!(disp_kmsc->gl.eglCreateImageKHR = eglGetProcAddress("eglCreateImageKHR"))) {
+               ERROR("No eglCreateImageKHR?!");
+               return -1;
+       }
+
+       if (!(disp_kmsc->gl.eglDestroyImageKHR = eglGetProcAddress("eglDestroyImageKHR"))) {
+               ERROR("No eglDestroyImageKHR?!");
+               return -1;
+       }
+
+       if (!(disp_kmsc->gl.glEGLImageTargetTexture2DOES = eglGetProcAddress("glEGLImageTargetTexture2DOES"))) {
+               ERROR("No glEGLImageTargetTexture2DOES?!");
+               return -1;
+       }
+
+       const char *exts = glGetString(GL_EXTENSIONS);
+       printf("GL Extensions \"%s\"\n", exts);
+
+       if (!strstr(exts, "GL_TI_image_external_raw_video")) {
+               ERROR("No GL_TI_image_external_raw_video extension?!");
+               return -1;
+       }
+
+       vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+
+       glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
+       glCompileShader(vertex_shader);
+
+       glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
+       if (!ret) {
+               char *log;
+
+               ERROR("vertex shader compilation failed!:");
+               glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
+               if (ret > 1) {
+                       log = malloc(ret);
+                       glGetShaderInfoLog(vertex_shader, ret, NULL, log);
+                       ERROR("%s", log);
+               }
+
+               return -1;
+       }
+
+       fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+       glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
+       glCompileShader(fragment_shader);
+
+       glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
+       if (!ret) {
+               char *log;
+
+               ERROR("fragment shader compilation failed!:");
+               glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
+
+               if (ret > 1) {
+                       log = malloc(ret);
+                       glGetShaderInfoLog(fragment_shader, ret, NULL, log);
+                       ERROR("%s", log);
+               }
+
+               return -1;
+       }
+
+       disp_kmsc->gl.program = glCreateProgram();
+
+       glAttachShader(disp_kmsc->gl.program, vertex_shader);
+       glAttachShader(disp_kmsc->gl.program, fragment_shader);
+
+       glBindAttribLocation(disp_kmsc->gl.program, 0, "in_position");
+       glBindAttribLocation(disp_kmsc->gl.program, 1, "in_normal");
+       glBindAttribLocation(disp_kmsc->gl.program, 2, "in_color");
+       glBindAttribLocation(disp_kmsc->gl.program, 3, "in_texuv");
+
+       glLinkProgram(disp_kmsc->gl.program);
+
+       glGetProgramiv(disp_kmsc->gl.program, GL_LINK_STATUS, &ret);
+       if (!ret) {
+               char *log;
+
+               ERROR("program linking failed!:");
+               glGetProgramiv(disp_kmsc->gl.program, GL_INFO_LOG_LENGTH, &ret);
+
+               if (ret > 1) {
+                       log = malloc(ret);
+                       glGetProgramInfoLog(disp_kmsc->gl.program, ret, NULL, log);
+                       ERROR("%s", log);
+               }
+
+               return -1;
+       }
+
+       glUseProgram(disp_kmsc->gl.program);
+
+       disp_kmsc->gl.modelviewmatrix = glGetUniformLocation(disp_kmsc->gl.program, "modelviewMatrix");
+       disp_kmsc->gl.modelviewprojectionmatrix =
+               glGetUniformLocation(disp_kmsc->gl.program, "modelviewprojectionMatrix");
+       disp_kmsc->gl.normalmatrix = glGetUniformLocation(disp_kmsc->gl.program, "normalMatrix");
+
+       glViewport(0, 0, disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay);
+
+       // Texture.
+       glGenTextures(1, &disp_kmsc->gl.texture_name);
+       glBindTexture(GL_TEXTURE_EXTERNAL_OES, disp_kmsc->gl.texture_name);
+
+       if (glGetError() != GL_NO_ERROR) {
+               ERROR("glBindTexture!");
+               return -1;
+       }
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        disp_kmsc->gl.uniform_texture = glGetUniformLocation(disp_kmsc->gl.program, "uniform_texture");
+        glUniform1i(disp_kmsc->gl.uniform_texture, 0);
+
+       return 0;
+}
+
+static void draw(struct display_kmscube *disp_kmsc)
+{
+       ESMatrix modelview;
+       static const GLfloat vVertices[] = {
+                       // front
+                       -1.0f, -1.0f, +1.0f, // point blue
+                       +1.0f, -1.0f, +1.0f, // point magenta
+                       -1.0f, +1.0f, +1.0f, // point cyan
+                       +1.0f, +1.0f, +1.0f, // point white
+                       // back
+                       +1.0f, -1.0f, -1.0f, // point red
+                       -1.0f, -1.0f, -1.0f, // point black
+                       +1.0f, +1.0f, -1.0f, // point yellow
+                       -1.0f, +1.0f, -1.0f, // point green
+                       // right
+                       +1.0f, -1.0f, +1.0f, // point magenta
+                       +1.0f, -1.0f, -1.0f, // point red
+                       +1.0f, +1.0f, +1.0f, // point white
+                       +1.0f, +1.0f, -1.0f, // point yellow
+                       // left
+                       -1.0f, -1.0f, -1.0f, // point black
+                       -1.0f, -1.0f, +1.0f, // point blue
+                       -1.0f, +1.0f, -1.0f, // point green
+                       -1.0f, +1.0f, +1.0f, // point cyan
+                       // top
+                       -1.0f, +1.0f, +1.0f, // point cyan
+                       +1.0f, +1.0f, +1.0f, // point white
+                       -1.0f, +1.0f, -1.0f, // point green
+                       +1.0f, +1.0f, -1.0f, // point yellow
+                       // bottom
+                       -1.0f, -1.0f, -1.0f, // point black
+                       +1.0f, -1.0f, -1.0f, // point red
+                       -1.0f, -1.0f, +1.0f, // point blue
+                       +1.0f, -1.0f, +1.0f  // point magenta
+       };
+
+       static const GLfloat vColors[] = {
+                       // front
+                       0.0f,  0.0f,  1.0f, // blue
+                       1.0f,  0.0f,  1.0f, // magenta
+                       0.0f,  1.0f,  1.0f, // cyan
+                       1.0f,  1.0f,  1.0f, // white
+                       // back
+                       1.0f,  0.0f,  0.0f, // red
+                       0.0f,  0.0f,  0.0f, // black
+                       1.0f,  1.0f,  0.0f, // yellow
+                       0.0f,  1.0f,  0.0f, // green
+                       // right
+                       1.0f,  0.0f,  1.0f, // magenta
+                       1.0f,  0.0f,  0.0f, // red
+                       1.0f,  1.0f,  1.0f, // white
+                       1.0f,  1.0f,  0.0f, // yellow
+                       // left
+                       0.0f,  0.0f,  0.0f, // black
+                       0.0f,  0.0f,  1.0f, // blue
+                       0.0f,  1.0f,  0.0f, // green
+                       0.0f,  1.0f,  1.0f, // cyan
+                       // top
+                       0.0f,  1.0f,  1.0f, // cyan
+                       1.0f,  1.0f,  1.0f, // white
+                       0.0f,  1.0f,  0.0f, // green
+                       1.0f,  1.0f,  0.0f, // yellow
+                       // bottom
+                       0.0f,  0.0f,  0.0f, // black
+                       1.0f,  0.0f,  0.0f, // red
+                       0.0f,  0.0f,  1.0f, // blue
+                       1.0f,  0.0f,  1.0f  // magenta
+       };
+
+       static const GLfloat vNormals[] = {
+                       // front
+                       +0.0f, +0.0f, +1.0f, // forward
+                       +0.0f, +0.0f, +1.0f, // forward
+                       +0.0f, +0.0f, +1.0f, // forward
+                       +0.0f, +0.0f, +1.0f, // forward
+                       // back
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       // top
+                       +0.0f, +1.0f, +0.0f, // up
+                       +0.0f, +1.0f, +0.0f, // up
+                       +0.0f, +1.0f, +0.0f, // up
+                       +0.0f, +1.0f, +0.0f, // up
+                       // bottom
+                       +0.0f, -1.0f, +0.0f, // down
+                       +0.0f, -1.0f, +0.0f, // down
+                       +0.0f, -1.0f, +0.0f, // down
+                       +0.0f, -1.0f, +0.0f  // down
+       };
+
+       static const GLfloat vTexUVs[] = {
+                       // front
+                       0.0f,  1.0f,
+                       1.0f,  1.0f,
+                       0.0f,  0.0f,
+                       1.0f,  0.0f,
+                       // back
+                       0.0f,  1.0f,
+                       1.0f,  1.0f,
+                       0.0f,  0.0f,
+                       1.0f,  0.0f,
+                       // right
+                       0.0f,  1.0f,
+                       1.0f,  1.0f,
+                       0.0f,  0.0f,
+                       1.0f,  0.0f,
+                       // left
+                       0.0f,  1.0f,
+                       1.0f,  1.0f,
+                       0.0f,  0.0f,
+                       1.0f,  0.0f,
+                       // top
+                       0.0f,  1.0f,
+                       1.0f,  1.0f,
+                       0.0f,  0.0f,
+                       1.0f,  0.0f,
+                       // bottom
+                       0.0f,  1.0f,
+                       1.0f,  1.0f,
+                       0.0f,  0.0f,
+                       1.0f,  0.0f,
+       };
+
+       /* clear the color buffer */
+       glClearColor(0.5, 0.5, 0.5, 1.0);
+       glClear(GL_COLOR_BUFFER_BIT);
+
+       glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
+       glEnableVertexAttribArray(0);
+
+       glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vNormals);
+       glEnableVertexAttribArray(1);
+
+       glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, vColors);
+       glEnableVertexAttribArray(2);
+
+       glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, vTexUVs);
+       glEnableVertexAttribArray(3);
+
+       esMatrixLoadIdentity(&modelview);
+       esTranslate(&modelview, 0.0f, 0.0f, -disp_kmsc->gl.distance);
+       esRotate(&modelview, 45.0f + (0.25f * disp_kmsc->i), 1.0f, 0.0f, 0.0f);
+       esRotate(&modelview, 45.0f - (0.5f * disp_kmsc->i), 0.0f, 1.0f, 0.0f);
+       esRotate(&modelview, 10.0f + (0.15f * disp_kmsc->i), 0.0f, 0.0f, 1.0f);
+
+       GLfloat aspect = (GLfloat)(disp_kmsc->drm.mode->hdisplay) / (GLfloat)(disp_kmsc->drm.mode->vdisplay);
+
+       ESMatrix projection;
+       esMatrixLoadIdentity(&projection);
+       esPerspective(&projection, disp_kmsc->gl.fov, aspect, 1.0f, 10.0f);
+
+       ESMatrix modelviewprojection;
+       esMatrixLoadIdentity(&modelviewprojection);
+       esMatrixMultiply(&modelviewprojection, &modelview, &projection);
+
+       float normal[9];
+       normal[0] = modelview.m[0][0];
+       normal[1] = modelview.m[0][1];
+       normal[2] = modelview.m[0][2];
+       normal[3] = modelview.m[1][0];
+       normal[4] = modelview.m[1][1];
+       normal[5] = modelview.m[1][2];
+       normal[6] = modelview.m[2][0];
+       normal[7] = modelview.m[2][1];
+       normal[8] = modelview.m[2][2];
+
+       glUniformMatrix4fv(disp_kmsc->gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
+       glUniformMatrix4fv(disp_kmsc->gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
+       glUniformMatrix3fv(disp_kmsc->gl.normalmatrix, 1, GL_FALSE, normal);
+
+       glEnable(GL_CULL_FACE);
+
+       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
+}
+
+static void
+drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
+{
+       struct drm_fb *fb = data;
+//     struct gbm_device *gbm = gbm_bo_get_device(bo);
+       struct display_kmscube *disp_kmsc = fb->disp_kmsc;
+
+       if (fb->fb_id)
+               drmModeRmFB(disp_kmsc->base.fd, fb->fb_id);
+
+       free(fb);
+}
+
+static struct drm_fb * drm_fb_get_from_bo(struct display_kmscube *disp_kmsc, struct gbm_bo *bo)
+{
+       struct drm_fb *fb = gbm_bo_get_user_data(bo);
+       uint32_t width, height, stride, handle;
+       int ret;
+
+       if (fb)
+               return fb;
+
+       fb = calloc(1, sizeof(*fb));
+       fb->bo = bo;
+       fb->disp_kmsc = disp_kmsc;
+
+       width = gbm_bo_get_width(bo);
+       height = gbm_bo_get_height(bo);
+       stride = gbm_bo_get_stride(bo);
+       handle = gbm_bo_get_handle(bo).u32;
+
+       ret = drmModeAddFB(disp_kmsc->base.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
+       if (ret) {
+               ERROR("failed to create fb: %s", strerror(errno));
+               free(fb);
+               return NULL;
+       }
+
+       gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
+
+       return fb;
+}
+
+static void page_flip_handler(int fd, unsigned int frame,
+                 unsigned int sec, unsigned int usec, void *data)
+{
+       int *waiting_for_flip = data;
+       *waiting_for_flip = 0;
+}
+
+static struct omap_bo *
+alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
+               uint32_t *bo_handle, uint32_t *pitch)
+{
+       struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
+       struct omap_bo *bo;
+       uint32_t bo_flags = disp_kmsc->bo_flags;
+
+       if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
+               bo_flags &= ~OMAP_BO_TILED;
+               if (bpp == 8) {
+                       bo_flags |= OMAP_BO_TILED_8;
+               } else if (bpp == 16) {
+                       bo_flags |= OMAP_BO_TILED_16;
+               } else if (bpp == 32) {
+                       bo_flags |= OMAP_BO_TILED_32;
+               }
+       }
+
+       bo_flags |= OMAP_BO_WC;
+
+       if (bo_flags & OMAP_BO_TILED) {
+               bo = omap_bo_new_tiled(disp->dev, width, height, bo_flags);
+       } else {
+               bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
+       }
+
+       if (bo) {
+               *bo_handle = omap_bo_handle(bo);
+               *pitch = width * bpp / 8;
+               if (bo_flags & OMAP_BO_TILED)
+                       *pitch = ALIGN2(*pitch, PAGE_SHIFT);
+       }
+
+       return bo;
+}
+
+/* We allocate single planar buffers, always. This, for EGLImage. Also, we
+ * create on EGLImageKHR per buffer. */
+static struct buffer *
+alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
+{
+       struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
+       struct buffer_kmscube *buf_kmsc;
+       struct buffer *buf;
+       uint32_t bo_handles[4] = {0}, offsets[4] = {0};
+       int ret;
+
+       buf_kmsc = calloc(1, sizeof(*buf_kmsc));
+       if (!buf_kmsc) {
+               ERROR("allocation failed");
+               return NULL;
+       }
+       buf = &buf_kmsc->base;
+
+       buf->fourcc = fourcc;
+       buf->width = w;
+       buf->height = h;
+       buf->multiplanar = false;
+
+       buf->nbo = 1;
+
+       if (!fourcc)
+               fourcc = FOURCC('A','R','2','4');
+
+       switch(fourcc) {
+       case FOURCC('A','R','2','4'):
+               buf->nbo = 1;
+               buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
+                               &bo_handles[0], &buf->pitches[0]);
+               break;
+       case FOURCC('U','Y','V','Y'):
+       case FOURCC('Y','U','Y','V'):
+               buf->nbo = 1;
+               buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
+                               &bo_handles[0], &buf->pitches[0]);
+               break;
+       case FOURCC('N','V','1','2'):
+               buf->nbo = 1;
+               buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
+                               &bo_handles[0], &buf->pitches[0]);
+               break;
+       case FOURCC('I','4','2','0'):
+               buf->nbo = 1;
+               buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
+                               &bo_handles[0], &buf->pitches[0]);
+               break;
+       default:
+               ERROR("invalid format: 0x%08x", fourcc);
+               goto fail;
+       }
+
+       // Create EGLImage and return.
+       // TODO: cropping attributes when this will be supported.
+       EGLint attr[] = {
+           EGL_GL_VIDEO_FOURCC_TI,      buf->fourcc,
+           EGL_GL_VIDEO_WIDTH_TI,       buf->width,
+           EGL_GL_VIDEO_HEIGHT_TI,      buf->height,
+           EGL_GL_VIDEO_BYTE_SIZE_TI,   omap_bo_size(buf->bo[0]),
+           // TODO: pick proper YUV flags..
+           EGL_GL_VIDEO_YUV_FLAGS_TI,   EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE | EGLIMAGE_FLAGS_YUV_BT601,
+           EGL_NONE
+       };
+
+       int fd = omap_bo_dmabuf(buf->bo[0]);
+
+       buf_kmsc->egl_img =
+       disp_kmsc->gl.eglCreateImageKHR(disp_kmsc->gl.display, EGL_NO_CONTEXT,
+                                       EGL_RAW_VIDEO_TI2, (EGLClientBuffer)fd, attr);
+
+       if (buf_kmsc->egl_img == EGL_NO_IMAGE_KHR) {
+               ERROR("eglCreateImageKHR failed!\n");
+               return NULL;
+       }
+
+       return buf;
+
+fail:
+       // XXX cleanup
+       return NULL;
+}
+
+static struct buffer **
+alloc_buffers(struct display *disp, uint32_t n,
+               uint32_t fourcc, uint32_t w, uint32_t h)
+{
+       struct buffer **bufs;
+       uint32_t i = 0;
+
+       bufs = calloc(n, sizeof(*bufs));
+       if (!bufs) {
+               ERROR("allocation failed");
+               goto fail;
+       }
+
+       for (i = 0; i < n; i++) {
+               bufs[i] = alloc_buffer(disp, fourcc, w, h);
+               if (!bufs[i]) {
+                       ERROR("allocation failed");
+                       goto fail;
+               }
+       }
+
+       return bufs;
+
+fail:
+       // XXX cleanup
+       return NULL;
+}
+
+static struct buffer **
+get_buffers(struct display *disp, uint32_t n)
+{
+       MSG("get_buffers not supported!");
+       return NULL;
+}
+
+static struct buffer **
+get_vid_buffers(struct display *disp, uint32_t n,
+               uint32_t fourcc, uint32_t w, uint32_t h)
+{
+       return alloc_buffers(disp, n, fourcc, w, h);
+}
+
+static int
+post_buffer(struct display *disp, struct buffer *buf)
+{
+       ERROR("post_buffer not supported!");
+       return -1;
+}
+
+static int
+post_vid_buffer(struct display *disp, struct buffer *buf,
+               uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+       struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
+       struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
+
+//     struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
+//     int ret = 0;
+//     uint32_t i, j;
+//
+//     /* ensure we have the overlay setup: */
+//     for (i = 0; i < disp_kmsc->connectors_count; i++) {
+//             struct connector *connector = &disp_kmsc->connector[i];
+//             uint32_t used_planes = 0;
+//             drmModeModeInfo *mode = connector->mode;
+//
+//             if (! mode) {
+//                     continue;
+//             }
+//
+//             if (! disp_kmsc->ovr[i]) {
+//
+//                     for (j = 0; j < disp_kmsc->plane_resources->count_planes; j++) {
+//                             drmModePlane *ovr = drmModeGetPlane(disp->fd,
+//                                             disp_kmsc->plane_resources->planes[j]);
+//                             if ((ovr->possible_crtcs & (1 << connector->pipe)) &&
+//                                             !(used_planes & (1 << j))) {
+//                                     disp_kmsc->ovr[i] = ovr;
+//                                     used_planes |= (1 << j);
+//                                     break;
+//                             }
+//                     }
+//             }
+//
+//             if (! disp_kmsc->ovr[i]) {
+//                     MSG("Could not find plane for crtc %d", connector->crtc);
+//                     ret = -1;
+//                     /* carry on and see if we can find at least one usable plane */
+//                     continue;
+//             }
+//
+//             ret = drmModeSetPlane(disp->fd, disp_kmsc->ovr[i]->plane_id,
+//                             connector->crtc, buf_kmsc->fb_id, 0,
+//                             /* make video fullscreen: */
+//                             0, 0, mode->hdisplay, mode->vdisplay,
+//                             /* source/cropping coordinates are given in Q16 */
+//                             x << 16, y << 16, w << 16, h << 16);
+//             if (ret) {
+//                     ERROR("failed to enable plane %d: %s",
+//                                     disp_kmsc->ovr[i]->plane_id, strerror(errno));
+//             }
+//     }
+//
+//     return ret;
+
+       // TODO: For now, draw cube...
+
+       fd_set fds;
+       drmEventContext evctx = {
+                       .version = DRM_EVENT_CONTEXT_VERSION,
+                       .page_flip_handler = page_flip_handler,
+       };
+       struct gbm_bo *bo;
+       struct drm_fb *fb;
+       int ret;
+       struct gbm_bo *next_bo;
+       int waiting_for_flip = 1;
+
+       FD_ZERO(&fds);
+       FD_SET(0, &fds);
+       FD_SET(disp_kmsc->base.fd, &fds);
+
+       // Update video texture / EGL Image.
+        disp_kmsc->gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->egl_img);
+
+       if (glGetError() != GL_NO_ERROR) {
+               ERROR("glEGLImageTargetTexture2DOES!\n");
+               return -1;
+       }
+
+       // Draw cube.
+       draw(disp_kmsc);
+       (disp_kmsc->i)++;
+
+       eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
+       next_bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
+       fb = drm_fb_get_from_bo(disp_kmsc, next_bo);
+
+       /*
+        * Here you could also update drm plane layers if you want
+        * hw composition
+        */
+
+       ret = drmModePageFlip(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id,
+                       DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
+       if (ret) {
+               ERROR("failed to queue page flip: %s\n", strerror(errno));
+               return -1;
+       }
+
+       while (waiting_for_flip) {
+               ret = select(disp_kmsc->base.fd + 1, &fds, NULL, NULL, NULL);
+               if (ret < 0) {
+                       ERROR("select err: %s\n", strerror(errno));
+                       return ret;
+               } else if (ret == 0) {
+                       ERROR("select timeout!\n");
+                       return -1;
+               } else if (FD_ISSET(0, &fds)) {
+                       ERROR("user interrupted!\n");
+                       break;
+               }
+               drmHandleEvent(disp_kmsc->base.fd, &evctx);
+       }
+
+       /* release last buffer to render on again: */
+       gbm_surface_release_buffer(disp_kmsc->gbm.surface, bo);
+       bo = next_bo;
+
+       return 0;
+}
+
+static void
+close_kmscube(struct display *disp)
+{
+}
+
+void
+disp_kmscube_usage(void)
+{
+       MSG("KMSCUBE Display Options:");
+       MSG("\t--distance <float>\tset cube distance (default 8.0)");
+       MSG("\t--fov <float>\tset field of vision (default 45.0)");
+       MSG("\t--kmscube\tEnable display kmscube (default: disabled)");
+}
+
+struct display *
+disp_kmscube_open(int argc, char **argv)
+{
+       struct display_kmscube *disp_kmsc = NULL;
+       struct display *disp;
+       struct gbm_bo *bo;
+       struct drm_fb *fb;
+       int ret, i, enabled = 0;
+       float fov = 45, distance = 8;
+
+       /* note: set args to NULL after we've parsed them so other modules know
+        * that it is already parsed (since the arg parsing is decentralized)
+        */
+       for (i = 1; i < argc; i++) {
+               if (!argv[i]) {
+                       continue;
+               }
+               if (!strcmp("--distance", argv[i])) {
+                       argv[i++] = NULL;
+                       if (sscanf(argv[i], "%f", &distance) != 1) {
+                               ERROR("invalid arg: %s", argv[i]);
+                               goto fail;
+                       }
+               } else if (!strcmp("--fov", argv[i])) {
+                       argv[i++] = NULL;
+                       if (sscanf(argv[i], "%f", &fov) != 1) {
+                               ERROR("invalid arg: %s", argv[i]);
+                               goto fail;
+                       }
+               } else if (!strcmp("--kmscube", argv[i])) {
+                       enabled = 1;
+               } else {
+                       /* ignore */
+                       continue;
+               }
+               argv[i] = NULL;
+       }
+
+       // If not explicitely enabled from command line, fail (and fallback to disp-kms).
+       if (!enabled)
+               goto fail;
+
+       disp_kmsc = calloc(1, sizeof(*disp_kmsc));
+       if (!disp_kmsc) {
+               ERROR("allocation failed");
+               goto fail;
+       }
+       disp_kmsc->gl.distance = distance;
+       disp_kmsc->gl.fov = fov;
+       disp = &disp_kmsc->base;
+
+       disp->fd = drmOpen("omapdrm", NULL);
+       if (disp->fd < 0) {
+               ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
+               goto fail;
+       }
+
+       disp->dev = omap_device_new(disp->fd);
+       if (!disp->dev) {
+               ERROR("couldn't create device");
+               goto fail;
+       }
+
+       disp->get_buffers = get_buffers;
+       disp->get_vid_buffers = get_vid_buffers;
+       disp->post_buffer = post_buffer;
+       disp->post_vid_buffer = post_vid_buffer;
+       disp->close = close_kmscube;
+
+       if (init_drm(disp_kmsc)) {
+               ERROR("couldn't init drm");
+               goto fail;
+       }
+
+       disp_kmsc->drm.plane_resources = drmModeGetPlaneResources(disp->fd);
+       if (!disp_kmsc->drm.plane_resources) {
+               ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
+               goto fail;
+       }
+
+       if (init_gbm(disp_kmsc)) {
+               ERROR("couldn't init gbm");
+               goto fail;
+       }
+
+       if (init_gl(disp_kmsc)) {
+               ERROR("couldn't init gl(es)");
+               goto fail;
+       }
+
+       /* clear the color buffer */
+       glClearColor(0.5, 0.5, 0.5, 1.0);
+       glClear(GL_COLOR_BUFFER_BIT);
+       eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
+       bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
+       fb = drm_fb_get_from_bo(disp_kmsc, bo);
+
+       /* set mode: */
+       ret = drmModeSetCrtc(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id, 0, 0,
+                       &disp_kmsc->drm.connector_id, 1, disp_kmsc->drm.mode);
+       if (ret) {
+               ERROR("failed to set mode: %s\n", strerror(errno));
+               return ret;
+       }
+
+       disp->width = 0;
+       disp->height = 0;
+       disp->multiplanar = false;
+//     for (i = 0; i < (int)disp_kmsc->connectors_count; i++) {
+//             struct connector *c = &disp_kmsc->connector[i];
+//             connector_find_mode(disp, c);
+//             if (c->mode == NULL)
+//                     continue;
+//             /* setup side-by-side virtual display */
+//             disp->width += c->mode->hdisplay;
+//             if (disp->height < c->mode->vdisplay) {
+//                     disp->height = c->mode->vdisplay;
+//             }
+//     }
+//
+//     MSG("using %d connectors, %dx%d display, multiplanar: %d",
+//                     disp_kmsc->connectors_count, disp->width, disp->height, disp->multiplanar);
+
+       return disp;
+
+fail:
+       // XXX cleanup
+       return NULL;
+}
diff --git a/util/esTransform.c b/util/esTransform.c
new file mode 100644 (file)
index 0000000..5fff0c9
--- /dev/null
@@ -0,0 +1,235 @@
+//
+// Book:      OpenGL(R) ES 2.0 Programming Guide
+// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+// ISBN-10:   0321502795
+// ISBN-13:   9780321502797
+// Publisher: Addison-Wesley Professional
+// URLs:      http://safari.informit.com/9780321563835
+//            http://www.opengles-book.com
+//
+
+/*
+ * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+// ESUtil.c
+//
+//    A utility library for OpenGL ES.  This library provides a
+//    basic common framework for the example applications in the
+//    OpenGL ES 2.0 Programming Guide.
+//
+
+///
+//  Includes
+//
+#include "esUtil.h"
+#include <math.h>
+#include <string.h>
+
+#define PI 3.1415926535897932384626433832795f
+
+void ESUTIL_API
+esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz)
+{
+    result->m[0][0] *= sx;
+    result->m[0][1] *= sx;
+    result->m[0][2] *= sx;
+    result->m[0][3] *= sx;
+
+    result->m[1][0] *= sy;
+    result->m[1][1] *= sy;
+    result->m[1][2] *= sy;
+    result->m[1][3] *= sy;
+
+    result->m[2][0] *= sz;
+    result->m[2][1] *= sz;
+    result->m[2][2] *= sz;
+    result->m[2][3] *= sz;
+}
+
+void ESUTIL_API
+esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz)
+{
+    result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz);
+    result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz);
+    result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz);
+    result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz);
+}
+
+void ESUTIL_API
+esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+{
+   GLfloat sinAngle, cosAngle;
+   GLfloat mag = sqrtf(x * x + y * y + z * z);
+      
+   sinAngle = sinf ( angle * PI / 180.0f );
+   cosAngle = cosf ( angle * PI / 180.0f );
+   if ( mag > 0.0f )
+   {
+      GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
+      GLfloat oneMinusCos;
+      ESMatrix rotMat;
+   
+      x /= mag;
+      y /= mag;
+      z /= mag;
+
+      xx = x * x;
+      yy = y * y;
+      zz = z * z;
+      xy = x * y;
+      yz = y * z;
+      zx = z * x;
+      xs = x * sinAngle;
+      ys = y * sinAngle;
+      zs = z * sinAngle;
+      oneMinusCos = 1.0f - cosAngle;
+
+      rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
+      rotMat.m[0][1] = (oneMinusCos * xy) - zs;
+      rotMat.m[0][2] = (oneMinusCos * zx) + ys;
+      rotMat.m[0][3] = 0.0F; 
+
+      rotMat.m[1][0] = (oneMinusCos * xy) + zs;
+      rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
+      rotMat.m[1][2] = (oneMinusCos * yz) - xs;
+      rotMat.m[1][3] = 0.0F;
+
+      rotMat.m[2][0] = (oneMinusCos * zx) - ys;
+      rotMat.m[2][1] = (oneMinusCos * yz) + xs;
+      rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
+      rotMat.m[2][3] = 0.0F; 
+
+      rotMat.m[3][0] = 0.0F;
+      rotMat.m[3][1] = 0.0F;
+      rotMat.m[3][2] = 0.0F;
+      rotMat.m[3][3] = 1.0F;
+
+      esMatrixMultiply( result, &rotMat, result );
+   }
+}
+
+void ESUTIL_API
+esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ)
+{
+    float       deltaX = right - left;
+    float       deltaY = top - bottom;
+    float       deltaZ = farZ - nearZ;
+    ESMatrix    frust;
+
+    if ( (nearZ <= 0.0f) || (farZ <= 0.0f) ||
+         (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) )
+         return;
+
+    frust.m[0][0] = 2.0f * nearZ / deltaX;
+    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
+
+    frust.m[1][1] = 2.0f * nearZ / deltaY;
+    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
+
+    frust.m[2][0] = (right + left) / deltaX;
+    frust.m[2][1] = (top + bottom) / deltaY;
+    frust.m[2][2] = -(nearZ + farZ) / deltaZ;
+    frust.m[2][3] = -1.0f;
+
+    frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
+    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
+
+    esMatrixMultiply(result, &frust, result);
+}
+
+
+void ESUTIL_API 
+esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ)
+{
+   GLfloat frustumW, frustumH;
+   
+   frustumH = tanf( fovy / 360.0f * PI ) * nearZ;
+   frustumW = frustumH * aspect;
+
+   esFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ );
+}
+
+void ESUTIL_API
+esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ)
+{
+    float       deltaX = right - left;
+    float       deltaY = top - bottom;
+    float       deltaZ = farZ - nearZ;
+    ESMatrix    ortho;
+
+    if ( (deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f) )
+        return;
+
+    esMatrixLoadIdentity(&ortho);
+    ortho.m[0][0] = 2.0f / deltaX;
+    ortho.m[3][0] = -(right + left) / deltaX;
+    ortho.m[1][1] = 2.0f / deltaY;
+    ortho.m[3][1] = -(top + bottom) / deltaY;
+    ortho.m[2][2] = -2.0f / deltaZ;
+    ortho.m[3][2] = -(nearZ + farZ) / deltaZ;
+
+    esMatrixMultiply(result, &ortho, result);
+}
+
+
+void ESUTIL_API
+esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB)
+{
+    ESMatrix    tmp;
+    int         i;
+
+       for (i=0; i<4; i++)
+       {
+               tmp.m[i][0] =   (srcA->m[i][0] * srcB->m[0][0]) +
+                                               (srcA->m[i][1] * srcB->m[1][0]) +
+                                               (srcA->m[i][2] * srcB->m[2][0]) +
+                                               (srcA->m[i][3] * srcB->m[3][0]) ;
+
+               tmp.m[i][1] =   (srcA->m[i][0] * srcB->m[0][1]) + 
+                                               (srcA->m[i][1] * srcB->m[1][1]) +
+                                               (srcA->m[i][2] * srcB->m[2][1]) +
+                                               (srcA->m[i][3] * srcB->m[3][1]) ;
+
+               tmp.m[i][2] =   (srcA->m[i][0] * srcB->m[0][2]) + 
+                                               (srcA->m[i][1] * srcB->m[1][2]) +
+                                               (srcA->m[i][2] * srcB->m[2][2]) +
+                                               (srcA->m[i][3] * srcB->m[3][2]) ;
+
+               tmp.m[i][3] =   (srcA->m[i][0] * srcB->m[0][3]) + 
+                                               (srcA->m[i][1] * srcB->m[1][3]) +
+                                               (srcA->m[i][2] * srcB->m[2][3]) +
+                                               (srcA->m[i][3] * srcB->m[3][3]) ;
+       }
+    memcpy(result, &tmp, sizeof(ESMatrix));
+}
+
+
+void ESUTIL_API
+esMatrixLoadIdentity(ESMatrix *result)
+{
+    memset(result, 0x0, sizeof(ESMatrix));
+    result->m[0][0] = 1.0f;
+    result->m[1][1] = 1.0f;
+    result->m[2][2] = 1.0f;
+    result->m[3][3] = 1.0f;
+}
+
diff --git a/util/esUtil.h b/util/esUtil.h
new file mode 100644 (file)
index 0000000..c2d7c1d
--- /dev/null
@@ -0,0 +1,303 @@
+//
+// Book:      OpenGL(R) ES 2.0 Programming Guide
+// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+// ISBN-10:   0321502795
+// ISBN-13:   9780321502797
+// Publisher: Addison-Wesley Professional
+// URLs:      http://safari.informit.com/9780321563835
+//            http://www.opengles-book.com
+//
+
+/*
+ * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+//
+/// \file ESUtil.h
+/// \brief A utility library for OpenGL ES.  This library provides a
+///        basic common framework for the example applications in the
+///        OpenGL ES 2.0 Programming Guide.
+//
+#ifndef ESUTIL_H
+#define ESUTIL_H
+
+///
+//  Includes
+//
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+#ifdef __cplusplus
+
+extern "C" {
+#endif
+
+   
+///
+//  Macros
+//
+#define ESUTIL_API
+#define ESCALLBACK
+
+
+/// esCreateWindow flag - RGB color buffer
+#define ES_WINDOW_RGB           0
+/// esCreateWindow flag - ALPHA color buffer
+#define ES_WINDOW_ALPHA         1 
+/// esCreateWindow flag - depth buffer
+#define ES_WINDOW_DEPTH         2 
+/// esCreateWindow flag - stencil buffer
+#define ES_WINDOW_STENCIL       4
+/// esCreateWindow flat - multi-sample buffer
+#define ES_WINDOW_MULTISAMPLE   8
+
+
+///
+// Types
+//
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+typedef struct
+{
+    GLfloat   m[4][4];
+} ESMatrix;
+
+typedef struct _escontext
+{
+   /// Put your user data here...
+   void*       userData;
+
+   /// Window width
+   GLint       width;
+
+   /// Window height
+   GLint       height;
+
+   /// Window handle
+   EGLNativeWindowType  hWnd;
+
+   /// EGL display
+   EGLDisplay  eglDisplay;
+      
+   /// EGL context
+   EGLContext  eglContext;
+
+   /// EGL surface
+   EGLSurface  eglSurface;
+
+   /// Callbacks
+   void (ESCALLBACK *drawFunc) ( struct _escontext * );
+   void (ESCALLBACK *keyFunc) ( struct _escontext *, unsigned char, int, int );
+   void (ESCALLBACK *updateFunc) ( struct _escontext *, float deltaTime );
+} ESContext;
+
+
+///
+//  Public Functions
+//
+
+//
+///
+/// \brief Initialize ES framework context.  This must be called before calling any other functions.
+/// \param esContext Application context
+//
+void ESUTIL_API esInitContext ( ESContext *esContext );
+
+//
+/// \brief Create a window with the specified parameters
+/// \param esContext Application context
+/// \param title Name for title bar of window
+/// \param width Width in pixels of window to create
+/// \param height Height in pixels of window to create
+/// \param flags Bitfield for the window creation flags 
+///         ES_WINDOW_RGB     - specifies that the color buffer should have R,G,B channels
+///         ES_WINDOW_ALPHA   - specifies that the color buffer should have alpha
+///         ES_WINDOW_DEPTH   - specifies that a depth buffer should be created
+///         ES_WINDOW_STENCIL - specifies that a stencil buffer should be created
+///         ES_WINDOW_MULTISAMPLE - specifies that a multi-sample buffer should be created
+/// \return GL_TRUE if window creation is succesful, GL_FALSE otherwise
+GLboolean ESUTIL_API esCreateWindow ( ESContext *esContext, const char *title, GLint width, GLint height, GLuint flags );
+
+//
+/// \brief Start the main loop for the OpenGL ES application
+/// \param esContext Application context
+//
+void ESUTIL_API esMainLoop ( ESContext *esContext );
+
+//
+/// \brief Register a draw callback function to be used to render each frame
+/// \param esContext Application context
+/// \param drawFunc Draw callback function that will be used to render the scene
+//
+void ESUTIL_API esRegisterDrawFunc ( ESContext *esContext, void (ESCALLBACK *drawFunc) ( ESContext* ) );
+
+//
+/// \brief Register an update callback function to be used to update on each time step
+/// \param esContext Application context
+/// \param updateFunc Update callback function that will be used to render the scene
+//
+void ESUTIL_API esRegisterUpdateFunc ( ESContext *esContext, void (ESCALLBACK *updateFunc) ( ESContext*, float ) );
+
+//
+/// \brief Register an keyboard input processing callback function
+/// \param esContext Application context
+/// \param keyFunc Key callback function for application processing of keyboard input
+//
+void ESUTIL_API esRegisterKeyFunc ( ESContext *esContext, 
+                                    void (ESCALLBACK *drawFunc) ( ESContext*, unsigned char, int, int ) );
+//
+/// \brief Log a message to the debug output for the platform
+/// \param formatStr Format string for error log.  
+//
+void ESUTIL_API esLogMessage ( const char *formatStr, ... );
+
+//
+///
+/// \brief Load a shader, check for compile errors, print error messages to output log
+/// \param type Type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER)
+/// \param shaderSrc Shader source string
+/// \return A new shader object on success, 0 on failure
+//
+GLuint ESUTIL_API esLoadShader ( GLenum type, const char *shaderSrc );
+
+//
+///
+/// \brief Load a vertex and fragment shader, create a program object, link program.
+///        Errors output to log.
+/// \param vertShaderSrc Vertex shader source code
+/// \param fragShaderSrc Fragment shader source code
+/// \return A new program object linked with the vertex/fragment shader pair, 0 on failure
+//
+GLuint ESUTIL_API esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc );
+
+
+//
+/// \brief Generates geometry for a sphere.  Allocates memory for the vertex data and stores 
+///        the results in the arrays.  Generate index list for a TRIANGLE_STRIP
+/// \param numSlices The number of slices in the sphere
+/// \param vertices If not NULL, will contain array of float3 positions
+/// \param normals If not NULL, will contain array of float3 normals
+/// \param texCoords If not NULL, will contain array of float2 texCoords
+/// \param indices If not NULL, will contain the array of indices for the triangle strip
+/// \return The number of indices required for rendering the buffers (the number of indices stored in the indices array
+///         if it is not NULL ) as a GL_TRIANGLE_STRIP
+//
+int ESUTIL_API esGenSphere ( int numSlices, float radius, GLfloat **vertices, GLfloat **normals, 
+                             GLfloat **texCoords, GLuint **indices );
+
+//
+/// \brief Generates geometry for a cube.  Allocates memory for the vertex data and stores 
+///        the results in the arrays.  Generate index list for a TRIANGLES
+/// \param scale The size of the cube, use 1.0 for a unit cube.
+/// \param vertices If not NULL, will contain array of float3 positions
+/// \param normals If not NULL, will contain array of float3 normals
+/// \param texCoords If not NULL, will contain array of float2 texCoords
+/// \param indices If not NULL, will contain the array of indices for the triangle strip
+/// \return The number of indices required for rendering the buffers (the number of indices stored in the indices array
+///         if it is not NULL ) as a GL_TRIANGLES
+//
+int ESUTIL_API esGenCube ( float scale, GLfloat **vertices, GLfloat **normals, 
+                           GLfloat **texCoords, GLuint **indices );
+
+//
+/// \brief Loads a 24-bit TGA image from a file
+/// \param fileName Name of the file on disk
+/// \param width Width of loaded image in pixels
+/// \param height Height of loaded image in pixels
+///  \return Pointer to loaded image.  NULL on failure. 
+//
+char* ESUTIL_API esLoadTGA ( char *fileName, int *width, int *height );
+
+
+//
+/// \brief multiply matrix specified by result with a scaling matrix and return new matrix in result
+/// \param result Specifies the input matrix.  Scaled matrix is returned in result.
+/// \param sx, sy, sz Scale factors along the x, y and z axes respectively
+//
+void ESUTIL_API esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz);
+
+//
+/// \brief multiply matrix specified by result with a translation matrix and return new matrix in result
+/// \param result Specifies the input matrix.  Translated matrix is returned in result.
+/// \param tx, ty, tz Scale factors along the x, y and z axes respectively
+//
+void ESUTIL_API esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz);
+
+//
+/// \brief multiply matrix specified by result with a rotation matrix and return new matrix in result
+/// \param result Specifies the input matrix.  Rotated matrix is returned in result.
+/// \param angle Specifies the angle of rotation, in degrees.
+/// \param x, y, z Specify the x, y and z coordinates of a vector, respectively
+//
+void ESUTIL_API esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+
+//
+// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result
+/// \param result Specifies the input matrix.  new matrix is returned in result.
+/// \param left, right Coordinates for the left and right vertical clipping planes
+/// \param bottom, top Coordinates for the bottom and top horizontal clipping planes
+/// \param nearZ, farZ Distances to the near and far depth clipping planes.  Both distances must be positive.
+//
+void ESUTIL_API esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ);
+
+//
+/// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result
+/// \param result Specifies the input matrix.  new matrix is returned in result.
+/// \param fovy Field of view y angle in degrees
+/// \param aspect Aspect ratio of screen
+/// \param nearZ Near plane distance
+/// \param farZ Far plane distance
+//
+void ESUTIL_API esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ);
+
+//
+/// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result
+/// \param result Specifies the input matrix.  new matrix is returned in result.
+/// \param left, right Coordinates for the left and right vertical clipping planes
+/// \param bottom, top Coordinates for the bottom and top horizontal clipping planes
+/// \param nearZ, farZ Distances to the near and far depth clipping planes.  These values are negative if plane is behind the viewer
+//
+void ESUTIL_API esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ);
+
+//
+/// \brief perform the following operation - result matrix = srcA matrix * srcB matrix
+/// \param result Returns multiplied matrix
+/// \param srcA, srcB Input matrices to be multiplied
+//
+void ESUTIL_API esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB);
+
+//
+//// \brief return an indentity matrix 
+//// \param result returns identity matrix
+//
+void ESUTIL_API esMatrixLoadIdentity(ESMatrix *result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ESUTIL_H
index 0b9e0eb984ff69fc19568d4cd19ff70d6375f41d..0c260f2f707c82a363b649a2324b15fb942e30fa 100644 (file)
@@ -35,6 +35,11 @@ struct display * disp_x11_open(int argc, char **argv);
 void disp_x11_close(struct display *disp);
 #endif
 
+#ifdef HAVE_KMSCUBE
+void disp_kmscube_usage(void);
+struct display * disp_kmscube_open(int argc, char **argv);
+#endif
+
 void
 disp_usage(void)
 {
@@ -45,6 +50,9 @@ disp_usage(void)
 
 #ifdef HAVE_X11
        disp_x11_usage();
+#endif
+#ifdef HAVE_KMSCUBE
+       disp_kmscube_usage();
 #endif
        disp_kms_usage();
 }
@@ -100,6 +108,11 @@ disp_open(int argc, char **argv)
        if (disp)
                goto out;
 #endif
+#ifdef HAVE_KMSCUBE
+       disp = disp_kmscube_open(argc, argv);
+       if (disp)
+               goto out;
+#endif
 
        disp = disp_kms_open(argc, argv);