]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/omapdrmtest.git/commitdiff
display-kmscube: Render multiple videos on cube faces
authorNikhil Devshatwar <nikhil.nd@ti.com>
Wed, 3 Sep 2014 07:17:31 +0000 (12:47 +0530)
committerNikhil Devshatwar <nikhil.nd@ti.com>
Wed, 1 Oct 2014 08:38:31 +0000 (14:08 +0530)
Earlier implementation renders only one video on all the 6 faces of cube.
This patch enhances the library by adding support for:

* Using multiple textures
    - Create an egl image and a texture once for every buffer object
    - Extend buffer_kmscube to store the texture name and the associated face.
    - All buffers allocated by one call to disp_get_vid_buffers are grouped
      for one face

* Multithreaded post_vid_buffer
    - As the buffers would be posted by multiple threads asynchronously,
      post_vid_buffer would just update the buffer to be used for next
      rendereing and wait
    - Extend display_kmscube to store the current buffer for each face
    - If the video threads are less than 6, then the textures are repeated
      on the multiple faces.
    - Also, the post_vid_buffer waits till the buffer is being used by the
      GPU for texturing the face.

* Render thread
    - disp_open starts a render thread which initializes gbm and gl
    - The same thread will continue in a loop, where it reads from the
      current buffers array and updates the textures accordingly.
    - The textures are created from this thread if the buffer is being
      rendered first time (it won't have valid texture name)
    - This ensures that all the gl calls are called from a single thread

Signed-off-by: Nikhil Devshatwar <nikhil.nd@ti.com>
util/display-kmscube.c

index 11a9c8659437aae916afd809d256d5ccc606d49e..9a4d00aaaecd21c3e528d0ee562e8f8e6d8d10e8 100644 (file)
 #include <EGL/eglext.h>
 #include <GLES/gl.h>
 #include <GLES/glext.h>
+#include <pthread.h>
 
 #include "esUtil.h"
 
+#define MAX_FACES 6
+
 typedef EGLImageKHR (eglCreateImageKHR_t)(EGLDisplay dpy, EGLContext ctx,
                        EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
 
@@ -121,6 +124,12 @@ struct display_kmscube {
 
        //user specified connector id
        uint32_t user_connector_id;
+
+       int num_faces;
+       struct buffer *current_buffers[MAX_FACES];
+       pthread_t renderThread;
+       pthread_mutex_t lock[MAX_FACES];
+       pthread_mutex_t init_lock;
 };
 
 /* All our buffers are only vid buffers, and they all have an EGLImage. */
@@ -129,6 +138,8 @@ struct buffer_kmscube {
        struct buffer base;
        uint32_t fb_id;
        EGLImageKHR egl_img;
+       GLuint texture_name;
+       int face_no;
 };
 
 struct drm_fb {
@@ -137,6 +148,10 @@ struct drm_fb {
        uint32_t fb_id;
 };
 
+static void *render_thread(void *data);
+static int create_texture(struct display_kmscube *disp_kmsc, struct buffer *buf);
+static int get_texture(struct display_kmscube *disp_kmsc, int face);
+
 static int init_drm(struct display_kmscube *disp_kmsc)
 {
        drmModeRes *resources;
@@ -426,29 +441,18 @@ static int init_gl(struct display_kmscube *disp_kmsc)
        disp_kmsc->gl.modelviewprojectionmatrix =
                glGetUniformLocation(disp_kmsc->gl.program, "modelviewprojectionMatrix");
        disp_kmsc->gl.normalmatrix = glGetUniformLocation(disp_kmsc->gl.program, "normalMatrix");
+       disp_kmsc->gl.uniform_texture = glGetUniformLocation(disp_kmsc->gl.program, "uniform_texture");
 
        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);
+       /* Textures would be created at runtime by render thread */
 
        return 0;
 }
 
 static void draw(struct display_kmscube *disp_kmsc)
 {
+       int face, tex_name;
        ESMatrix modelview;
        static const GLfloat vVertices[] = {
                        // front
@@ -631,12 +635,15 @@ static void draw(struct display_kmscube *disp_kmsc)
 
        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);
+       for(face=0; face<MAX_FACES; face++) {
+               glActiveTexture(GL_TEXTURE0);
+               tex_name = get_texture(disp_kmsc, face);
+               glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex_name);
+               if (glGetError() != GL_NO_ERROR)
+                       printf("glBindTexture failed render for face %d!\n", face);
+               glUniform1i(disp_kmsc->gl.uniform_texture, 0);
+               glDrawArrays(GL_TRIANGLE_STRIP, face * 4, 4);
+       }
 }
 
 static void
@@ -735,7 +742,6 @@ alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
        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) {
@@ -796,29 +802,11 @@ alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
                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_TI_DMABUF, (EGLClientBuffer)fd, attr);
