/************************************************************************** * * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * **************************************************************************/ /* * Draw a triangle with X/EGL and OpenGL ES 2.x */ #define USE_FULL_GL 0 #include #include #include #include #include #include #include #include #if USE_FULL_GL #include /* use full OpenGL */ #else #include /* use OpenGL ES 2.x */ #endif #include #define FLOAT_TO_FIXED(X) ((X) * 65535.0) static GLfloat view_rotx = 0.0, view_roty = 0.0; static GLint u_matrix = -1; static GLint attr_pos = 0, attr_color = 1; static void make_z_rot_matrix(GLfloat angle, GLfloat *m) { float c = cos(angle * M_PI / 180.0); float s = sin(angle * M_PI / 180.0); int i; for (i = 0; i < 16; i++) m[i] = 0.0; m[0] = m[5] = m[10] = m[15] = 1.0; m[0] = c; m[1] = s; m[4] = -s; m[5] = c; } static void make_scale_matrix(GLfloat xs, GLfloat ys, GLfloat zs, GLfloat *m) { int i; for (i = 0; i < 16; i++) m[i] = 0.0; m[0] = xs; m[5] = ys; m[10] = zs; m[15] = 1.0; } static void mul_matrix(GLfloat *prod, const GLfloat *a, const GLfloat *b) { #define A(row,col) a[(col<<2)+row] #define B(row,col) b[(col<<2)+row] #define P(row,col) p[(col<<2)+row] GLfloat p[16]; GLint i; for (i = 0; i < 4; i++) { const GLfloat ai0=A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3); P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); } memcpy(prod, p, sizeof(p)); #undef A #undef B #undef PROD } static void draw(void) { static const GLfloat verts[3][2] = { { -1, -1 }, { 1, -1 }, { 0, 1 } }; static const GLfloat colors[3][3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; GLfloat mat[16], rot[16], scale[16]; /* Set modelview/projection matrix */ make_z_rot_matrix(view_rotx, rot); make_scale_matrix(0.5, 0.5, 0.5, scale); mul_matrix(mat, rot, scale); glUniformMatrix4fv(u_matrix, 1, GL_FALSE, mat); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); { glVertexAttribPointer(attr_pos, 2, GL_FLOAT, GL_FALSE, 0, verts); glVertexAttribPointer(attr_color, 3, GL_FLOAT, GL_FALSE, 0, colors); glEnableVertexAttribArray(attr_pos); glEnableVertexAttribArray(attr_color); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(attr_pos); glDisableVertexAttribArray(attr_color); } } /* new window size or exposure */ static void reshape(int width, int height) { glViewport(0, 0, (GLint) width, (GLint) height); } static void create_shaders(void) { static const char *fragShaderText = "varying vec4 v_color;\n" "void main() {\n" " gl_FragColor = v_color;\n" "}\n"; static const char *vertShaderText = "uniform mat4 modelviewProjection;\n" "attribute vec4 pos;\n" "attribute vec4 color;\n" "varying vec4 v_color;\n" "void main() {\n" " gl_Position = modelviewProjection * pos;\n" " v_color = color;\n" "}\n"; GLuint fragShader, vertShader, program; GLint stat; fragShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragShader, 1, (const char **) &fragShaderText, NULL); glCompileShader(fragShader); glGetShaderiv(fragShader, GL_COMPILE_STATUS, &stat); if (!stat) { printf("Error: fragment shader did not compile!\n"); exit(1); } vertShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertShader, 1, (const char **) &vertShaderText, NULL); glCompileShader(vertShader); glGetShaderiv(vertShader, GL_COMPILE_STATUS, &stat); if (!stat) { printf("Error: vertex shader did not compile!\n"); exit(1); } program = glCreateProgram(); glAttachShader(program, fragShader); glAttachShader(program, vertShader); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &stat); if (!stat) { char log[1000]; GLsizei len; glGetProgramInfoLog(program, 1000, &len, log); printf("Error: linking:\n%s\n", log); exit(1); } glUseProgram(program); if (1) { /* test setting attrib locations */ glBindAttribLocation(program, attr_pos, "pos"); glBindAttribLocation(program, attr_color, "color"); glLinkProgram(program); /* needed to put attribs into effect */ } else { /* test automatic attrib locations */ attr_pos = glGetAttribLocation(program, "pos"); attr_color = glGetAttribLocation(program, "color"); } u_matrix = glGetUniformLocation(program, "modelviewProjection"); printf("Uniform modelviewProjection at %d\n", u_matrix); printf("Attrib pos at %d\n", attr_pos); printf("Attrib color at %d\n", attr_color); } static void init(void) { typedef void (*proc)(); #if 1 /* test code */ proc p = eglGetProcAddress("eglCreateContext"); assert(p); p = eglGetProcAddress("glMapBufferOES"); assert(p); #endif glClearColor(0.4, 0.4, 0.4, 0.0); create_shaders(); } /* * Create an RGB, double-buffered X window. * Return the window and context handles. */ static void make_x_window(Display *x_dpy, EGLDisplay egl_dpy, const char *name, int x, int y, int width, int height, Window *winRet, EGLContext *ctxRet, EGLSurface *surfRet) { static const EGLint attribs[] = { EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_DEPTH_SIZE, 1, EGL_NONE }; static const EGLint ctx_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; int scrnum; XSetWindowAttributes attr; unsigned long mask; Window root; Window win; XVisualInfo *visInfo, visTemplate; int num_visuals; EGLContext ctx; EGLConfig config; EGLint num_configs; EGLint vid; scrnum = DefaultScreen( x_dpy ); root = RootWindow( x_dpy, scrnum ); if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) { printf("Error: couldn't get an EGL visual config\n"); exit(1); } assert(config); assert(num_configs > 0); if (!eglGetConfigAttrib(egl_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; mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 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); } #if USE_FULL_GL /* XXX fix this when eglBindAPI() works */ eglBindAPI(EGL_OPENGL_API); #else eglBindAPI(EGL_OPENGL_ES_API); #endif ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); if (!ctx) { printf("Error: eglCreateContext failed\n"); exit(1); } /* test eglQueryContext() */ { EGLint val; eglQueryContext(egl_dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &val); assert(val == 2); } *surfRet = eglCreateWindowSurface(egl_dpy, config, win, NULL); if (!*surfRet) { printf("Error: eglCreateWindowSurface failed\n"); exit(1); } { EGLint val; eglQuerySurface(egl_dpy, *surfRet, EGL_WIDTH, &val); assert(val == width); eglQuerySurface(egl_dpy, *surfRet, EGL_HEIGHT, &val); assert(val == height); eglQuerySurface(egl_dpy, *surfRet, EGL_SURFACE_TYPE, &val); assert(val == EGL_WINDOW_BIT); } XFree(visInfo); *winRet = win; *ctxRet = ctx; } static void event_loop(Display *dpy, Window win, EGLDisplay egl_dpy, EGLSurface egl_surf) { while (1) { int redraw = 0; XEvent event; XNextEvent(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 { r = XLookupString(&event.xkey, buffer, sizeof(buffer), NULL, NULL); if (buffer[0] == 27) { /* escape */ return; } } } redraw = 1; break; default: ; /*no-op*/ } if (redraw) { draw(); eglSwapBuffers(egl_dpy, egl_surf); } } } static void usage(void) { printf("Usage:\n"); printf(" -display set the display to run on\n"); printf(" -info display OpenGL renderer info\n"); } int main(int argc, char *argv[]) { const int winWidth = 300, winHeight = 300; Display *x_dpy; Window win; EGLSurface egl_surf; EGLContext egl_ctx; EGLDisplay egl_dpy; char *dpyName = NULL; GLboolean printInfo = GL_FALSE; EGLint egl_major, egl_minor; int i; const char *s; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-display") == 0) { dpyName = argv[i+1]; i++; } else if (strcmp(argv[i], "-info") == 0) { printInfo = GL_TRUE; } else { usage(); return -1; } } x_dpy = XOpenDisplay(dpyName); if (!x_dpy) { printf("Error: couldn't open display %s\n", dpyName ? dpyName : getenv("DISPLAY")); return -1; } egl_dpy = eglGetDisplay(x_dpy); if (!egl_dpy) { printf("Error: eglGetDisplay() failed\n"); return -1; } if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) { printf("Error: eglInitialize() failed\n"); return -1; } s = eglQueryString(egl_dpy, EGL_VERSION); printf("EGL_VERSION = %s\n", s); s = eglQueryString(egl_dpy, EGL_VENDOR); printf("EGL_VENDOR = %s\n", s); s = eglQueryString(egl_dpy, EGL_EXTENSIONS); printf("EGL_EXTENSIONS = %s\n", s); s = eglQueryString(egl_dpy, EGL_CLIENT_APIS); printf("EGL_CLIENT_APIS = %s\n", s); make_x_window(x_dpy, egl_dpy, "OpenGL ES 2.x tri", 0, 0, winWidth, winHeight, &win, &egl_ctx, &egl_surf); XMapWindow(x_dpy, win); if (!eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) { printf("Error: eglMakeCurrent() failed\n"); return -1; } if (printInfo) { printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR)); printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); } init(); /* Set initial projection/viewing transformation. * We can't be sure we'll get a ConfigureNotify event when the window * first appears. */ reshape(winWidth, winHeight); event_loop(x_dpy, win, egl_dpy, egl_surf); eglDestroyContext(egl_dpy, egl_ctx); eglDestroySurface(egl_dpy, egl_surf); eglTerminate(egl_dpy); XDestroyWindow(x_dpy, win); XCloseDisplay(x_dpy); return 0; }