/** ** gles1-kd-pbuffer ** Windowed example of using pbuffers to render to a texture. ** ** Features: ** - configurable window size through the -size=(width)x(height) option ** - configurable window position through the -pos=(x),(y) option ** - adjustable swap interval through the -interval=(n) option; ** a swap interval of 0 lets the app run as fast as possible ** numbers of 1 or more limit the rate to the number of vsync periods ** - use the KD_QNX_window extension to set window position ** - application responds to size changes from window manager or delegate ** - rendering is suspended if window is not visible ** - application exits if power event invalidates window surface(s) ** ** Please go to the following URL for updates to this tutorial: ** http://community.qnx.com/sf/scm/do/listRepositories/projects.graphics/scm ** ** Copyright 2009, QNX Software Systems Ltd. All Rights Reserved ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, ** provided that the above copyright notice appear in all copies and that ** both that copyright notice and this permission notice appear in ** supporting documentation. ** ** This file is provided AS IS with no warranties of any kind. The author ** shall have no liability with respect to the infringement of copyrights, ** trade secrets or any patents by this file or any part thereof. In no ** event will the author be liable for any lost revenue or profits or ** other special, indirect and consequential damages. */ /** ** Include the header files for libraries we are using. **/ #include /* Header file for isdigit */ #include /* Header file for fprintf */ #include /* Header file for EXIT_FAILURE, EXIT_SUCCESS, atoi */ #include /* Header file for strncmp */ #include /* Header file for KEYCODE_ESCAPE */ #include /* Header file for EGL */ #include /* Header file for OpenKODE */ #include /* Header file for OpenGL ES 1.X */ /** ** Header file that defines egl_perror and kd_perror. These functions will ** convert EGL and OpenKODE error codes into more meaningful messages. **/ #include "error.h" /** ** Here is the list of attributes that will be passed to EGL to get us ** a pixel format configuration. An EGL configuration is required by ** EGL when creating surfaces and rendering contexts. Since we will ** modify certain values in this list when certain command line arguments ** are provided, we will organize our attributes as an aggregate of named ** key/value pairs of EGLint's. This way we won't have to track the ** index locations various attributes in a one-dimensional array. **/ struct { EGLint surface_type[2]; EGLint renderable_type[2]; EGLint level[2]; EGLint transparent_type[2]; EGLint red_size[2]; EGLint green_size[2]; EGLint blue_size[2]; EGLint alpha_size[2]; EGLint sample_buffers[2]; EGLint samples[2]; EGLint config_id[2]; EGLint none; } egl_conf_attr = { .surface_type = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT }, /* Ask for displayable and pbuffer surfaces */ .renderable_type = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT }, /* We want to do OpenGL ES 1.X rendering */ .level = { EGL_LEVEL, 0 }, /* Use layer 0 by default */ .transparent_type = { EGL_TRANSPARENT_TYPE, EGL_DONT_CARE }, /* Allow configs with transparent color */ .red_size = { EGL_RED_SIZE, EGL_DONT_CARE }, /* Minimum number of red bits per pixel */ .green_size = { EGL_GREEN_SIZE, EGL_DONT_CARE }, /* Minimum number of green bits per pixel */ .blue_size = { EGL_BLUE_SIZE, EGL_DONT_CARE }, /* Minimum number of blue bits per pixel */ .alpha_size = { EGL_ALPHA_SIZE, EGL_DONT_CARE }, /* Minimum number of alpha bits per pixel */ .sample_buffers = { EGL_SAMPLE_BUFFERS, 0 }, /* Multi-sampling buffers */ .samples = { EGL_SAMPLES, 0 }, /* Minimum number of samples per pixel */ .config_id = { EGL_CONFIG_ID, EGL_DONT_CARE }, /* used to get a specific EGL config */ .none = EGL_NONE /* End of list */ }; /** ** Choosing an appropriate EGL config is an important part of the ** initialization procedure, especially in embedded systems where the ** difference in performance between pixel formats can make or break an ** application. On desktop systems, asking for rgb111 or better usually is all ** it takes. This returns the best configuration supported by the hardware. In ** embedded systems, rgba8888 may not be an option even if the rendering ** hardware supports it because the memory bandwidth used by the display ** controller to paint the display at 60 frames per second is just too much ** for the system to handle. Specifying an EGL configuration by its id gives ** you the ability to get exactly what you want. However, this approach is far ** from user friendly when you have to deal with multiple platforms, windowing ** systems, etc. This function allows the user to choose a pixel format and/or ** a number of per-pixel samples or an EGL configuration id. **/ EGLConfig choose_config(EGLDisplay egl_disp, const char* str) { EGLConfig egl_conf = (EGLConfig)0; /* the resulting EGL config */ EGLConfig *egl_configs; /* describes the color and ancillary buffers */ EGLint egl_num_configs; /* number of configs that match our attributes */ EGLint val; /* an EGL integer value */ EGLBoolean rc; /* the return value of EGL functions */ const char *tok; /* a pointer that will traverse the string */ EGLint i; /* variable used to loop on matching configs */ /** ** We start by parsing the config string, which is a comma-separated list ** of specifiers. We don't have to use strtok because the syntax is quite ** simple. All we will need is strncmp and atoi. We start by skipping any ** whitespace or separators. The str argument might be null, indicating ** that we must find a config that matches the default criteria. In this ** case, we must skip any processing of the str and make sure that there ** is a default value for all configuration attributes (usually ** EGL_DONT_CARE.) **/ if (str != NULL) { tok = str; while (*tok == ' ' || *tok == ',') { tok++; } /** ** Loop as long as there are tokens to be processed. **/ while (*tok != '\0') { if (strncmp(tok, "rgba8888", strlen("rgba8888")) == 0) { egl_conf_attr.red_size[1] = 8; egl_conf_attr.green_size[1] = 8; egl_conf_attr.blue_size[1] = 8; egl_conf_attr.alpha_size[1] = 8; tok += strlen("rgba8888"); } else if (strncmp(tok, "rgba5551", strlen("rgba5551")) == 0) { egl_conf_attr.red_size[1] = 5; egl_conf_attr.green_size[1] = 5; egl_conf_attr.blue_size[1] = 5; egl_conf_attr.alpha_size[1] = 1; tok += strlen("rgba5551"); } else if (strncmp(tok, "rgba4444", strlen("rgba4444")) == 0) { egl_conf_attr.red_size[1] = 4; egl_conf_attr.green_size[1] = 4; egl_conf_attr.blue_size[1] = 4; egl_conf_attr.alpha_size[1] = 4; tok += strlen("rgba4444"); } else if (strncmp(tok, "rgb565", strlen("rgb565")) == 0) { egl_conf_attr.red_size[1] = 5; egl_conf_attr.green_size[1] = 6; egl_conf_attr.blue_size[1] = 5; egl_conf_attr.alpha_size[1] = 0; tok += strlen("rgb565"); } else if (isdigit(*tok)) { /** ** An integer value could either be an EGL configuration id or ** a multi-sampling count. An 'x' immediately after the integer ** indicates that it is a multi-sampling specifier. **/ val = atoi(tok); while (isdigit(*(++tok))); if (*tok == 'x') { egl_conf_attr.sample_buffers[1] = 1; egl_conf_attr.samples[1] = val; tok++; } else { /** ** Using the EGL_CONFIG_ID attribute allows us to get a ** configuration by its id without a typecast, ** i.e. egl_conf = (EGLConfig)val. Note that the EGL spec says ** that when the EGL_CONFIG_ID attribute is specified, all ** other attributes are ignored, so we don't have to shorten ** the list ourselves in this case. **/ egl_conf_attr.config_id[1] = val; } } else { /** ** Print a message on the console if we encounter something we ** don't know. This way, the user will know that he might not get ** the config he was expecting. **/ fprintf(stderr, "invalid configuration specifier: "); while (*tok != ' ' && *tok != ',' && *tok != '\0') { fputc(*tok++, stderr); } fputc('\n', stderr); } /** ** Skip an spaces and separators between this token and the next one. **/ while (*tok == ' ' || *tok == ',') { tok++; } } } /** ** We use eglChooseConfig to get EGLConfigs that match our list of ** attributes. We first call it with NULL for configs and 0 for ** config_size because we just want to know how many configs match the ** criteria. **/ rc = eglChooseConfig(egl_disp, (EGLint*)&egl_conf_attr, NULL, 0, &egl_num_configs); if (rc != EGL_TRUE) { egl_perror("eglChooseConfig"); return egl_conf; } if (egl_num_configs == 0) { fprintf(stderr, "eglChooseConfig: could not find a configuration\n"); return egl_conf; } /** ** Now the total number of configs that match the criteria has been stored ** in egl_num_configs. At this point, we know there is one or more match. ** We will malloc enough memory to hold all the matching configs. We need ** this to traverse the configs to find a perfect match. **/ egl_configs = malloc(egl_num_configs * sizeof(*egl_configs)); if (egl_configs == NULL) { fprintf(stderr, "could not allocate memory for %d EGL configs\n", egl_num_configs); return egl_conf; } /** ** The second time we call eglChooseConfig, it is with an array of configs ** big enough to store all the results. The list of EGL configs is ** expected to be static, which means our list of matching configs should ** be static as well. As long as the call succedes, we don't have to check ** for egl_num_configs again. **/ rc = eglChooseConfig(egl_disp, (EGLint*)&egl_conf_attr, egl_configs, egl_num_configs, &egl_num_configs); if (rc != EGL_TRUE) { egl_perror("eglChooseConfig"); free(egl_configs); return egl_conf; } /** ** Many selection criteria are not exact matches. Most of the matching ** rules are 'AtLeast', meaning that configs with an attribute greater or ** equal to the reference value are included. It is our job to go through ** the list of 'matchin' configs to find the best fit. For example, asking ** for rgba5551 can return rgba8888 configs, because they have the ** requested bit depths or more. What's worse is that in this case, ** rgba8888 configs will be first in this list, so if you really want ** rgba5551, you simply can't just take the first match that comes along. **/ for (i = 0; i < egl_num_configs; i++) { /** ** Make sure the number of red bits matches what we asked for. **/ if (egl_conf_attr.red_size[1] != EGL_DONT_CARE) { eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_RED_SIZE, &val); if (val != egl_conf_attr.red_size[1]) { continue; } } /** ** Make sure the number of green bits matches what we asked for. **/ if (egl_conf_attr.green_size[1] != EGL_DONT_CARE) { eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_GREEN_SIZE, &val); if (val != egl_conf_attr.green_size[1]) { continue; } } /** ** Make sure the number of blue bits matches what we asked for. **/ if (egl_conf_attr.blue_size[1] != EGL_DONT_CARE) { eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_BLUE_SIZE, &val); if (val != egl_conf_attr.blue_size[1]) { continue; } } /** ** Make sure the number of alpha bits matches what we asked for. **/ if (egl_conf_attr.alpha_size[1] != EGL_DONT_CARE) { eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_ALPHA_SIZE, &val); if (val != egl_conf_attr.alpha_size[1]) { continue; } } /** ** This config has the pixel format we asked for, so we can keep it ** and stop looking. **/ egl_conf = egl_configs[i]; break; } /** ** At this point, it is important to remember to free the array allocated ** to hold all configs before returning. **/ free(egl_configs); /** ** The value of egl_conf will be that of a matching config if one was ** found or (EGLConfig)0 if there were no exact matches. The calling ** function can decide to do what it wants with the result. For example, ** it could ask for a different config if no matches were found, or it can ** simply fail and report the error. **/ if (egl_conf == (EGLConfig)0) { fprintf(stderr, "eglChooseConfig: could not find a matching configuration\n"); } return egl_conf; } /** ** The resize function is used to initialize the OpenGL ES viewport and ** projection matrix. It also computes the position of vertices that will be ** used to do rendering. We compute the position of those vertices based on ** the window's dimensions instead of using scale and translation matrices. ** Because it is unlikely that the size will change very often, this is more ** efficient than applying transformations every time a frame is rendered. **/ static void resize(GLshort *points, GLfloat *coords, GLint width, GLint height) { /** ** The first six vertices take up 12 shorts. These vertices define two ** triangles that share a vertex. Because the OpenGL ES coordinate system ** starts at the bottom left instead of the top left corner, all y values ** need to be inverted. **/ points[0] = 10; points[1] = 246; points[2] = 246; points[3] = 246; points[4] = 128; points[5] = 128; points[6] = 128; points[7] = 128; points[8] = 246; points[9] = 10; points[10] = 10; points[11] = 10; /** ** The next four vertices take up 8 shorts. These vertices define a ** rectangle that goes from (0,0) to (barwidth,height). A translation ** matrix will be used to slide this rectangle across the viewport. **/ points[12] = 0; points[13] = height; points[14] = width; points[15] = height; points[16] = 0; points[17] = 0; points[18] = width; points[19] = 0; /** ** Finally, we need four texture coordinates to go along the four vertex ** coordinates that we just added. The rendering loop will reset the ** vertex pointer array in the second pass to start at points[12], so ** our vertex coordinates can start at offset 0. To make it more ** interesting, we'll make the texture repeat twice in both directions. **/ coords[0] = 0.0f; coords[1] = 2.0f; coords[2] = 2.0f; coords[3] = 2.0f; coords[4] = 0.0f; coords[5] = 0.0f; coords[6] = 2.0f; coords[7] = 0.0f; } /** ** This is the entry point of our OpenKODE application. This is where we will ** parse command line arguments, create our window, handle events, and do our ** rendering. **/ KDint kdMain(KDint argc, const KDchar *const *argv) { /** ** These are the attributes required to create an EGL pbuffer that can ** later be bound to a 2D texture. The format can either be RGB or RGBA. ** Since we are not doing any kind of blending with the results, all we ** need is a pbuffer that can be used as an RGB 2D texture. We also ** make sure that no additional space is allocated for mipmaps. **/ const EGLint egl_pbuf_attr[] = { EGL_WIDTH, 256, EGL_HEIGHT, 256, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB, EGL_MIPMAP_TEXTURE, EGL_FALSE, EGL_NONE }; /** ** This is the size for an invisible exit button. We choose a value that's ** big enough to be useable with touchscreens and pointer devices. **/ const KDint32 exit_area_size = 20; /** ** We will use the surface attributes to choose between single-buffered ** and double-buffered rendering. Again, to avoid having to keep track of ** indices in a one-dimensional array of attribute/value pairs, we use ** an aggregate of named attribute/value pairs of EGLint's. **/ struct { EGLint render_buffer[2]; EGLint none; } egl_surf_attr = { .render_buffer = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER }, /* Ask for double-buffering */ .none = EGL_NONE /* End of list */ }; /* use default display unless one is specified on the command line */ EGLNativeDisplayType disp_id = EGL_DEFAULT_DISPLAY; EGLDisplay egl_disp; /* abstract display on which graphics are drawn */ EGLConfig egl_conf; /* describes the color and ancillary buffers */ EGLSurface egl_pbuf; /* pbuffer that renders to a texture */ EGLSurface egl_surf; /* refers to our window's rendering surface */ EGLContext egl_ctx; /* a handle to a rendering context */ KDWindow *kd_win; /* a handle to an OpenKODE window */ EGLNativeWindowType egl_win; /* native window from which we'll create a surface */ KDint32 kd_size[2] = { -1, -1 }; /* width and height of our window */ KDint32 kd_pos[2] = { -1, -1 }; /* x,y position of our window */ EGLint interval = 1; /* EGL swap interval */ EGLBoolean verbose = EGL_FALSE; /* EGL_TRUE if the verbose option was set */ KDboolean kd_vis = KD_TRUE; /* boolean that indicates if our window is visible */ KDboolean kd_freeze = KD_FALSE; /* EGL_TRUE if rendering is frozen */ KDint32 kd_alpha = 255; /* global transparency */ KDust timeout = 0; /* nanoseconds to wait for next OpenKODE event */ const KDEvent *event; /* pointer to an OpenKODE event */ const char *conf_str = NULL; /* configuration string */ const char *tok; /* used to process command line arguments */ int rval = EXIT_FAILURE; /* application exits with value stored here */ int rc; /* return value from functions */ int i; /* loop/frame counter */ GLshort points[20]; /* we'll store the vertices in this array */ GLfloat coords[8]; /* we'll store texture coordinates in this array */ /** ** We start by processing the command line arguments. These are passed by ** OpenKODE in the argc and argv arguments. The first argument is skipped ** because it contains the name of the program. Arguments follow the ** syntax -(option)=(value). **/ for (i = 1; i < argc; i++) { if (strncmp(argv[i], "-config=", strlen("-config=")) == 0) { /** ** The syntax of the EGL configuration option is ** -config=[option][,[option]...]. All we need is to do is pass ** the string after the '=' to choose_config, which will parse the ** options and find the right EGL config. **/ conf_str = argv[i] + strlen("-config="); } else if (strncmp(argv[i], "-size=", strlen("-size=")) == 0) { /** ** The syntax of the size option is -size=(width)x(height). ** Because atoi stops processing at the first non-digit character, ** we can simply call atoi on the string after the '=' to get the ** width, and call atoi again on the string after the 'x' to get ** the height. **/ tok = argv[i] + strlen("-size="); kd_size[0] = atoi(tok); while (*tok >= '0' && *tok <= '9') { tok++; } kd_size[1] = atoi(tok+1); } else if (strncmp(argv[i], "-pos=", strlen("-pos=")) == 0) { /** ** The syntax of the pos option is -pos=(x),(y). ** Because atoi stops processing at the first non-digit character, ** we can simply call atoi on the string after the '=' to get the ** x offset, and call atoi again on the string after the ',' to ** get the y offset. **/ tok = argv[i] + strlen("-pos="); kd_pos[0] = atoi(tok); while (*tok >= '0' && *tok <= '9') { tok++; } kd_pos[1] = atoi(tok+1); } else if (strncmp(argv[i], "-interval=", strlen("-interval=")) == 0) { /** ** The syntax of the interval option is -interval=(number). All ** we need is to convert the number that starts after the '='. **/ interval = atoi(argv[i] + strlen("-interval=")); } else if (strncmp(argv[i], "-display=", strlen("-display=")) == 0) { /** ** The syntax of the display option is -display=(number). All ** we need is to do is convert the number that starts after ** the '='. **/ disp_id = atoi(argv[i] + strlen("-display=")); } else if (strncmp(argv[i], "-layer=", strlen("-layer=")) == 0) { /** ** The syntax of the layer option is -layer=(number). All ** we need is to do is convert the number that starts after ** the '='. **/ egl_conf_attr.level[1] = atoi(argv[i] + strlen("-layer=")); } else if (strcmp(argv[i], "-single-buffer") == 0) { /** ** The -single-buffer option on the command line will cause the ** rendering to be done to a single on-screen buffer. There are ** typically artifacts associated with single-buffered rendering ** caused by rendering to a visible surface. **/ egl_surf_attr.render_buffer[1] = EGL_SINGLE_BUFFER; } else if (strcmp(argv[i], "-double-buffer") == 0) { /** ** The -double-buffer option on the command line will cause the ** rendering to be on alternating back buffers. This eliminates ** artifacts associated with single-buffered rendering. **/ egl_surf_attr.render_buffer[1] = EGL_BACK_BUFFER; } else if (strncmp(argv[i], "-global-alpha=", strlen("-global-alpha=")) == 0) { /** ** The syntax of the global alpha option is ** -global-alpha=(number). All we need is to do is convert the ** number that starts after the '='. **/ kd_alpha = atoi(argv[i] + strlen("-global-alpha=")); } else if (strncmp(argv[i], "-verbose", strlen("-verbose")) == 0) { /** ** The verbose option has no special syntax. It just has to be ** present on the command line to cause the verbose messages to ** be printed. **/ verbose = EGL_TRUE; } else { /** ** Make sure we say something instead of silently ignoring a ** command line option. **/ fprintf(stderr, "invalid command line option: %s\n", argv[i]); } } /** ** Before we can do any kind of rendering, we must establish a connection ** to a display. We don't have any particular preference, so we'll just ** ask for the default display, unless a display id is specified on the ** command line. **/ egl_disp = eglGetDisplay(disp_id); if (egl_disp == EGL_NO_DISPLAY) { egl_perror("eglGetDisplay"); goto fail1; } /** ** Now we initialize EGL on the display. We can't do anything with this ** EGL display until EGL has been initialized. OpenGL ES 1.X is supported ** by all versions of EGL, so it is not necessary to check for the major ** and minor version numbers. **/ rc = eglInitialize(egl_disp, NULL, NULL); if (rc != EGL_TRUE) { egl_perror("eglInitialize"); goto fail2; } /** ** Choosing an EGL configuration can be a tedious process. Here, we call ** choose_config which will do all the necessary work. In this case, this ** includes parsing a configuration string and/or select an appropriate ** configuration based on some configuration attributes. **/ egl_conf = choose_config(egl_disp, conf_str); if (egl_conf == (EGLConfig)0) { goto fail2; } /** ** If the application was started with the -verbose command line argument, ** we will print a few information strings about the EGL configuration we ** end-up using. This might be useful if the -config option was not ** provided, or if the user doesn't know the particular details of a given ** pixel format. We will get the information by using the ** eglGetConfigAttrib with several interesting attribute names. **/ verbose = 1; if (verbose) { printf("EGL_VENDOR = %s\n", eglQueryString(egl_disp, EGL_VENDOR)); printf("EGL_VERSION = %s\n", eglQueryString(egl_disp, EGL_VERSION)); printf("EGL_CLIENT_APIS = %s\n", eglQueryString(egl_disp, EGL_CLIENT_APIS)); printf("EGL_EXTENSIONS = %s\n\n", eglQueryString(egl_disp, EGL_EXTENSIONS)); i = -1; eglGetConfigAttrib(egl_disp, egl_conf, EGL_CONFIG_ID, &i); printf("EGL_CONFIG_ID = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_RED_SIZE, &i); printf("EGL_RED_SIZE = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_GREEN_SIZE, &i); printf("EGL_GREEN_SIZE = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_BLUE_SIZE, &i); printf("EGL_BLUE_SIZE = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_ALPHA_SIZE, &i); printf("EGL_ALPHA_SIZE = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_DEPTH_SIZE, &i); printf("EGL_DEPTH_SIZE = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_LEVEL, &i); printf("EGL_LEVEL = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_NATIVE_RENDERABLE, &i); printf("EGL_NATIVE_RENDERABLE = %s\n", i ? "EGL_TRUE" : "EGL_FALSE"); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_NATIVE_VISUAL_TYPE, &i); printf("EGL_NATIVE_VISUAL_TYPE = %d\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_RENDERABLE_TYPE, &i); printf("EGL_RENDERABLE_TYPE = 0x%04x\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_SURFACE_TYPE, &i); printf("EGL_SURFACE_TYPE = 0x%04x\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_TYPE, &i); if (i == EGL_TRANSPARENT_RGB) { printf("EGL_TRANSPARENT_TYPE = EGL_TRANSPARENT_RGB\n"); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_RED_VALUE, &i); printf("EGL_TRANSPARENT_RED = 0x%02x\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_GREEN_VALUE, &i); printf("EGL_TRANSPARENT_GREEN = 0x%02x\n", i); i = 0; eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_BLUE_VALUE, &i); printf("EGL_TRANSPARENT_BLUE = 0x%02x\n\n", i); } else { printf("EGL_TRANSPARENT_TYPE = EGL_NONE\n\n"); } } /** ** Now we ask OpenKODE to create a window using the EGL config that we ** just got. The last argument is an event user pointer that will be ** passed in all events generated for this window. When the value of the ** third argument is KD_NULL, the event user pointer contains a pointer ** to the OpenKODE window. At this point, the window is just a container ** and doesn't really exist on the display. **/ kd_win = kdCreateWindow(egl_disp, egl_conf, KD_NULL); if (kd_win == KD_NULL) { kd_perror("kdCreateWindow"); goto fail2; } /** ** If the user provided a size for our window through command line ** options, we'll set it here. Window systems don't have to support ** resizing of windows once they have been realized, so it is safer to do ** this now. OpenKODE doesn't define a window position property, so we ** use the KD_QNX_window extension to do that. We also set the global ** transparency property if the user requested some transparency ** through command line options. We won't bother setting the global ** transparency to 255 since this is the default value. **/ if (kd_size[0] != -1 && kd_size[1] != -1) { kdSetWindowPropertyiv(kd_win, KD_WINDOWPROPERTY_SIZE, kd_size); } #ifdef KD_QNX_window if (kd_pos[0] != -1 && kd_pos[1] != -1) { kdSetWindowPropertyiv(kd_win, KD_QNX_WINDOWPROPERTY_POSITION, kd_pos); } if (kd_alpha < 255) { kdSetWindowPropertyiv(kd_win, KD_QNX_WINDOWPROPERTY_ALPHA, &kd_alpha); } #else if (kd_pos[0] != -1 && kd_pos[1] != -1) { fprintf(stderr, "kdSetWindowPropertyiv: cannot set position\n"); } if (kd_alpha < 255) { fprintf(stderr, "kdSetWindowPropertyiv: cannot set global transparency\n"); } #endif /** ** Now we can realize this window. By default, windows are visible, so ** realizing a window makes it appear on the display. When a window is ** realized, a buffer is created to hold the window's contents. A handle ** to this buffer is returned by the kdRealizeWindow call. OpenKODE is ** not a rendering API so we can't really do anything with this handle ** except pass it along to EGL. **/ rc = kdRealizeWindow(kd_win, &egl_win); if (rc) { kd_perror("kdRealizeWindow"); goto fail3; } /** ** Now that we have created a native platform window we can use it to ** create an EGL on-screen rendering surface. We'll be able to use this ** surface as the target of our OpenGL ES 1.X rendering. We'll use the ** same EGL display and config to create the EGL surface as the ones we ** used to create our OpenKODE window. The EGL display has to be the ** same. The EGL config just needs to be compatible with the one used ** to create the OpenKODE window. We ask for double-buffering by default. ** Single buffering can be achieved by setting the EGL_RENDER_BUFFER ** attribute to EGL_SINGLE_BUFFER. If you don't care about single- or ** double-buffered rendering, but you want the buffer contents to be ** preserved after posting changes, you must add a call to ** eglSurfaceAttrib and set EGL_SWAP_BEHAVIOR to EGL_BUFFER_PRESERVED. **/ egl_surf = eglCreateWindowSurface(egl_disp, egl_conf, egl_win, (EGLint*)&egl_surf_attr); if (egl_surf == EGL_NO_SURFACE) { egl_perror("eglCreateWindowSurface"); goto fail3; } /** ** We next create an EGL pbuffer surface that will be used to do render ** to texture. Note that the configuration used to create the pbuffer does ** not have to be identical, or even be compatible with the configuration ** used to create our window surface. The OpenGL ES implementation can ** sample the texture and convert the format, if necessary, before writing ** into the renderbuffer. Using the same config here allows us to reuse ** the same context for both surfaces, instead of creating a different ** context for each. **/ egl_pbuf = eglCreatePbufferSurface(egl_disp, egl_conf, egl_pbuf_attr); if (egl_pbuf == EGL_NO_SURFACE) { egl_perror("eglCreatePbufferSurface"); goto fail4; } /** ** Now we need to create an OpenGL ES rendering context. The context will ** keep track of the OpenGL ES 1.X state among other things. We don't have ** to specify the current rendering API with an eglBindApi call because ** OpenGL ES is the default rendering API. The third argument to ** eglCreateContext is another EGL rendering context that we wish to share ** data with. We pass EGL_NO_CONTEXT to indicate that we won't need any ** of the textures or vertex buffer objects created in another EGL render ** context. The last argument is an attribute list that can be used to ** specify an API version number. We would use it to override the ** EGL CONTEXT CLIENT VERSION default value of 1 to 2 if we were writing ** an OpenGL ES 2.X application. **/ egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, NULL); if (egl_ctx == EGL_NO_CONTEXT) { egl_perror("eglCreateContext"); goto fail5; } /** ** eglMakeCurrent binds ctx to the current rendering thread and to a draw ** and a read surface. In our case, we want to draw to our EGL surface and ** don't really care about where we read from. EGL does not allow ** specifying EGL_NO_SURFACE for the read surface only, so we will simply ** use egl_surf for both reading and writing. Once eglMakeCurrent ** completes successfully, all OpenGL ES 1.X calls will be executed on ** the context and surface provided as arguments. **/ rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx); if (rc != EGL_TRUE) { egl_perror("eglMakeCurrent"); goto fail6; } /** ** The eglSwapInterval function specifies the minimum number of video ** frame periods per buffer swap for the window associated with the ** current context. If the interval is 0, the application renders as ** fast as it can. Interval values of 1 or more limit the rendering ** to fractions of the display's refresh rate, i.e. 60, 30, 20, 15, etc ** fps in the case of a display with a refresh rate of 60 Hz. **/ rc = eglSwapInterval(egl_disp, interval); if (rc != EGL_TRUE) { egl_perror("eglSwapInterval"); goto fail7; } /** ** If we didn't provide a size for our window, the window manager will ** have assigned our window a size during the realization process. We may ** or may not receive a KD_EVENT_WINDOWPROPERTY_CHANGE. Since we need to ** know the size of our window to properly initialize the OpenGL ES ** viewport and the geometry, we won't take any chances and ask for the ** size now. **/ if (kd_size[0] == -1 || kd_size[1] == -1) { kdGetWindowPropertyiv(kd_win, KD_WINDOWPROPERTY_SIZE, kd_size); } /** ** At this point, we can start doing OpenGL ES stuff. Our application is ** quite simple, so we'll just do the initialization here followed by ** some basic rendering in our application loop. **/ if (verbose) { printf("GL_VENDOR = %s\n", (char *)glGetString(GL_VENDOR)); printf("GL_RENDERER = %s\n", (char *)glGetString(GL_RENDERER)); printf("GL_VERSION = %s\n", (char *)glGetString(GL_VERSION)); printf("GL_EXTENSIONS = %s\n", (char *)glGetString(GL_EXTENSIONS)); } /* The resize function initializes the viewport and geometry */ resize(points, coords, kd_size[0], kd_size[1]); /* Create a texture object that doesn't require mipmaps to be complete */ glBindTexture(GL_TEXTURE_2D, 1); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); /** ** This is our OpenKODE application loop. It keeps on running unless an ** error occurs or we receive a close event from OpenKODE. The application ** loop consists of two parts. The first part processes any events that have ** been put in our queue. The second part does the rendering. When the ** window is visible, we don't wait if the event queue is empty and move ** on to the rendering part immediately. When the window is not visible ** we skip the rendering part. To avoid hogging the CPU, we put a 1 second ** delay in our event loop. **/ i = 0; while (1) { /** ** We start the loop by processing any events that might be in our ** queue. The only event that is of interest to us are the resize ** and close events. The timeout variable is set to 0 (no wait) or ** 1 second depending if the window is visible or invisible. **/ while ((event = kdWaitEvent(timeout)) != KD_NULL) { switch (event->type) { case KD_EVENT_WINDOW_CLOSE: /** ** All we have to do when we receive the close event is ** to exit the application loop. Because we have a loop ** within a loop, a simple break won't work. We'll just ** use a goto to take us out of here. **/ goto end; case KD_EVENT_WINDOWPROPERTY_CHANGE: /** ** There is no resize event in OpenKODE. What we get is a ** property change event where the name of the changed ** property is set to KD_WINDOWPROPERTY_SIZE. **/ switch (event->data.windowproperty.pname) { case KD_WINDOWPROPERTY_SIZE: /** ** The new size is not included in the event, so ** we must get it ourselves. **/ kdGetWindowPropertyiv(kd_win, KD_WINDOWPROPERTY_SIZE, kd_size); /** ** We must not forget to reset the viewport, projection ** matrix, and vertex positions. They all depend on the ** window size. **/ resize(points, coords, kd_size[0], kd_size[1]); break; case KD_WINDOWPROPERTY_VISIBILITY: /** ** The new visibility status is not included in the ** event, so we must get it ourselves. **/ kdGetWindowPropertybv(kd_win, KD_WINDOWPROPERTY_VISIBILITY, &kd_vis); /** ** Change the timeout when waiting for events to ** 1 second if the window is not visible, or no ** timeout when the window is visible. **/ if (kd_vis) { timeout = 0; } else { /* 1 second is 1,000,000,000 nanoseconds */ timeout = 1000000000; } break; } break; case KD_EVENT_INPUT_POINTER: /** ** To provide a way of gracefully terminating our application, ** we will exit if there is a pointer select event in the upper ** right corner of our window. This should happen if the mouse's ** left button is clicked or if a touchscreen display is pressed. ** The event will come as an OpenKODE KD_EVENT_INPUT_POINTER ** event, with an (x,y) coordinate relative to the window's upper ** left corner and a select value. We have to verify ourselves that ** the coordinates of the pointer are in the upper righthand area. ** We won't bother checking the OpenKODE I/O item index. We will ** simply process pointer events coming from any input source. **/ if (event->data.inputpointer.select) { if (event->data.inputpointer.x >= kd_size[0] - exit_area_size && event->data.inputpointer.x < kd_size[0] && event->data.inputpointer.y >= 0 && event->data.inputpointer.y < exit_area_size) { goto end; } } break; #ifdef KD_QNX_input case KD_QNX_EVENT_KEYBOARD: if (event->data.keyboardqnx.flags & KEY_DOWN) { switch (event->data.keyboardqnx.key_sym) { case KEYCODE_ESCAPE: goto end; case KEYCODE_F: kd_freeze = !kd_freeze; break; } } break; #endif default: /** ** We let OpenKODE's default event handler take care of ** all the other events we aren't interested in. **/ kdDefaultEvent(event); break; } } /** ** The second part of the application loop is the rendering. We want ** to skip the rendering part if our window is not visible. This will ** leave the CPU and GPU to other applications and make the system a ** little bit more responsive while we are invisible. **/ if (kd_vis && !kd_freeze) { /** ** The first pass will be rendering into the EGL pbuffer. We will ** use the contents of this buffer later as a texture. For this ** tutorial, there is no real need to have two separate contexts. ** We will simply use the same context to render to our pbuffer ** and then to draw using that texture. **/ rc = eglMakeCurrent(egl_disp, egl_pbuf, egl_pbuf, egl_ctx); if (rc != EGL_TRUE) { egl_perror("eglMakeCurrent"); goto end; } /* Update the viewport and projection matrix for the pbuffer */ glViewport(0, 0, 256, 256); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0.0f, (GLfloat)256, 0.0f, (GLfloat)256, -1.0f, 1.0f); /* Set the clear color to yellow */ glClearColor(1.0f, 1.0f, 0.0f, 1.0f); /* Start by clearing the window */ glClear(GL_COLOR_BUFFER_BIT); /* We want the hourglass to be drawn in gray */ glColor4f(0.62745f, 0.62745f, 0.62745f, 1.0f); /* We will use one vertex array for all of our rendering */ glVertexPointer(2, GL_SHORT, 0, (const GLvoid *)points); glEnableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); /* no texturing required in this pass */ glDisable(GL_TEXTURE_2D); /* Render the hourglass */ glDrawArrays(GL_TRIANGLES, 0, 6); /** ** Now we can use the pbuffer as a texture to do some drawing ** into the window surface. There is no need to flush before ** using the pbuffer, eglBindTexture will implicitely do one for ** us. **/ rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx); if (rc != EGL_TRUE) { egl_perror("eglMakeCurrent"); goto end; } /* Update the viewport and projection matrix for the window dimensions */ glViewport(0, 0, kd_size[0], kd_size[1]); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0.0f, (GLfloat)kd_size[0], 0.0f, (GLfloat)kd_size[1], -1.0f, 1.0f); /* setup texturing */ glEnable(GL_TEXTURE_2D); eglBindTexImage(egl_disp, egl_pbuf, EGL_BACK_BUFFER); /* We set the color to white to keep the colors in the texture */ glColor4f(1.0f, 1.0f, 1.0f, 1.0f); /* We will use one vertex array for all of our rendering */ glVertexPointer(2, GL_SHORT, 0, (const GLvoid *)&points[12]); glTexCoordPointer(2, GL_FLOAT, 0, (const GLvoid *)coords); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); /* Render using the hourglass texture */ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); /** ** It is important that we release the texture image to allow the ** next pass to render into the pbuffer again. Until ** eglReleaseTexImage is called, we will not be able to write to ** the pbuffer. **/ eglReleaseTexImage(egl_disp, egl_pbuf, EGL_BACK_BUFFER); /** ** Posting of the new frame requires a call to eglSwapBuffers. ** For now, this is true even when using single buffering. If an ** event has occured that invalidates the surface we are currently ** using, eglSwapBuffers will return EGL_FALSE and set the error ** code to EGL_BAD_NATIVE_WINDOW. At this point, we could destroy ** our EGL surface, close our OpenKODE window, and start again. ** This application will simply exit when any errors occur. **/ rc = eglSwapBuffers(egl_disp, egl_surf); if (rc != EGL_TRUE) { egl_perror("eglSwapBuffers"); break; } } } /** ** If we made it here, it means everything ran successfully. We'll thus ** change the exit return value to indicate success. **/ end: rval = EXIT_SUCCESS; /** ** Before we can destroy any of the resources we have created for this ** application, we must deactivate the rendering context that we were ** using and release the surfaces we were drawing to and reading from. ** This is done by calling eglMakeCurrent with EGL_NO_SURFACE and ** EGL_NO_CONTEXT for arguments. Note that the call to eglMakeCurrent ** will generate an error unless all arguments are EGL_NO_SURFACE and ** EGL_NO_CONTEXT, or all arguments are valid EGLSurface and EGLContext ** objects. **/ fail7: eglMakeCurrent(egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); /** ** Destroy the EGL render context if one was created. **/ fail6: eglDestroyContext(egl_disp, egl_ctx); /** ** Destroy the EGL surface if one was created. **/ fail5: eglDestroySurface(egl_disp, egl_pbuf); /** ** Destroy the EGL surface if one was created. **/ fail4: eglDestroySurface(egl_disp, egl_surf); /** ** Destroy the OpenKODE window of one was created. **/ fail3: kdDestroyWindow(kd_win); /** ** Terminate our connection to the EGL display. Since we are just about to ** exit, we can also release any resources that were allocated for this ** thread. This is done by calling eglReleaseThread. On most systems, ** those resources would probably be released automatically when the ** program exists. **/ fail2: eglTerminate(egl_disp); eglReleaseThread(); /** ** Return with EXIT_SUCCESS or EXIT_FAILURE. **/ fail1: return rval; } __SRCVERSION( "$URL: http://svn.ott.qnx.com/product/trunk/apps/kd/tutorials/gles1-kd-vsync/gles1- kd-vsync.c $ $Rev: 220409 $" );