-
-       if (buf_kmsc->egl_img == EGL_NO_IMAGE_KHR) {
-               ERROR("eglCreateImageKHR failed!\n");
-               return NULL;
-       }
-
+       buf_kmsc->face_no = disp_kmsc->num_faces;
+       /*
+        * EGL image for this buffer would be created by the render thread
+        * Nothing to be done here
+        */
        return buf;
 
 fail:
@@ -830,6 +818,7 @@ static struct buffer **
 alloc_buffers(struct display *disp, uint32_t n,
                uint32_t fourcc, uint32_t w, uint32_t h)
 {
+       struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
        struct buffer **bufs;
        uint32_t i = 0;
 
@@ -847,6 +836,11 @@ alloc_buffers(struct display *disp, uint32_t n,
                }
        }
 
+       /*
+        * All buffers allocated with one call to get_vid_buffers are marked
+        * with the same face number
+        */
+       disp_kmsc->num_faces++;
        disp->buf = bufs;
        return bufs;
 
@@ -901,57 +895,25 @@ post_vid_buffer(struct display *disp, struct buffer *buf,
 {
        struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
        struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
+       int face;
+
+       face = buf_kmsc->face_no;
+       /*
+        * No gl_ calls here, just update the buffer to be used for the
+        * the specific face to which this buffer belongs
+        */
+
+       pthread_mutex_lock(&disp_kmsc->lock[face]);
+       disp_kmsc->current_buffers[face] = buf;
+       pthread_mutex_unlock(&disp_kmsc->lock[face]);
+
+       return 0;
+}
 
-//     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...
+static void *
+render_thread(void *data)
+{
+       struct display_kmscube *disp_kmsc = to_display_kmscube(data);
 
        fd_set fds;
        drmEventContext evctx = {
@@ -960,62 +922,170 @@ post_vid_buffer(struct display *disp, struct buffer *buf,
        };
        struct gbm_bo *bo;
        struct drm_fb *fb;
-       int ret;
+       int ret, face;
        struct gbm_bo *next_bo;
        int waiting_for_flip = 1;
 
-       FD_ZERO(&fds);
-       FD_SET(0, &fds);
-       FD_SET(disp_kmsc->base.fd, &fds);
+       if (init_gbm(disp_kmsc)) {
+               ERROR("couldn't init gbm");
+               return NULL;
+       }
+       if (init_gl(disp_kmsc)) {
+               ERROR("couldn't init gl(es)");
+               return NULL;
+       }
 
-       // Update video texture / EGL Image.
-        disp_kmsc->gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->egl_img);
+       /* 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);
 
-       if (glGetError() != GL_NO_ERROR) {
-               ERROR("glEGLImageTargetTexture2DOES!\n");
-               return -1;
+       /* 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 NULL;
        }
 
-       // Draw cube.
-       draw(disp_kmsc);
-       (disp_kmsc->i)++;
+       pthread_mutex_unlock(&disp_kmsc->init_lock);
 
-       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);
+       while(1) {
 
-       /*
-        * Here you could also update drm plane layers if you want
-        * hw composition
-        */
+               for(face = 0; face < MAX_FACES; face++) {
+                       pthread_mutex_lock(&disp_kmsc->lock[face]);
+               }
 
-       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;
-       }
+               FD_ZERO(&fds);
+               FD_SET(disp_kmsc->base.fd, &fds);
 
-       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;
+               // 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);
+
+               for(face = 0; face < MAX_FACES; face++) {
+                       pthread_mutex_unlock(&disp_kmsc->lock[face]);
+               }
+
+               /*
+                * 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 NULL;
+               }
+
+               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 NULL;
+                       } else if (ret == 0) {
+                               ERROR("select timeout!\n");
+                               return NULL;
+                       } else if (FD_ISSET(0, &fds)) {
+                               ERROR("user interrupted!\n");
+                               break;
+                       }
+                       drmHandleEvent(disp_kmsc->base.fd, &evctx);
                }
-               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;
+
+               waiting_for_flip = 1;
+
+       }
+       return data;
+}
+
+static int
+get_texture(struct display_kmscube *disp_kmsc, int face)
+{
+
+       struct buffer *buf;
+       struct buffer_kmscube *buf_kmsc;
+       int num_faces;
+
+       num_faces = disp_kmsc->num_faces ? disp_kmsc->num_faces : 1;
+       face = face % num_faces;
+       buf = disp_kmsc->current_buffers[face];
+
+       if(buf == NULL) {
+               return 0;
+       }
+
+       buf_kmsc = to_buffer_kmscube(buf);
+       if(buf_kmsc->texture_name == 0) {
+               create_texture(disp_kmsc, buf);
+       }
+
+       return buf_kmsc->texture_name;
+}
+
+static int
+create_texture(struct display_kmscube *disp_kmsc, struct buffer *buf)
+{
+
+       struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
+       // 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]),
+           EGL_GL_VIDEO_BYTE_STRIDE_TI,   buf->pitches[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_TI_DMABUF, (EGLClientBuffer)fd, attr);
+
+       if (buf_kmsc->egl_img == EGL_NO_IMAGE_KHR) {
+               ERROR("eglCreateImageKHR failed!\n");
+               goto fail;
        }
 
-       /* release last buffer to render on again: */
-       gbm_surface_release_buffer(disp_kmsc->gbm.surface, bo);
-       bo = next_bo;
+       // Texture.
+       glGenTextures(1, &buf_kmsc->texture_name);
+       glBindTexture(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->texture_name);
+
+       if (glGetError() != GL_NO_ERROR) {
+               ERROR("glBindTexture!");
+               goto fail;
+       }
 
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       // 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");
+               goto fail;
+       }
        return 0;
