diff options
Diffstat (limited to 'progs/egl')
-rw-r--r-- | progs/egl/.gitignore | 2 | ||||
-rw-r--r-- | progs/egl/Makefile | 88 | ||||
-rw-r--r-- | progs/egl/demo3.c | 2 | ||||
-rw-r--r-- | progs/egl/xeglbindtex.c | 474 | ||||
-rw-r--r-- | progs/egl/xeglthreads.c | 773 |
5 files changed, 1279 insertions, 60 deletions
diff --git a/progs/egl/.gitignore b/progs/egl/.gitignore index 793c6c0f61..7a13d4686a 100644 --- a/progs/egl/.gitignore +++ b/progs/egl/.gitignore @@ -6,5 +6,7 @@ eglinfo eglscreen egltri peglgears +xeglbindtex xeglgears +xeglthreads xegl_tri diff --git a/progs/egl/Makefile b/progs/egl/Makefile index e1fdb1ce63..ff9a858c56 100644 --- a/progs/egl/Makefile +++ b/progs/egl/Makefile @@ -7,6 +7,9 @@ include $(TOP)/configs/current INCLUDE_DIRS = -I$(TOP)/include HEADERS = $(TOP)/include/GLES/egl.h +LIB_DEP = $(TOP)/$(LIB_DIR)/libEGL.so + +LIBS = -L$(TOP)/$(LIB_DIR) -lEGL -lGL PROGRAMS = \ demo1 \ @@ -17,7 +20,9 @@ PROGRAMS = \ eglgears \ eglscreen \ peglgears \ + xeglbindtex \ xeglgears \ + xeglthreads \ xegl_tri @@ -28,77 +33,42 @@ PROGRAMS = \ default: $(PROGRAMS) +demo1: demo1.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) -demo1: demo1.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) $(LDFLAGS) demo1.o -L$(TOP)/$(LIB_DIR) -lEGL $(LIBDRM_LIB) -o $@ - -demo1.o: demo1.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include demo1.c - - -demo2: demo2.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) $(LDFLAGS) demo2.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) $(APP_LIB_DEPS) -o $@ - -demo2.o: demo2.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include demo2.c - - -demo3: demo3.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) $(LDFLAGS) demo3.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) $(APP_LIB_DEPS) -o $@ - -demo3.o: demo3.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include demo3.c - - -egltri: egltri.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) egltri.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) -o $@ +demo2: demo2.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) -egltri.o: egltri.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include egltri.c +demo3: demo3.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) +egltri: egltri.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) -eglinfo: eglinfo.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) $(LDFLAGS) eglinfo.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) -o $@ +eglinfo: eglinfo.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) -eglinfo.o: eglinfo.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include eglinfo.c +eglgears: eglgears.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) +eglscreen: eglscreen.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) -eglgears: eglgears.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) $(LDFLAGS) eglgears.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) $(APP_LIB_DEPS) -o $@ +peglgears: peglgears.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(LIBDRM_LIB) -eglgears.o: eglgears.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include eglgears.c +xeglbindtex: xeglbindtex.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) -lX11 -eglscreen: eglscreen.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) $(LDFLAGS) eglscreen.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) $(APP_LIB_DEPS) -o $@ +xeglgears: xeglgears.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) -lX11 -eglscreen.o: eglscreen.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include eglscreen.c - -peglgears: peglgears.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) peglgears.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) $(APP_LIB_DEPS) -o $@ - -peglgears.o: peglgears.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include peglgears.c - - -xeglgears: xeglgears.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) xeglgears.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) $(APP_LIB_DEPS) -o $@ - -xeglgears.o: xeglgears.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include xeglgears.c - - -xegl_tri: xegl_tri.o $(TOP)/$(LIB_DIR)/libEGL.so - $(CC) $(CFLAGS) xegl_tri.o -L$(TOP)/$(LIB_DIR) -lEGL -lGL $(LIBDRM_LIB) $(APP_LIB_DEPS) -o $@ - -xegl_tri.o: xegl_tri.c $(HEADERS) - $(CC) -c $(CFLAGS) -I$(TOP)/include xegl_tri.c +xeglthreads: xeglthreads.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) -lX11 +xegl_tri: xegl_tri.o $(HEADERS) $(LIB_DEP) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) -lX11 clean: -rm -f *.o *~ - -rm -f *.so -rm -f $(PROGRAMS) - diff --git a/progs/egl/demo3.c b/progs/egl/demo3.c index a6096a257e..daab62d173 100644 --- a/progs/egl/demo3.c +++ b/progs/egl/demo3.c @@ -551,7 +551,7 @@ write_ppm(const char *filename, const GLubyte *buffer, int width, int height) } } -#include "../src/egl/main/egldisplay.h" +#include "../../src/egl/main/egldisplay.h" typedef struct fb_display { diff --git a/progs/egl/xeglbindtex.c b/progs/egl/xeglbindtex.c new file mode 100644 index 0000000000..fdd9fe2b87 --- /dev/null +++ b/progs/egl/xeglbindtex.c @@ -0,0 +1,474 @@ +/* + * Simple demo for eglBindTexImage. Based on xegl_tri.c by + * + * Copyright (C) 2008 Brian Paul All Rights Reserved. + * + * 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 + * BRIAN PAUL 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. + */ + +/* + * The spec says that eglBindTexImage supports only OpenGL ES context, but this + * demo uses OpenGL context. Keep in mind that this is non-standard. + */ + +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <GL/gl.h> +#include <EGL/egl.h> + +static EGLDisplay dpy; +static EGLContext ctx_win, ctx_pbuf; +static EGLSurface surf_win, surf_pbuf; +static GLuint tex_pbuf; + +static GLfloat view_rotx = 0.0, view_roty = 0.0, view_rotz = 0.0; +static GLboolean blend = GL_TRUE; +static GLuint color_flow; + +static void +make_pbuffer(int width, int height) +{ + static const EGLint config_attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE, + EGL_NONE + }; + EGLint pbuf_attribs[] = { + EGL_WIDTH, width, + EGL_HEIGHT, height, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB, + EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, + EGL_NONE + }; + EGLConfig config; + EGLint num_configs; + + if (!eglChooseConfig(dpy, config_attribs, &config, 1, &num_configs)) { + printf("Error: couldn't get an EGL visual config for pbuffer\n"); + exit(1); + } + + eglBindAPI(EGL_OPENGL_API); + ctx_pbuf = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL ); + surf_pbuf = eglCreatePbufferSurface(dpy, config, pbuf_attribs); + if (surf_pbuf == EGL_NO_SURFACE) { + printf("failed to allocate pbuffer\n"); + exit(1); + } + + glGenTextures(1, &tex_pbuf); +} + +static void +use_pbuffer(void) +{ + static int initialized; + + eglMakeCurrent(dpy, surf_pbuf, surf_pbuf, ctx_pbuf); + if (!initialized) { + EGLint width, height; + GLfloat ar; + + initialized = 1; + + eglQuerySurface(dpy, surf_pbuf, EGL_WIDTH, &width); + eglQuerySurface(dpy, surf_pbuf, EGL_WIDTH, &height); + ar = (GLfloat) width / (GLfloat) height; + + glViewport(0, 0, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-ar, ar, -1, 1, 1.0, 10.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + /* y-inverted */ + glScalef(1.0, -1.0, 1.0); + + glTranslatef(0.0, 0.0, -5.0); + + glClearColor(0.2, 0.2, 0.2, 0.0); + } +} + +static void +make_window(Display *x_dpy, const char *name, + int x, int y, int width, int height, + Window *winRet) +{ + static const EGLint attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_NONE + }; + + int scrnum; + XSetWindowAttributes attr; + unsigned long mask; + Window root; + Window win; + XVisualInfo *visInfo, visTemplate; + int num_visuals; + EGLConfig config; + EGLint num_configs, vid; + + scrnum = DefaultScreen( x_dpy ); + root = RootWindow( x_dpy, scrnum ); + + if (!eglChooseConfig(dpy, attribs, &config, 1, &num_configs)) { + printf("Error: couldn't get an EGL visual config\n"); + exit(1); + } + + if (!eglGetConfigAttrib(dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { + printf("Error: eglGetConfigAttrib() failed\n"); + exit(1); + } + + /* The X window visual must match the EGL config */ + visTemplate.visualid = vid; + visInfo = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals); + if (!visInfo) { + printf("Error: couldn't get X visual\n"); + exit(1); + } + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap( x_dpy, root, visInfo->visual, AllocNone); + attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; + attr.override_redirect = 0; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect; + + win = XCreateWindow( x_dpy, root, 0, 0, width, height, + 0, visInfo->depth, InputOutput, + visInfo->visual, mask, &attr ); + + /* set hints and properties */ + { + XSizeHints sizehints; + sizehints.x = x; + sizehints.y = y; + sizehints.width = width; + sizehints.height = height; + sizehints.flags = USSize | USPosition; + XSetNormalHints(x_dpy, win, &sizehints); + XSetStandardProperties(x_dpy, win, name, name, + None, (char **)NULL, 0, &sizehints); + } + + eglBindAPI(EGL_OPENGL_API); + ctx_win = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL ); + if (!ctx_win) { + printf("Error: glXCreateContext failed\n"); + exit(1); + } + + surf_win = eglCreateWindowSurface(dpy, config, win, NULL); + + XFree(visInfo); + + *winRet = win; +} + +static void +use_window(void) +{ + static int initialized; + + eglMakeCurrent(dpy, surf_win, surf_win, ctx_win); + if (!initialized) { + initialized = 1; + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tex_pbuf); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } +} + +static void +draw_triangle(void) +{ + static const GLfloat verts[3][2] = { + { -3, -3 }, + { 3, -3 }, + { 0, 3 } + }; + GLfloat colors[3][3] = { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }; + GLint i; + + /* flow the color */ + for (i = 0; i < 3; i++) { + GLint first = (i + color_flow / 256) % 3; + GLint second = (first + 1) % 3; + GLint third = (second + 1) % 3; + GLfloat c = (color_flow % 256) / 256.0f; + + c = c * c * c; + colors[i][first] = 1.0f - c; + colors[i][second] = c; + colors[i][third] = 0.0f; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glVertexPointer(2, GL_FLOAT, 0, verts); + glColorPointer(3, GL_FLOAT, 0, colors); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); +} + +static void +draw_textured_cube(void) +{ + static const GLfloat verts[][2] = { + { -4, -4 }, + { 4, -4 }, + { 4, 4 }, + { -4, 4 } + }; + static const GLfloat colors[][4] = { + { 1, 1, 1, 0.5 }, + { 1, 1, 1, 0.5 }, + { 1, 1, 1, 0.5 }, + { 1, 1, 1, 0.5 } + }; + static const GLfloat texs[][2] = { + { 0, 0 }, + { 1, 0 }, + { 1, 1 }, + { 0, 1 } + }; + static const GLfloat xforms[6][4] = { + { 0, 0, 1, 0 }, + { 90, 0, 1, 0 }, + { 180, 0, 1, 0 }, + { 270, 0, 1, 0 }, + { 90, 1, 0, 0 }, + { -90, 1, 0, 0 } + }; + GLint i; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (blend) { + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + } else { + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + } + + glVertexPointer(2, GL_FLOAT, 0, verts); + glColorPointer(4, GL_FLOAT, 0, colors); + glTexCoordPointer(2, GL_FLOAT, 0, texs); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + for (i = 0; i < 6; i++) { + glPushMatrix(); + glRotatef(xforms[i][0], xforms[i][1], xforms[i][2], xforms[i][3]); + glTranslatef(0, 0, 4.1); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glPopMatrix(); + } + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +static void +draw(void) +{ + use_pbuffer(); + draw_triangle(); + + use_window(); + + eglBindTexImage(dpy, surf_pbuf, EGL_BACK_BUFFER); + + glPushMatrix(); + glRotatef(view_rotx, 1, 0, 0); + glRotatef(view_roty, 0, 1, 0); + glRotatef(view_rotz, 0, 0, 1); + + draw_textured_cube(); + + glPopMatrix(); + + eglReleaseTexImage(dpy, surf_pbuf, EGL_BACK_BUFFER); +} + +/* new window size or exposure */ +static void +reshape(int width, int height) +{ + GLfloat ar = (GLfloat) width / (GLfloat) height; + + use_window(); + + glViewport(0, 0, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-ar, ar, -1, 1, 5.0, 60.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0, 0.0, -40.0); +} + +static void +event_loop(Display *x_dpy, Window win) +{ + while (1) { + int redraw = 1; + + if (XPending(x_dpy) > 0) { + XEvent event; + XNextEvent(x_dpy, &event); + + switch (event.type) { + case Expose: + redraw = 1; + break; + case ConfigureNotify: + reshape(event.xconfigure.width, event.xconfigure.height); + break; + case KeyPress: + { + char buffer[10]; + int r, code; + code = XLookupKeysym(&event.xkey, 0); + if (code == XK_Left) { + view_roty += 5.0; + } + else if (code == XK_Right) { + view_roty -= 5.0; + } + else if (code == XK_Up) { + view_rotx += 5.0; + } + else if (code == XK_Down) { + view_rotx -= 5.0; + } + else if (code == XK_b) { + blend = !blend; + } + else { + r = XLookupString(&event.xkey, buffer, sizeof(buffer), + NULL, NULL); + if (buffer[0] == 27) { + /* escape */ + return; + } + } + } + redraw = 1; + break; + default: + ; /*no-op*/ + } + } + + if (redraw) { + view_rotx += 1.0; + view_roty += 2.0; + view_rotz += 1.5; + color_flow += 20; + draw(); + eglSwapBuffers(dpy, surf_win); + } + } +} + +int +main(int argc, char *argv[]) +{ + const int winWidth = 300, winHeight = 300; + Display *x_dpy; + Window win; + char *dpyName = NULL; + EGLint egl_major, egl_minor; + const char *s; + + x_dpy = XOpenDisplay(dpyName); + if (!x_dpy) { + printf("Error: couldn't open display %s\n", + dpyName ? dpyName : getenv("DISPLAY")); + return -1; + } + + dpy = eglGetDisplay(x_dpy); + if (!dpy) { + printf("Error: eglGetDisplay() failed\n"); + return -1; + } + + if (!eglInitialize(dpy, &egl_major, &egl_minor)) { + printf("Error: eglInitialize() failed\n"); + return -1; + } + + s = eglQueryString(dpy, EGL_VERSION); + printf("EGL_VERSION = %s\n", s); + + make_window(x_dpy, "color flow", 0, 0, winWidth, winHeight, &win); + make_pbuffer(winWidth, winHeight); + + XMapWindow(x_dpy, win); + + reshape(winWidth, winHeight); + event_loop(x_dpy, win); + + glDeleteTextures(1, &tex_pbuf); + + eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(dpy); + + XDestroyWindow(x_dpy, win); + XCloseDisplay(x_dpy); + + return 0; +} diff --git a/progs/egl/xeglthreads.c b/progs/egl/xeglthreads.c new file mode 100644 index 0000000000..508dbc0943 --- /dev/null +++ b/progs/egl/xeglthreads.c @@ -0,0 +1,773 @@ +/* + * Copyright (C) 2000 Brian Paul All Rights Reserved. + * + * 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 + * BRIAN PAUL 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. + * + * Ported to EGL by Chia-I Wu <olvaffe@gmail.com> + */ + + +/* + * This program tests EGL thread safety. + * Command line options: + * -p Open a display connection for each thread + * -l Enable application-side locking + * -n <num threads> Number of threads to create (default is 2) + * -display <display name> Specify X display (default is $DISPLAY) + * -t Use texture mapping + * + * Brian Paul 20 July 2000 + */ + + +/* + * Notes: + * - Each thread gets its own EGL context. + * + * - The EGL contexts share texture objects. + * + * - When 't' is pressed to update the texture image, the window/thread which + * has input focus is signalled to change the texture. The other threads + * should see the updated texture the next time they call glBindTexture. + */ + + +#if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */ + +#include <assert.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <GL/gl.h> +#include <EGL/egl.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <pthread.h> + + +/* + * Each window/thread/context: + */ +struct winthread { + Display *Dpy; + int Index; + pthread_t Thread; + Window Win; + EGLDisplay Display; + EGLContext Context; + EGLSurface Surface; + float Angle; + int WinWidth, WinHeight; + GLboolean NewSize; + GLboolean Initialized; + GLboolean MakeNewTexture; +}; + + +#define MAX_WINTHREADS 100 +static struct winthread WinThreads[MAX_WINTHREADS]; +static int NumWinThreads = 0; +static volatile GLboolean ExitFlag = GL_FALSE; + +static GLboolean MultiDisplays = 0; +static GLboolean Locking = 0; +static GLboolean Texture = GL_FALSE; +static GLuint TexObj = 12; +static GLboolean Animate = GL_TRUE; + +static pthread_mutex_t Mutex; +static pthread_cond_t CondVar; +static pthread_mutex_t CondMutex; + + +static void +Error(const char *msg) +{ + fprintf(stderr, "Error: %s\n", msg); + exit(1); +} + + +static void +signal_redraw(void) +{ + pthread_mutex_lock(&CondMutex); + pthread_cond_broadcast(&CondVar); + pthread_mutex_unlock(&CondMutex); +} + + +static void +MakeNewTexture(struct winthread *wt) +{ +#define TEX_SIZE 128 + static float step = 0.0; + GLfloat image[TEX_SIZE][TEX_SIZE][4]; + GLint width; + int i, j; + + for (j = 0; j < TEX_SIZE; j++) { + for (i = 0; i < TEX_SIZE; i++) { + float dt = 5.0 * (j - 0.5 * TEX_SIZE) / TEX_SIZE; + float ds = 5.0 * (i - 0.5 * TEX_SIZE) / TEX_SIZE; + float r = dt * dt + ds * ds + step; + image[j][i][0] = + image[j][i][1] = + image[j][i][2] = 0.75 + 0.25 * cos(r); + image[j][i][3] = 1.0; + } + } + + step += 0.5; + + glBindTexture(GL_TEXTURE_2D, TexObj); + + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + if (width) { + assert(width == TEX_SIZE); + /* sub-tex replace */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEX_SIZE, TEX_SIZE, + GL_RGBA, GL_FLOAT, image); + } + else { + /* create new */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0, + GL_RGBA, GL_FLOAT, image); + } +} + + + +/* draw a colored cube */ +static void +draw_object(void) +{ + glPushMatrix(); + glScalef(0.75, 0.75, 0.75); + + glColor3f(1, 0, 0); + + if (Texture) { + glBindTexture(GL_TEXTURE_2D, TexObj); + glEnable(GL_TEXTURE_2D); + } + else { + glDisable(GL_TEXTURE_2D); + } + + glBegin(GL_QUADS); + + /* -X */ + glColor3f(0, 1, 1); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f(-1, 1, -1); + glTexCoord2f(1, 1); glVertex3f(-1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); + + /* +X */ + glColor3f(1, 0, 0); + glTexCoord2f(0, 0); glVertex3f(1, -1, -1); + glTexCoord2f(1, 0); glVertex3f(1, 1, -1); + glTexCoord2f(1, 1); glVertex3f(1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(1, -1, 1); + + /* -Y */ + glColor3f(1, 0, 1); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, -1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); + + /* +Y */ + glColor3f(0, 1, 0); + glTexCoord2f(0, 0); glVertex3f(-1, 1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, 1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); + + /* -Z */ + glColor3f(1, 1, 0); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, -1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); + + /* +Y */ + glColor3f(0, 0, 1); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); + + glEnd(); + + glPopMatrix(); +} + + +/* signal resize of given window */ +static void +resize(struct winthread *wt, int w, int h) +{ + wt->NewSize = GL_TRUE; + wt->WinWidth = w; + wt->WinHeight = h; + if (!Animate) + signal_redraw(); +} + + +/* + * We have an instance of this for each thread. + */ +static void +draw_loop(struct winthread *wt) +{ + while (!ExitFlag) { + + if (Locking) + pthread_mutex_lock(&Mutex); + + if (!wt->Initialized) { + eglMakeCurrent(wt->Display, wt->Surface, wt->Surface, wt->Context); + printf("xeglthreads: %d: GL_RENDERER = %s\n", wt->Index, + (char *) glGetString(GL_RENDERER)); + if (Texture /*&& wt->Index == 0*/) { + MakeNewTexture(wt); + } + wt->Initialized = GL_TRUE; + } + + if (Locking) + pthread_mutex_unlock(&Mutex); + + eglBindAPI(EGL_OPENGL_API); + if (eglGetCurrentContext() != wt->Context) { + printf("xeglthreads: current context %p != %p\n", + eglGetCurrentContext(), wt->Context); + } + + glEnable(GL_DEPTH_TEST); + + if (wt->NewSize) { + GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight; + glViewport(0, 0, wt->WinWidth, wt->WinHeight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-w, w, -1.0, 1.0, 1.5, 10); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2.5); + wt->NewSize = GL_FALSE; + } + + if (wt->MakeNewTexture) { + MakeNewTexture(wt); + wt->MakeNewTexture = GL_FALSE; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + glRotatef(wt->Angle, 0, 1, 0); + glRotatef(wt->Angle, 1, 0, 0); + glScalef(0.7, 0.7, 0.7); + draw_object(); + glPopMatrix(); + + if (Locking) + pthread_mutex_lock(&Mutex); + + eglSwapBuffers(wt->Display, wt->Surface); + + if (Locking) + pthread_mutex_unlock(&Mutex); + + if (Animate) { + usleep(5000); + } + else { + /* wait for signal to draw */ + pthread_mutex_lock(&CondMutex); + pthread_cond_wait(&CondVar, &CondMutex); + pthread_mutex_unlock(&CondMutex); + } + wt->Angle += 1.0; + } + eglMakeCurrent(wt->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + + +static void +keypress(XEvent *event, struct winthread *wt) +{ + char buf[100]; + KeySym keySym; + XComposeStatus stat; + + XLookupString(&event->xkey, buf, sizeof(buf), &keySym, &stat); + + switch (keySym) { + case XK_Escape: + /* tell all threads to exit */ + if (!Animate) { + signal_redraw(); + } + ExitFlag = GL_TRUE; + /*printf("exit draw_loop %d\n", wt->Index);*/ + return; + case XK_t: + case XK_T: + if (Texture) { + wt->MakeNewTexture = GL_TRUE; + if (!Animate) + signal_redraw(); + } + break; + case XK_a: + case XK_A: + Animate = !Animate; + if (Animate) /* yes, prev Animate state! */ + signal_redraw(); + break; + case XK_s: + case XK_S: + if (!Animate) + signal_redraw(); + break; + default: + ; /* nop */ + } +} + + +/* + * The main process thread runs this loop. + * Single display connection for all threads. + */ +static void +event_loop(Display *dpy) +{ + XEvent event; + int i; + + assert(!MultiDisplays); + + while (!ExitFlag) { + + if (Locking) { + while (1) { + int k; + pthread_mutex_lock(&Mutex); + k = XPending(dpy); + if (k) { + XNextEvent(dpy, &event); + pthread_mutex_unlock(&Mutex); + break; + } + pthread_mutex_unlock(&Mutex); + usleep(5000); + } + } + else { + XNextEvent(dpy, &event); + } + + switch (event.type) { + case ConfigureNotify: + /* Find winthread for this event's window */ + for (i = 0; i < NumWinThreads; i++) { + struct winthread *wt = &WinThreads[i]; + if (event.xconfigure.window == wt->Win) { + resize(wt, event.xconfigure.width, + event.xconfigure.height); + break; + } + } + break; + case KeyPress: + for (i = 0; i < NumWinThreads; i++) { + struct winthread *wt = &WinThreads[i]; + if (event.xkey.window == wt->Win) { + keypress(&event, wt); + break; + } + } + break; + default: + /*no-op*/ ; + } + } +} + + +/* + * Separate display connection for each thread. + */ +static void +event_loop_multi(void) +{ + XEvent event; + int w = 0; + + assert(MultiDisplays); + + while (!ExitFlag) { + struct winthread *wt = &WinThreads[w]; + if (XPending(wt->Dpy)) { + XNextEvent(wt->Dpy, &event); + switch (event.type) { + case ConfigureNotify: + resize(wt, event.xconfigure.width, event.xconfigure.height); + break; + case KeyPress: + keypress(&event, wt); + break; + default: + ; /* nop */ + } + } + w = (w + 1) % NumWinThreads; + usleep(5000); + } +} + + + +/* + * we'll call this once for each thread, before the threads are created. + */ +static void +create_window(struct winthread *wt, EGLContext shareCtx) +{ + Window win; + EGLContext ctx; + EGLSurface surf; + EGLint attribs[] = { EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_DEPTH_SIZE, 1, + EGL_NONE }; + EGLConfig config; + EGLint num_configs; + EGLint vid; + int scrnum; + XSetWindowAttributes attr; + unsigned long mask; + Window root; + XVisualInfo *visinfo, visTemplate; + int num_visuals; + int width = 160, height = 160; + int xpos = (wt->Index % 8) * (width + 10); + int ypos = (wt->Index / 8) * (width + 20); + + scrnum = DefaultScreen(wt->Dpy); + root = RootWindow(wt->Dpy, scrnum); + + if (!eglChooseConfig(wt->Display, attribs, &config, 1, &num_configs)) { + Error("Unable to choose an EGL config"); + } + + assert(config); + assert(num_configs > 0); + + if (!eglGetConfigAttrib(wt->Display, config, EGL_NATIVE_VISUAL_ID, &vid)) { + Error("Unable to get visual id of EGL config\n"); + } + + visTemplate.visualid = vid; + visinfo = XGetVisualInfo(wt->Dpy, VisualIDMask, + &visTemplate, &num_visuals); + if (!visinfo) { + Error("Unable to find RGB, Z, double-buffered visual"); + } + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(wt->Dpy, root, visinfo->visual, AllocNone); + attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height, + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &attr); + if (!win) { + Error("Couldn't create window"); + } + + XFree(visinfo); + + { + XSizeHints sizehints; + sizehints.x = xpos; + sizehints.y = ypos; + sizehints.width = width; + sizehints.height = height; + sizehints.flags = USSize | USPosition; + XSetNormalHints(wt->Dpy, win, &sizehints); + XSetStandardProperties(wt->Dpy, win, "xeglthreads", "xeglthreads", + None, (char **)NULL, 0, &sizehints); + } + + eglBindAPI(EGL_OPENGL_API); + + ctx = eglCreateContext(wt->Display, config, shareCtx, NULL); + if (!ctx) { + Error("Couldn't create EGL context"); + } + surf = eglCreateWindowSurface(wt->Display, config, win, NULL); + if (!surf) { + Error("Couldn't create EGL surface"); + } + + XMapWindow(wt->Dpy, win); + XSync(wt->Dpy, 0); + + /* save the info for this window/context */ + wt->Win = win; + wt->Context = ctx; + wt->Surface = surf; + wt->Angle = 0.0; + wt->WinWidth = width; + wt->WinHeight = height; + wt->NewSize = GL_TRUE; +} + + +/* + * Called by pthread_create() + */ +static void * +thread_function(void *p) +{ + struct winthread *wt = (struct winthread *) p; + draw_loop(wt); + return NULL; +} + + +/* + * called before exit to wait for all threads to finish + */ +static void +clean_up(void) +{ + int i; + + /* wait for threads to finish */ + for (i = 0; i < NumWinThreads; i++) { + pthread_join(WinThreads[i].Thread, NULL); + } + + for (i = 0; i < NumWinThreads; i++) { + eglDestroyContext(WinThreads[i].Display, WinThreads[i].Context); + XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win); + } +} + + +static void +usage(void) +{ + printf("xeglthreads: test of EGL/GL thread safety (any key = exit)\n"); + printf("Usage:\n"); + printf(" xeglthreads [options]\n"); + printf("Options:\n"); + printf(" -display DISPLAYNAME Specify display string\n"); + printf(" -n NUMTHREADS Number of threads to create\n"); + printf(" -p Use a separate display connection for each thread\n"); + printf(" -l Use application-side locking\n"); + printf(" -t Enable texturing\n"); + printf("Keyboard:\n"); + printf(" Esc Exit\n"); + printf(" t Change texture image (requires -t option)\n"); + printf(" a Toggle animation\n"); + printf(" s Step rotation (when not animating)\n"); +} + + +int +main(int argc, char *argv[]) +{ + char *displayName = NULL; + int numThreads = 2; + Display *dpy = NULL; + EGLDisplay *egl_dpy = NULL; + int i; + Status threadStat; + + if (argc == 1) { + usage(); + } + else { + int i; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) { + displayName = argv[i + 1]; + i++; + } + else if (strcmp(argv[i], "-p") == 0) { + MultiDisplays = 1; + } + else if (strcmp(argv[i], "-l") == 0) { + Locking = 1; + } + else if (strcmp(argv[i], "-t") == 0) { + Texture = 1; + } + else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) { + numThreads = atoi(argv[i + 1]); + if (numThreads < 1) + numThreads = 1; + else if (numThreads > MAX_WINTHREADS) + numThreads = MAX_WINTHREADS; + i++; + } + else { + usage(); + exit(1); + } + } + } + + if (Locking) + printf("xeglthreads: Using explicit locks around Xlib calls.\n"); + else + printf("xeglthreads: No explict locking.\n"); + + if (MultiDisplays) + printf("xeglthreads: Per-thread display connections.\n"); + else + printf("xeglthreads: Single display connection.\n"); + + /* + * VERY IMPORTANT: call XInitThreads() before any other Xlib functions. + */ + if (!MultiDisplays) { + if (!Locking) { + threadStat = XInitThreads(); + if (threadStat) { + printf("XInitThreads() returned %d (success)\n", + (int) threadStat); + } + else { + printf("XInitThreads() returned 0 " + "(failure- this program may fail)\n"); + } + } + + dpy = XOpenDisplay(displayName); + if (!dpy) { + fprintf(stderr, "Unable to open display %s\n", + XDisplayName(displayName)); + return -1; + } + egl_dpy = eglGetDisplay(dpy); + if (!egl_dpy) { + fprintf(stderr, "Unable to get EGL display\n"); + XCloseDisplay(dpy); + return -1; + } + if (!eglInitialize(egl_dpy, NULL, NULL)) { + fprintf(stderr, "Unable to initialize EGL display\n"); + return -1; + } + } + + pthread_mutex_init(&Mutex, NULL); + pthread_mutex_init(&CondMutex, NULL); + pthread_cond_init(&CondVar, NULL); + + printf("xeglthreads: creating windows\n"); + + NumWinThreads = numThreads; + + /* Create the EGL windows and contexts */ + for (i = 0; i < numThreads; i++) { + EGLContext share; + + if (MultiDisplays) { + WinThreads[i].Dpy = XOpenDisplay(displayName); + assert(WinThreads[i].Dpy); + WinThreads[i].Display = eglGetDisplay(WinThreads[i].Dpy); + assert(eglInitialize(WinThreads[i].Display, NULL, NULL)); + } + else { + WinThreads[i].Dpy = dpy; + WinThreads[i].Display = egl_dpy; + } + WinThreads[i].Index = i; + WinThreads[i].Initialized = GL_FALSE; + + share = (Texture && i > 0) ? WinThreads[0].Context : 0; + + create_window(&WinThreads[i], share); + } + + printf("xeglthreads: creating threads\n"); + + /* Create the threads */ + for (i = 0; i < numThreads; i++) { + pthread_create(&WinThreads[i].Thread, NULL, thread_function, + (void*) &WinThreads[i]); + printf("xeglthreads: Created thread %p\n", + (void *) WinThreads[i].Thread); + } + + if (MultiDisplays) + event_loop_multi(); + else + event_loop(dpy); + + clean_up(); + + if (MultiDisplays) { + for (i = 0; i < numThreads; i++) { + eglTerminate(WinThreads[i].Display); + XCloseDisplay(WinThreads[i].Dpy); + } + } + else { + eglTerminate(egl_dpy); + XCloseDisplay(dpy); + } + + return 0; +} + + +#else /* PTHREADS */ + + +#include <stdio.h> + +int +main(int argc, char *argv[]) +{ + printf("Sorry, this program wasn't compiled with PTHREADS defined.\n"); + return 0; +} + + +#endif /* PTHREADS */ |