summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 02f22a8)
raw | patch | inline | side by side (parent: 02f22a8)
author | Subhajit Paul <subhajit_paul@ti.com> | |
Tue, 10 Apr 2018 15:54:09 +0000 (21:24 +0530) | ||
committer | Anand Balagopalakrishnan <anandb@ti.com> | |
Tue, 10 Apr 2018 15:54:09 +0000 (21:24 +0530) |
Multi-threaded EGL is always a very tricky operation. This application shows
how to use GBM EGL or Wayland EGL for correct multi-threaded operation on TI
DRA7xx SoC.
Signed-off-by: Subhajit Paul <subhajit_paul@ti.com>
Signed-off-by: Anand Balagopalakrishnan <anandb@ti.com>
how to use GBM EGL or Wayland EGL for correct multi-threaded operation on TI
DRA7xx SoC.
Signed-off-by: Subhajit Paul <subhajit_paul@ti.com>
Signed-off-by: Anand Balagopalakrishnan <anandb@ti.com>
13 files changed:
egl-multi-thread/Makefile | [new file with mode: 0644] | patch | blob |
egl-multi-thread/README | [new file with mode: 0644] | patch | blob |
egl-multi-thread/drm_gbm.c | [new file with mode: 0644] | patch | blob |
egl-multi-thread/drm_gbm.h | [new file with mode: 0644] | patch | blob |
egl-multi-thread/esTransform.c | [new file with mode: 0644] | patch | blob |
egl-multi-thread/esUtil.h | [new file with mode: 0644] | patch | blob |
egl-multi-thread/gl_kmscube.c | [new file with mode: 0644] | patch | blob |
egl-multi-thread/gl_kmscube.h | [new file with mode: 0644] | patch | blob |
egl-multi-thread/main.c | [new file with mode: 0644] | patch | blob |
egl-multi-thread/render_thread.c | [new file with mode: 0644] | patch | blob |
egl-multi-thread/render_thread.h | [new file with mode: 0644] | patch | blob |
egl-multi-thread/wayland-window.c | [new file with mode: 0644] | patch | blob |
egl-multi-thread/wayland_window.h | [new file with mode: 0644] | patch | blob |
diff --git a/egl-multi-thread/Makefile b/egl-multi-thread/Makefile
--- /dev/null
@@ -0,0 +1,46 @@
+# Makefile
+
+# Set the cross compiler and target file system path
+CROSS_COMPILE=arm-linux-gnueabihf-
+
+## No need to change below this
+
+COMMON_INCLUDES = -I$(FSDIR)/usr/include
+COMMON_LFLAGS = -L$(FSDIR)/usr/lib -Wl,--rpath-link,$(FSDIR)/usr/lib
+
+PLAT_CPP = $(CROSS_COMPILE)gcc
+
+PLAT_CFLAGS = $(COMMON_INCLUDES) -g
+PLAT_LINK = $(COMMON_LFLAGS) -lEGL -lGLESv2 -ludev -lpthread -lm -lrt
+
+SRCNAME = esTransform.c \
+ gl_kmscube.c \
+ main.c \
+ render_thread.c \
+
+BASE_OUTNAME = egl_multi_layer
+
+ifeq ($(BUILD_WAYLAND), yes)
+SRCNAME += wayland-window.c \
+
+PLAT_CFLAGS += -DUSE_WAYLAND
+PLAT_LINK += -lwayland-client -lpvr_wlegl
+OUTNAME = $(BASE_OUTNAME)_wayland
+else
+SRCNAME += drm_gbm.c \
+
+PLAT_CFLAGS += -I$(FSDIR)/usr/include/libdrm -I$(FSDIR)/usr/include/gbm
+PLAT_LINK += -lgbm -ldrm
+OUTNAME = $(BASE_OUTNAME)_drm
+endif
+
+OBJECTS = $(SRCNAME:.c=.o)
+
+$(OUTNAME): $(SRCNAME)
+ $(PLAT_CPP) -o $@ $^ $(PLAT_CFLAGS) $(LINK) $(PLAT_LINK)
+
+install:
+ cp $(OUTNAME) $(FSDIR)/home/root
+
+clean:
+ rm $(OUTNAME)
diff --git a/egl-multi-thread/README b/egl-multi-thread/README
--- /dev/null
+++ b/egl-multi-thread/README
@@ -0,0 +1,18 @@
+**************
+Build Instructions
+**************
+
+# Mount target file system on host
+# Set FSDIR to the target file system in Makefile
+
+# Include 4.9 cross compiler in the PATH
+export PATH=<4.9_CROSS_COMPILER>/bin:$PATH
+export FSDIR=<TARGETFS_PATH>
+
+#if the application is to be a wayland-client, then export the following as well
+#export BUILD_WAYLAND=yes
+
+make
+sudo -E make install
+
+# egl_multi_layer_{wayland/drm} will be installed to /home/root on target fs
diff --git a/egl-multi-thread/drm_gbm.c b/egl-multi-thread/drm_gbm.c
--- /dev/null
@@ -0,0 +1,315 @@
+#include <stdio.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <libdrm/drm.h>
+#include <libdrm/drm_mode.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <gbm/gbm.h>
+
+#include "drm_gbm.h"
+
+struct drm_data {
+ int fd;
+ int conn_id;
+ int crtc_id;
+ int width;
+ int height;
+
+ int count_planes;
+ struct plane_data *pdata;
+ int primary_planes;
+ int nonprimary_planes;
+
+ struct gbm_device *gbm_dev;
+};
+
+struct drm_fb {
+ struct gbm_bo *bo;
+ uint32_t fb_id;
+ int dmabuf_fd;
+ int fd;
+};
+
+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);
+
+ if (fb->fb_id)
+ drmModeRmFB(fb->fd, fb->fb_id);
+
+ free(fb);
+}
+
+static struct drm_fb * drm_fb_get_from_bo(int fd, 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->fd = fd;
+
+ 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(fd, width, height, 32, 32, stride, handle, &fb->fb_id);
+ if (ret) {
+ free(fb);
+ return NULL;
+ }
+
+ fb->dmabuf_fd = gbm_bo_get_fd(bo);
+
+ 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 *flips = data;
+ *flips= *flips - 1;
+}
+
+
+struct drm_data *init_drm_gbm (int conn_id) {
+ int ret;
+ int count;
+ struct drm_set_client_cap req;
+
+ struct drm_data *drm = calloc(sizeof(struct drm_data), 1);
+ if(!drm) {
+ printf("drm data alloc failed\n");
+ return NULL;
+ }
+
+ int fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
+ drm->fd = fd;
+ if(fd < 0) {
+ printf("drm open failed\n");
+ return NULL;
+ }
+
+ ret = drmSetMaster(fd);
+ if(ret < 0) {
+ printf("drm set master failed\n");
+ return NULL;
+ }
+
+ req.capability = DRM_CLIENT_CAP_ATOMIC;
+ req.value = 1;
+ ret = ioctl(fd, DRM_IOCTL_SET_CLIENT_CAP, &req);
+ if(ret < 0) {
+ printf("drm set atomic cap failed\n");
+ return NULL;
+ }
+
+ int conn_found = 0;
+ drm->conn_id = conn_id;
+ drmModeResPtr res = drmModeGetResources(fd);
+ for(count = 0; count < res->count_connectors; count++) {
+ if(res->connectors[count]== drm->conn_id) {
+ drmModeConnectorPtr connector = drmModeGetConnector(fd, drm->conn_id);
+ drmModeEncoderPtr encoder = drmModeGetEncoder(fd, connector->encoder_id);
+ drm->crtc_id = encoder->crtc_id;
+ drmModeCrtcPtr crtc = drmModeGetCrtc(fd, encoder->crtc_id);
+ drm->width = crtc->width;
+ drm->height = crtc->height;
+ conn_found = 1;
+ break;
+ }
+ }
+ if(!conn_found) {
+ printf("drm connector %d not found\n", drm->conn_id);
+ return NULL;
+ }
+
+ drm->gbm_dev = gbm_create_device(fd);
+
+ drmModePlaneResPtr planes = drmModeGetPlaneResources(fd);
+ drm->count_planes = planes->count_planes;
+ drm->pdata = calloc(sizeof(struct plane_data), planes->count_planes);
+ for(count = 0; count < planes->count_planes; count++) {
+ drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(fd, planes->planes[count], DRM_MODE_OBJECT_PLANE);
+ int propc;
+ for(propc = 0; propc < props->count_props; propc++) {
+ drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[propc]);
+ if(strcmp(prop->name, "type") == 0) {
+ unsigned long long prop_value = props->prop_values[propc];
+ if(prop_value) {
+ drm->pdata[count].primary = 1;
+ drm->primary_planes++;
+ } else {
+ drm->pdata[count].primary = 0;
+ drm->nonprimary_planes++;
+ }
+ }
+ if(strcmp(prop->name, "zorder") == 0) {
+ if(drm->pdata[count].primary) {
+ drm->pdata[count].zorder = drm->primary_planes -1 ;
+ } else {
+ drm->pdata[count].zorder = 4 - drm->nonprimary_planes;
+ }
+ drmModeObjectSetProperty(fd, planes->planes[count],
+ DRM_MODE_OBJECT_PLANE,
+ props->props[propc], drm->pdata[count].zorder);
+ }
+ if(strcmp(prop->name, "FB_ID") == 0)
+ drm->pdata[count].fb_id_property = props->props[propc];
+ }
+ drm->pdata[count].plane = planes->planes[count];
+ drm->pdata[count].occupied = 0;
+ drm->pdata[count].gbm_dev = drm->gbm_dev;
+
+ }
+
+ return drm;
+
+}
+
+struct plane_data *get_new_surface(struct drm_data *drm, int posx, int posy, int width, int height)
+{
+ int count;
+
+ if(posx < 0 || posx + width > drm->width || posy < 0 || posy + height > drm->height) {
+ printf("surface dimensions exceed crtc dimensions\n");
+ return NULL;
+ }
+
+ for(count = 0; count < drm->count_planes; count++) {
+ if(drm->pdata[count].primary)
+ continue;
+ if(drm->pdata[count].occupied)
+ continue;
+ break;
+ }
+
+ if(count == drm->count_planes) {
+ printf("no more planes\n");
+ return NULL;
+ }
+
+ drm->pdata[count].gbm_surf = gbm_surface_create(drm->gbm_dev,
+ width, height,
+ GBM_FORMAT_XRGB8888,
+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+ drm->pdata[count].posx = posx;
+ drm->pdata[count].posy = posy;
+ drm->pdata[count].width = width;
+ drm->pdata[count].height = height;
+ drm->pdata[count].occupied = 1;
+
+ return &drm->pdata[count];
+}
+
+int update_all_surfaces(struct drm_data *drm)
+{
+ int count;
+ int ret;
+ static int firsttime = 1;
+ static int flip_pending = 0;
+
+ if(firsttime) {
+ firsttime = 0;
+ for(count = 0; count < drm->count_planes; count++) {
+ struct plane_data *pdata = &drm->pdata[count];
+ if(drm->pdata[count].occupied == 0)
+ continue;
+ struct gbm_surface *surf = drm->pdata[count].gbm_surf;
+ struct gbm_bo *bo = gbm_surface_lock_front_buffer(surf);
+ struct drm_fb *fb = drm_fb_get_from_bo(drm->fd, bo);
+
+ ret = drmModeSetPlane(drm->fd,
+ drm->pdata[count].plane,
+ drm->crtc_id,
+ fb->fb_id,
+ 0,
+ drm->pdata[count].posx,
+ drm->pdata[count].posy,
+ drm->pdata[count].width,
+ drm->pdata[count].height,
+ 0,
+ 0,
+ drm->pdata[count].width << 16,
+ drm->pdata[count].height << 16);
+ }
+
+ }
+
+ drmModeAtomicReqPtr m_req = drmModeAtomicAlloc();
+
+ for(count = 0; count < drm->count_planes; count++) {
+ struct plane_data *pdata = &drm->pdata[count];
+ if(drm->pdata[count].occupied == 0)
+ continue;
+ struct gbm_surface *surf = drm->pdata[count].gbm_surf;
+ struct gbm_bo *bo = gbm_surface_lock_front_buffer(surf);
+ struct drm_fb *fb = drm_fb_get_from_bo(drm->fd, bo);
+
+ drmModeAtomicAddProperty(m_req,
+ drm->pdata[count].plane,
+ drm->pdata[count].fb_id_property,
+ fb->fb_id);
+
+ drm->pdata[count].current_bo = bo;
+ }
+
+
+ flip_pending++;
+ ret = drmModeAtomicCommit(drm->fd, m_req, DRM_MODE_ATOMIC_TEST_ONLY, 0);
+ ret = drmModeAtomicCommit(drm->fd, m_req, DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK, &flip_pending);
+
+ drmModeAtomicFree(m_req);
+
+ while (flip_pending) {
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(drm->fd, &fds);
+
+ ret = select(drm->fd + 1, &fds, NULL, NULL, NULL);
+
+ if(ret <= 0) {
+ printf("failing %d\n", ret);
+ return 0;
+ }
+
+ if (FD_ISSET(drm->fd, &fds)) {
+ drmEventContext ev = {
+ .version = DRM_EVENT_CONTEXT_VERSION,
+ .vblank_handler = 0,
+ .page_flip_handler = page_flip_handler,
+ };
+
+ drmHandleEvent(drm->fd, &ev);
+ }
+ }
+
+ for(count = 0; count < drm->count_planes; count++) {
+ struct plane_data *pdata = &drm->pdata[count];
+ if(drm->pdata[count].occupied == 0)
+ continue;
+ struct gbm_surface *surf = drm->pdata[count].gbm_surf;
+ struct gbm_bo *bo = drm->pdata[count].current_bo;
+ gbm_surface_release_buffer(surf, bo);
+ drm->pdata[count].current_bo = NULL;
+
+ }
+
+ return 0;
+
+}
diff --git a/egl-multi-thread/drm_gbm.h b/egl-multi-thread/drm_gbm.h
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __DRM_GBM_H__
+#define __DRM_GBM_H__
+
+#include <gbm/gbm.h>
+
+struct plane_data {
+ int plane;
+ int fb_id_property;
+ int zorder;
+ int primary;
+
+ struct gbm_device *gbm_dev;
+ struct gbm_surface *gbm_surf;
+
+ struct gbm_bo *current_bo;
+
+ int width;
+ int height;
+ int posx;
+ int posy;
+
+ int occupied;
+};
+
+struct drm_data;
+
+struct drm_data *init_drm_gbm (int conn_id);
+struct plane_data *get_new_surface(struct drm_data *drm, int posx, int posy, int width, int height);
+int update_all_surfaces(struct drm_data *drm);
+
+#endif /*__DRM_GBM_H__*/
diff --git a/egl-multi-thread/esTransform.c b/egl-multi-thread/esTransform.c
--- /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/egl-multi-thread/esUtil.h b/egl-multi-thread/esUtil.h
--- /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
diff --git a/egl-multi-thread/gl_kmscube.c b/egl-multi-thread/gl_kmscube.c
--- /dev/null
@@ -0,0 +1,368 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <GLES2/gl2.h>
+
+#include "render_thread.h"
+#include "gl_kmscube.h"
+#include "esUtil.h"
+
+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 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"
+ "\n"
+ "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
+ " \n"
+ "varying vec4 vVaryingColor; \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"
+ " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
+ " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
+ "} \n";
+
+static const char *fragment_shader_source =
+ "precision mediump float; \n"
+ " \n"
+ "varying vec4 vVaryingColor; \n"
+ " \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = vVaryingColor; \n"
+ "} \n";
+
+struct gl_kmscube_data {
+ GLuint program;
+ GLint modelviewmatrix;
+ GLint modelviewprojectionmatrix;
+ GLint normalmatrix;
+ GLuint vbo;
+ GLuint positionsoffset;
+ GLuint colorsoffset;
+ GLuint normalsoffset;
+ GLuint vertex_shader;
+ GLuint fragment_shader;
+ GLuint bgcolor;
+ GLuint alpha;
+ GLuint width;
+ GLuint height;
+};
+
+
+/*
+ * kmscube setup
+ * NOTE: Must be called after eglMakeCurrent returns successfully.
+ *
+ * NOTE: Caller can safely call eglMakeCurrent(disp,
+ * EGL_NO_SURFACE,
+ * EGL_NO_SURFACE,
+ * EGL_NO_CONTEXT);
+ * after this function returns a valid pointer.
+ *
+ * NOTE: If this function fails, it returns NULL, but DOES NOT free the
+ * resources allocated prior to the failure.
+ */
+void *setup_kmscube (struct render_thread_param *prm)
+{
+
+ int ret;
+
+ struct gl_kmscube_data *priv = calloc(sizeof(struct gl_kmscube_data), 1);
+ if(!priv) {
+ printf("kmscube: could not allocate priv data\n");
+ return NULL;
+ }
+
+ priv->vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+
+ glShaderSource(priv->vertex_shader, 1, &vertex_shader_source, NULL);
+ glCompileShader(priv->vertex_shader);
+
+ glGetShaderiv(priv->vertex_shader, GL_COMPILE_STATUS, &ret);
+ if (!ret) {
+ char *log;
+
+ printf("kmscube : vertex shader compilation failed!:\n");
+ glGetShaderiv(priv->vertex_shader, GL_INFO_LOG_LENGTH, &ret);
+ if (ret > 1) {
+ log = malloc(ret);
+ glGetShaderInfoLog(priv->vertex_shader, ret, NULL, log);
+ printf("kmscube : %s", log);
+ }
+
+ return NULL;
+ }
+
+ priv->fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource(priv->fragment_shader, 1, &fragment_shader_source, NULL);
+ glCompileShader(priv->fragment_shader);
+
+ glGetShaderiv(priv->fragment_shader, GL_COMPILE_STATUS, &ret);
+ if (!ret) {
+ char *log;
+
+ printf("kmscube : fragment shader compilation failed!:\n");
+ glGetShaderiv(priv->fragment_shader, GL_INFO_LOG_LENGTH, &ret);
+
+ if (ret > 1) {
+ log = malloc(ret);
+ glGetShaderInfoLog(priv->fragment_shader, ret, NULL, log);
+ printf("kmscube : %s", log);
+ }
+
+ return NULL;
+ }
+
+ priv->program = glCreateProgram();
+
+ glAttachShader(priv->program, priv->vertex_shader);
+ glAttachShader(priv->program, priv->fragment_shader);
+
+ glBindAttribLocation(priv->program, 0, "in_position");
+ glBindAttribLocation(priv->program, 1, "in_normal");
+ glBindAttribLocation(priv->program, 2, "in_color");
+
+ glLinkProgram(priv->program);
+
+ glGetProgramiv(priv->program, GL_LINK_STATUS, &ret);
+ if (!ret) {
+ char *log;
+
+ printf("kmscube : program linking failed!:\n");
+ glGetProgramiv(priv->program, GL_INFO_LOG_LENGTH, &ret);
+
+ if (ret > 1) {
+ log = malloc(ret);
+ glGetProgramInfoLog(priv->program, ret, NULL, log);
+ printf("kmscube : %s", log);
+ }
+
+ return NULL;
+ }
+
+ glUseProgram(priv->program);
+
+ priv->modelviewmatrix = glGetUniformLocation(priv->program, "modelviewMatrix");
+ priv->modelviewprojectionmatrix = glGetUniformLocation(priv->program, "modelviewprojectionMatrix");
+ priv->normalmatrix = glGetUniformLocation(priv->program, "normalMatrix");
+
+
+ priv->positionsoffset = 0;
+ priv->colorsoffset = sizeof(vVertices);
+ priv->normalsoffset = sizeof(vVertices) + sizeof(vColors);
+ glGenBuffers(1, &priv->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, priv->vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, priv->positionsoffset, sizeof(vVertices), &vVertices[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, priv->colorsoffset, sizeof(vColors), &vColors[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, priv->normalsoffset, sizeof(vNormals), &vNormals[0]);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)priv->positionsoffset);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)priv->normalsoffset);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)priv->colorsoffset);
+ glEnableVertexAttribArray(2);
+
+ priv->bgcolor = rand();
+ priv->alpha = (priv->bgcolor & 0xff000000) >> 24;
+ priv->width = prm->frame_width;
+ priv->height = prm->frame_height;
+
+ return (void *)priv;
+}
+
+/*
+ * kmscube render
+ * NOTE: Must be called after eglMakeCurrent returns successfully.
+ * NOTE: Caller thread must be current
+ *
+ * NOTE: Caller can safely call eglMakeCurrent(disp,
+ * EGL_NO_SURFACE,
+ * EGL_NO_SURFACE,
+ * EGL_NO_CONTEXT);
+ * after this function returns a valid pointer.
+ */
+int render_kmscube (void *priv)
+{
+ static int j = 0;
+ int r, g, b;
+ struct gl_kmscube_data *prm = priv;
+ /* connect the context to the surface */
+
+ r = (((prm->bgcolor & 0x00ff0000) >> 16) + j) % 512;
+ g = (((prm->bgcolor & 0x0000ff00) >> 8) + j) % 512;
+ b = ((prm->bgcolor & 0x000000ff) + j) % 512;
+
+ if(r >= 256)
+ r = 511 - r;
+ if(g >= 256)
+ g = 511 - g;
+ if(b >= 256)
+ b = 511 - b;
+
+ glViewport(0, 0, prm->width, prm->height);
+ glEnable(GL_CULL_FACE);
+
+ /*
+ * Different color every frame
+ */
+ glClearColor(r/256.0, g/256.0, b/256.0, prm->alpha/256.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ ESMatrix modelview;
+
+ /* clear the color buffer */
+
+ esMatrixLoadIdentity(&modelview);
+ esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
+ esRotate(&modelview, 45.0f + (0.25f * j), 1.0f, 0.0f, 0.0f);
+ esRotate(&modelview, 45.0f - (0.5f * j), 0.0f, 1.0f, 0.0f);
+ esRotate(&modelview, 10.0f + (0.15f * j), 0.0f, 0.0f, 1.0f);
+
+ GLfloat aspect = (GLfloat)(prm->height) / (GLfloat)(prm->width);
+
+ ESMatrix projection;
+ esMatrixLoadIdentity(&projection);
+ esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.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(prm->modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
+ glUniformMatrix4fv(prm->modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
+ glUniformMatrix3fv(prm->normalmatrix, 1, GL_FALSE, normal);
+
+ 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);
+
+ glDisable(GL_CULL_FACE);
+
+ j++;
+
+ return 0;
+}
diff --git a/egl-multi-thread/gl_kmscube.h b/egl-multi-thread/gl_kmscube.h
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __GL_KMSCUBE_H__
+#define __GL_KMSCUBE_H__
+
+#include "render_thread.h"
+
+void *setup_kmscube (struct render_thread_param *prm);
+int render_kmscube (void *prm);
+
+#endif /*__GL_KMSCUBE_H__*/
diff --git a/egl-multi-thread/main.c b/egl-multi-thread/main.c
--- /dev/null
+++ b/egl-multi-thread/main.c
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <time.h>
+#include <pthread.h>
+
+#include "esUtil.h"
+#include "render_thread.h"
+#include "gl_kmscube.h"
+
+#ifndef USE_WAYLAND
+#include "drm_gbm.h"
+#else
+#include "wayland_window.h"
+#endif
+
+#ifndef USE_WAYLAND
+int CONNECTOR_ID = (24);
+#endif
+
+#define FRAME_W (960)
+#define FRAME_H (540)
+
+#define MAX_NUM_THREADS (8)
+
+int num_threads = 3;
+
+#ifndef USE_WAYLAND
+static uint32_t gettime_msec()
+{
+ struct timespec t;
+ clock_gettime(CLOCK_REALTIME, &t);
+ return (uint32_t)(t.tv_sec * 1000) + (uint32_t)(t.tv_nsec / 1000000);
+}
+
+static uint32_t gettime_usec()
+{
+ struct timespec t;
+ clock_gettime(CLOCK_REALTIME, &t);
+ return (uint32_t)(t.tv_sec * 1000000) + (uint32_t)(t.tv_nsec / 1000);
+}
+
+unsigned long long gettime_nsec()
+{
+ struct timespec t;
+ clock_gettime(CLOCK_REALTIME, &t);
+ return ((unsigned long long)t.tv_sec) * 1000000000 + t.tv_nsec;
+}
+
+unsigned long long __gettime()
+{
+ struct timespec t;
+ clock_gettime(CLOCK_REALTIME, &t);
+ return ((unsigned long long)t.tv_sec) * 1000000000 + t.tv_nsec;
+}
+#endif
+
+#ifndef USE_WAYLAND
+void print_usage(char *app)
+{
+ printf("./%s --connector <CONNECTOR ID>\n", app);
+ printf("You can get the CONNECTOR_ID by running modetest on the \n \
+ target. For example our board shows the following:\n \
+ Connectors: \n \
+ id encoder status name size (mm) modes encoders \n \
+ 26 25 connected HDMI-A-1 160x90 40 25 \n \
+ modes: \n \
+ name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot) \n \
+ 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: phsync, pvsync; type: preferred, driver \n \
+ 1920x1080 60 1920 2008 2052 2200 1080 1084 1089 1125 flags: phsync, pvsync; type: driver \n \
+ \n \
+ On the above board, the CONNECTOR_ID will be set to 26.\n \
+ \n");
+}
+#else
+void print_usage(char *app)
+{
+ printf("./%s\n", app);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int count;
+
+#ifndef USE_WAYLAND
+ struct drm_data *dev;
+#else
+ struct wayland_data *dev;
+#endif
+
+ pthread_t threadid[MAX_NUM_THREADS];
+ struct render_thread_param threadparams[MAX_NUM_THREADS];
+
+#ifndef USE_WAYLAND
+ float fps;
+ int frame_num = 0;
+ unsigned long long starttime = 0;
+#endif
+
+ for(count = 0; count < argc; count++) {
+#ifndef USE_WAYLAND
+ if(strcmp(argv[count], "--connector") == 0)
+ if(count + 1 < argc)
+ CONNECTOR_ID = atoi(argv[count+1]);
+#endif
+
+ if (strcmp(argv[count], "--help") == 0)
+ print_usage(argv[0]);
+ }
+
+ srand(time(0));
+
+#ifndef USE_WAYLAND
+ dev = init_drm_gbm(CONNECTOR_ID);
+#else
+ dev = init_wayland_display();
+#endif
+ if(!dev) {
+ print_usage(argv[0]);
+ return -1;
+ }
+
+
+ for(count = 0; count < num_threads; count++) {
+#ifndef USE_WAYLAND
+ struct plane_data *pdata = get_new_surface(dev, count * FRAME_W, count * FRAME_H, FRAME_W, FRAME_H);
+#else
+ struct wayland_window_data *pdata = get_new_surface(dev, count * FRAME_W, count * FRAME_H, FRAME_W, FRAME_H);
+#endif
+ if(!pdata) {
+ break;
+ }
+#ifndef USE_WAYLAND
+ threadparams[count].dev = pdata->gbm_dev;
+ threadparams[count].surf = pdata->gbm_surf;
+#else
+ threadparams[count].dev = dev->display;
+ threadparams[count].surf = pdata->surf;
+#endif
+ threadparams[count].frame_width = FRAME_W;
+ threadparams[count].frame_height = FRAME_H;
+ threadparams[count].render_priv_setup = setup_kmscube;
+ threadparams[count].render_priv_render = render_kmscube;
+ }
+
+ printf("requested %d instances, rendering %d instances\n", num_threads, count);
+ num_threads = count;
+
+
+ /*
+ * Create the different GBM surfaces and start the draw threads
+ */
+
+ for(count = 0; count < num_threads; count++) {
+
+ int ret = setup_render_thread(&threadparams[count]);
+ if(ret != 0) {
+ printf("render_thread setup failed\n");
+ return -1;
+ }
+ }
+
+ for(count = 0; count < num_threads; count++) {
+ start_render_thread(&threadparams[count]);
+ }
+
+
+#ifndef USE_WAYLAND
+ starttime = __gettime();
+#endif
+
+ while(1) {
+
+ update_all_surfaces(dev);
+
+#ifndef USE_WAYLAND
+ unsigned long long __time;
+ frame_num++;
+ if(frame_num == 60) {
+ __time = __gettime();
+ fps = (60.0 * 1000000000) / (__time - starttime);
+ frame_num = 0;
+ starttime = __time;
+ printf("FPS = %f\n", fps);
+
+ }
+#endif
+
+ }
+
+}
+
diff --git a/egl-multi-thread/render_thread.c b/egl-multi-thread/render_thread.c
--- /dev/null
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <pthread.h>
+
+#include <EGL/egl.h>
+
+#include "render_thread.h"
+
+int setup_render_thread (struct render_thread_param *prm)
+{
+ EGLConfig config;
+ EGLint major, minor, n;
+ int ret;
+
+ EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ 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_DEPTH_SIZE, 8,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ prm->display = eglGetDisplay((EGLNativeDisplayType)prm->dev);
+
+ if (!eglInitialize(prm->display, &major, &minor)) {
+ printf("failed to initialize\n");
+ return -1;
+ }
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ printf("failed to bind api EGL_OPENGL_ES_API\n");
+ return -1;
+ }
+
+ if (!eglChooseConfig(prm->display, config_attribs, &config, 1, &n) || n != 1) {
+ printf("failed to choose config: %d\n", n);
+ return -1;
+ }
+
+ prm->context = eglCreateContext(prm->display, config,
+ EGL_NO_CONTEXT, context_attribs);
+ if (prm->context == NULL) {
+ printf("failed to create context\n");
+ return -1;
+ }
+
+ prm->surface = eglCreateWindowSurface(prm->display, config, prm->surf, NULL);
+ if (prm->surface == EGL_NO_SURFACE) {
+ printf("failed to create egl surface\n");
+ return -1;
+ }
+
+ eglMakeCurrent(prm->display, prm->surface, prm->surface, prm->context);
+
+ prm->render_priv_data = prm->render_priv_setup(prm);
+ if(!prm->render_priv_data) {
+ printf("failed to setup renderpriv\n");
+ return -1;
+ }
+
+ eglMakeCurrent(prm->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ return 0;
+}
+
+static void *render_thread (void *arg)
+{
+ struct render_thread_param *prm = arg;
+
+ eglMakeCurrent(prm->display, prm->surface, prm->surface, prm->context);
+
+ while(1) {
+ int ret = prm->render_priv_render(prm->render_priv_data);
+
+ if(ret != 0)
+ printf("renderpriv render returned %d\n", ret);
+
+ eglSwapBuffers(prm->display, prm->surface);
+
+ }
+}
+
+pthread_t start_render_thread (struct render_thread_param *prm)
+{
+ pthread_t threadid;
+
+ int ret = pthread_create(&threadid, NULL, render_thread, prm);
+
+ return ret == 0 ? threadid : -1;
+}
+
diff --git a/egl-multi-thread/render_thread.h b/egl-multi-thread/render_thread.h
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __RENDER_THREAD__
+#define __RENDER_THREAD__
+
+#include <EGL/egl.h>
+#include <gbm/gbm.h>
+#include <pthread.h>
+
+struct render_thread_param {
+ struct gbm_device *dev;
+ struct gbm_surface *surf;
+ EGLDisplay display;
+ EGLContext context;
+ EGLSurface surface;
+
+
+ unsigned int frame_width;
+ unsigned int frame_height;
+
+ void *render_priv_data;
+ void *(*render_priv_setup) (struct render_thread_param *prm);
+ int (*render_priv_render) (void *priv);
+
+};
+
+int setup_render_thread (struct render_thread_param *prm);
+
+pthread_t start_render_thread (struct render_thread_param *prm);
+
+#endif /*__RENDER_THREAD__*/
diff --git a/egl-multi-thread/wayland-window.c b/egl-multi-thread/wayland-window.c
--- /dev/null
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+#include "wayland_window.h"
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct wayland_data *d = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ d->shell = wl_registry_bind(registry, name,
+ &wl_shell_interface, 1);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+
+struct wayland_window_data *get_new_surface(struct wayland_data *wayland, int posx, int posy, int width, int height)
+{
+ struct wayland_window_data *window = calloc(sizeof(struct wayland_window_data), 1);
+ if(!window) {
+ printf("wayland window data alloc failed\n");
+ return NULL;
+ }
+
+ struct wl_surface *surface = wl_compositor_create_surface(wayland->compositor);
+ struct wl_shell_surface *shell_surface = wl_shell_get_shell_surface(wayland->shell, surface);
+
+ wl_shell_surface_set_toplevel(shell_surface);
+
+ struct wl_egl_window *egl_window = wl_egl_window_create(surface,
+ width, height);
+
+ window->surf = egl_window;
+
+ return window;
+}
+
+
+struct wayland_data *init_wayland_display () {
+ struct wayland_data *wayland = calloc(sizeof(struct wayland_data), 1);
+ if(!wayland) {
+ printf("wayland data alloc failed\n");
+ return NULL;
+ }
+
+ wayland->display = wl_display_connect(NULL);
+ if(!wayland->display) {
+ free(wayland);
+ printf("wl_display_connect failed\n");
+ return NULL;
+ }
+
+ wayland->registry = wl_display_get_registry(wayland->display);
+ wl_registry_add_listener(wayland->registry,
+ ®istry_listener, wayland);
+ wl_display_dispatch(wayland->display);
+
+ while(1) {
+ if(wayland->compositor && wayland->shell)
+ break;
+ wl_display_roundtrip(wayland->display);
+ }
+
+ return wayland;
+
+}
+
+int update_all_surfaces(struct wayland_data *wayland) {
+}
diff --git a/egl-multi-thread/wayland_window.h b/egl-multi-thread/wayland_window.h
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __WAYLAND_WINDOW_H__
+#define __WAYLAND_WINDOW_H__
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+struct wayland_data {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+};
+
+struct wayland_window_data {
+ struct wl_egl_window *surf;
+};
+
+struct wayland_data *init_wayland_display (void);
+struct wayland_window_data *get_new_surface(struct wayland_data *wayland, int posx, int posy, int width, int height);
+int update_all_surfaces(struct wayland_data *drm);
+
+#endif /*__WAYLAND_WINDOW_H__*/