summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 04da689)
raw | patch | inline | side by side (parent: 04da689)
author | Nikhil Devshatwar <nikhil.nd@ti.com> | |
Wed, 3 Sep 2014 07:17:31 +0000 (12:47 +0530) | ||
committer | Nikhil 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>
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 | patch | blob | history |
diff --git a/util/display-kmscube.c b/util/display-kmscube.c
index 11a9c8659437aae916afd809d256d5ccc606d49e..9a4d00aaaecd21c3e528d0ee562e8f8e6d8d10e8 100644 (file)
--- a/util/display-kmscube.c
+++ b/util/display-kmscube.c
#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);
//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. */
struct buffer base;
uint32_t fb_id;
EGLImageKHR egl_img;
+ GLuint texture_name;
+ int face_no;
};
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;
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
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
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) {
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:
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;
}
}
+ /*
+ * 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;
{
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 = {
};
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
{
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;
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);
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;