+
+fail:
+       return -1;
 }
 
 static void
@@ -1038,8 +1108,6 @@ 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, connector_id = 4;
 
@@ -1091,6 +1159,11 @@ disp_kmscube_open(int argc, char **argv)
        disp_kmsc->user_connector_id = connector_id;
        disp = &disp_kmsc->base;
 
+       for (i=0; i<MAX_FACES; i++) {
+               pthread_mutex_init(&disp_kmsc->lock[i], NULL);
+       }
+       pthread_mutex_init(&disp_kmsc->init_lock, NULL);
+
        disp->fd = drmOpen("omapdrm", NULL);
        if (disp->fd < 0) {
                ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
@@ -1122,48 +1195,27 @@ disp_kmscube_open(int argc, char **argv)
                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;
-       }
+       disp->width = 0;
+       disp->height = 0;
+       disp->multiplanar = false;
 
-       /* 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);
+       /*
+        * Initialize everything from a render thread.
+        */
+       pthread_mutex_lock(&disp_kmsc->init_lock);
 
-       /* 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;
+       ret = pthread_create(&disp_kmsc->renderThread, NULL,
+                       render_thread, disp);
+       if(ret) {
+               ERROR("Failed creating Render Thread: %s", strerror(errno));
        }
 
-       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);
+       /*
+        * Wait till initialization is complete from render thread
+        */
+       pthread_mutex_lock(&disp_kmsc->init_lock);
+       MSG("Display initialized, Render thread created");
+       pthread_mutex_unlock(&disp_kmsc->init_lock);
 
        return disp;