nkeynes@635 | 1 | /**
|
nkeynes@636 | 2 | * $Id$
|
nkeynes@635 | 3 | *
|
nkeynes@635 | 4 | * GL-based support functions
|
nkeynes@635 | 5 | *
|
nkeynes@635 | 6 | * Copyright (c) 2005 Nathan Keynes.
|
nkeynes@635 | 7 | *
|
nkeynes@635 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@635 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@635 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@635 | 11 | * (at your option) any later version.
|
nkeynes@635 | 12 | *
|
nkeynes@635 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@635 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@635 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@635 | 16 | * GNU General Public License for more details.
|
nkeynes@635 | 17 | */
|
nkeynes@736 | 18 |
|
nkeynes@645 | 19 | #include <string.h>
|
nkeynes@1129 | 20 | #include <stdlib.h>
|
nkeynes@687 | 21 | #include <glib/gstrfuncs.h>
|
nkeynes@635 | 22 | #include "pvr2/glutil.h"
|
nkeynes@635 | 23 |
|
nkeynes@667 | 24 | gboolean isGLSecondaryColorSupported()
|
nkeynes@667 | 25 | {
|
nkeynes@736 | 26 | return isGLExtensionSupported("GL_EXT_secondary_color");
|
nkeynes@667 | 27 | }
|
nkeynes@667 | 28 |
|
nkeynes@667 | 29 | gboolean isGLVertexBufferSupported()
|
nkeynes@667 | 30 | {
|
nkeynes@736 | 31 | return isGLExtensionSupported("GL_ARB_vertex_buffer_object");
|
nkeynes@667 | 32 | }
|
nkeynes@667 | 33 |
|
nkeynes@667 | 34 | gboolean isGLPixelBufferSupported()
|
nkeynes@667 | 35 | {
|
nkeynes@736 | 36 | return isGLExtensionSupported("GL_ARB_pixel_buffer_object");
|
nkeynes@667 | 37 | }
|
nkeynes@667 | 38 |
|
nkeynes@667 | 39 | gboolean isGLMirroredTextureSupported()
|
nkeynes@667 | 40 | {
|
nkeynes@736 | 41 | return isGLExtensionSupported("GL_ARB_texture_mirrored_repeat");
|
nkeynes@667 | 42 | }
|
nkeynes@667 | 43 |
|
nkeynes@1140 | 44 | /**
|
nkeynes@1140 | 45 | * Check if there's at least 2 texture units
|
nkeynes@1140 | 46 | */
|
nkeynes@1140 | 47 | gboolean isGLMultitextureSupported()
|
nkeynes@1140 | 48 | {
|
nkeynes@1140 | 49 | if( !isGLExtensionSupported("GL_ARB_multitexture") )
|
nkeynes@1140 | 50 | return FALSE;
|
nkeynes@1140 | 51 | int units = 0;
|
nkeynes@1219 | 52 |
|
nkeynes@1219 | 53 | #if defined(GL_MAX_TEXTURE_UNITS)
|
nkeynes@1219 | 54 | glGetIntegerv(GL_MAX_TEXTURE_UNITS, &units);
|
nkeynes@1219 | 55 | #elif defined(GL_MAX_TEXTURE_IMAGE_UNITS)
|
nkeynes@1219 | 56 | glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &units);
|
nkeynes@1219 | 57 | #endif
|
nkeynes@1140 | 58 | return units >= 2;
|
nkeynes@1140 | 59 | }
|
nkeynes@1140 | 60 |
|
nkeynes@1134 | 61 | gboolean isGLVertexRangeSupported()
|
nkeynes@1134 | 62 | {
|
nkeynes@1134 | 63 | return isGLExtensionSupported("GL_APPLE_vertex_array_range") ||
|
nkeynes@1134 | 64 | isGLExtensionSupported("GL_NV_vertex_array_range");
|
nkeynes@1134 | 65 | }
|
nkeynes@1134 | 66 |
|
nkeynes@635 | 67 | /**
|
nkeynes@635 | 68 | * Test if a specific extension is supported. From opengl.org
|
nkeynes@635 | 69 | * @param extension extension name to check for
|
nkeynes@635 | 70 | * @return TRUE if supported, otherwise FALSE.
|
nkeynes@635 | 71 | */
|
nkeynes@635 | 72 | gboolean isGLExtensionSupported( const char *extension )
|
nkeynes@635 | 73 | {
|
nkeynes@635 | 74 | const GLubyte *extensions = NULL;
|
nkeynes@635 | 75 | const GLubyte *start;
|
nkeynes@635 | 76 | GLubyte *where, *terminator;
|
nkeynes@635 | 77 |
|
nkeynes@635 | 78 | /* Extension names should not have spaces. */
|
nkeynes@635 | 79 | where = (GLubyte *) strchr(extension, ' ');
|
nkeynes@635 | 80 | if (where || *extension == '\0')
|
nkeynes@736 | 81 | return 0;
|
nkeynes@635 | 82 | extensions = glGetString(GL_EXTENSIONS);
|
nkeynes@645 | 83 | if( extensions == NULL ) {
|
nkeynes@736 | 84 | /* No GL available, so we're pretty sure the extension isn't
|
nkeynes@736 | 85 | * available either. */
|
nkeynes@736 | 86 | return FALSE;
|
nkeynes@645 | 87 | }
|
nkeynes@635 | 88 | /* It takes a bit of care to be fool-proof about parsing the
|
nkeynes@635 | 89 | OpenGL extensions string. Don't be fooled by sub-strings,
|
nkeynes@635 | 90 | etc. */
|
nkeynes@635 | 91 | start = extensions;
|
nkeynes@635 | 92 | for (;;) {
|
nkeynes@736 | 93 | where = (GLubyte *) strstr((const char *) start, extension);
|
nkeynes@736 | 94 | if (!where)
|
nkeynes@736 | 95 | break;
|
nkeynes@736 | 96 | terminator = where + strlen(extension);
|
nkeynes@736 | 97 | if (where == start || *(where - 1) == ' ')
|
nkeynes@736 | 98 | if (*terminator == ' ' || *terminator == '\0')
|
nkeynes@736 | 99 | return TRUE;
|
nkeynes@736 | 100 | start = terminator;
|
nkeynes@635 | 101 | }
|
nkeynes@635 | 102 | return FALSE;
|
nkeynes@635 | 103 | }
|
nkeynes@687 | 104 |
|
nkeynes@1129 | 105 | int compare_charp( const void *a, const void *b )
|
nkeynes@1129 | 106 | {
|
nkeynes@1129 | 107 | const char **ca = (const char **)a;
|
nkeynes@1129 | 108 | const char **cb = (const char **)b;
|
nkeynes@1129 | 109 | return strcmp(*ca, *cb);
|
nkeynes@1129 | 110 | }
|
nkeynes@1129 | 111 |
|
nkeynes@1134 | 112 | #define DEFAULT_TERMINAL_COLUMNS 80
|
nkeynes@1134 | 113 | #define DEFAULT_COLUMN_WIDTH 34
|
nkeynes@1134 | 114 |
|
nkeynes@1223 | 115 | int glGetMaxColourAttachments()
|
nkeynes@1223 | 116 | {
|
nkeynes@1223 | 117 | #ifdef GL_MAX_COLOR_ATTACHMENTS
|
nkeynes@1223 | 118 | GLint result = 0;
|
nkeynes@1223 | 119 | glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &result);
|
nkeynes@1223 | 120 | return result;
|
nkeynes@1223 | 121 | #else
|
nkeynes@1223 | 122 | return 1;
|
nkeynes@1223 | 123 | #endif
|
nkeynes@1223 | 124 | }
|
nkeynes@1223 | 125 |
|
nkeynes@1223 | 126 |
|
nkeynes@1134 | 127 | /**
|
nkeynes@1236 | 128 | * Define an orthographic projection matrix
|
nkeynes@1236 | 129 | * Note: row-major order
|
nkeynes@1236 | 130 | */
|
nkeynes@1236 | 131 | void defineOrthoMatrix( GLfloat *matrix, GLfloat width, GLfloat height, GLfloat znear, GLfloat zfar )
|
nkeynes@1236 | 132 | {
|
nkeynes@1236 | 133 | matrix[0] = 2/width;
|
nkeynes@1236 | 134 | matrix[1] = 0;
|
nkeynes@1236 | 135 | matrix[2] = 0;
|
nkeynes@1236 | 136 | matrix[3] = 0;
|
nkeynes@1236 | 137 |
|
nkeynes@1236 | 138 | matrix[4] = 0;
|
nkeynes@1236 | 139 | matrix[5] = -2/height;
|
nkeynes@1236 | 140 | matrix[6] = 0;
|
nkeynes@1236 | 141 | matrix[7] = 0;
|
nkeynes@1236 | 142 |
|
nkeynes@1236 | 143 | matrix[8] = 0;
|
nkeynes@1236 | 144 | matrix[9] = 0;
|
nkeynes@1236 | 145 | matrix[10]= -2/(zfar-znear);
|
nkeynes@1236 | 146 | matrix[11]= 0;
|
nkeynes@1236 | 147 |
|
nkeynes@1236 | 148 | matrix[12]= -1;
|
nkeynes@1236 | 149 | matrix[13]= 1;
|
nkeynes@1236 | 150 | matrix[14]= -(zfar+znear)/(zfar-znear);
|
nkeynes@1236 | 151 | matrix[15]= 1;
|
nkeynes@1236 | 152 | }
|
nkeynes@1236 | 153 |
|
nkeynes@1236 | 154 | /**
|
nkeynes@1134 | 155 | * Format a GL extension list (or other space-separated string) nicely, and
|
nkeynes@1134 | 156 | * print to the given output stream.
|
nkeynes@1134 | 157 | */
|
nkeynes@1134 | 158 |
|
nkeynes@1134 | 159 | void fprint_extensions( FILE *out, const char *extensions )
|
nkeynes@687 | 160 | {
|
nkeynes@1134 | 161 | unsigned int i, j, count, maxlen = DEFAULT_COLUMN_WIDTH, columns, per_column, terminal_columns;
|
nkeynes@1134 | 162 | const char *terminal_columns_str = getenv("COLUMNS");
|
nkeynes@1134 | 163 | if( terminal_columns_str == NULL || (terminal_columns = strtol(terminal_columns_str,0,10)) == 0 )
|
nkeynes@1134 | 164 | terminal_columns = DEFAULT_TERMINAL_COLUMNS;
|
nkeynes@1129 | 165 |
|
nkeynes@1134 | 166 | if( extensions == NULL || extensions[0] == '\0' )
|
nkeynes@1134 | 167 | return;
|
nkeynes@1134 | 168 |
|
nkeynes@1134 | 169 | gchar *ext_dup = g_strdup(extensions);
|
nkeynes@1134 | 170 | gchar **ext_split = g_strsplit(g_strstrip(extensions), " ", 0);
|
nkeynes@1134 | 171 | for( count = 0; ext_split[count] != NULL; count++ ) {
|
nkeynes@1134 | 172 | unsigned len = strlen(ext_split[count]);
|
nkeynes@1134 | 173 | if( len > maxlen )
|
nkeynes@1134 | 174 | maxlen = len;
|
nkeynes@1134 | 175 | }
|
nkeynes@1134 | 176 |
|
nkeynes@1134 | 177 | columns = terminal_columns / (maxlen+2);
|
nkeynes@1134 | 178 | if( columns == 0 )
|
nkeynes@1134 | 179 | columns = 1;
|
nkeynes@1134 | 180 | per_column = (count+columns-1) / columns;
|
nkeynes@1129 | 181 |
|
nkeynes@1129 | 182 | qsort(ext_split, count, sizeof(gchar *), compare_charp);
|
nkeynes@736 | 183 |
|
nkeynes@1134 | 184 | for( i=0; i<per_column; i++ ) {
|
nkeynes@1134 | 185 | for( j=0; j<columns; j++ ) {
|
nkeynes@1134 | 186 | unsigned idx = i + (j*per_column);
|
nkeynes@1134 | 187 | if( idx < count )
|
nkeynes@1134 | 188 | fprintf( out, " %-*s", maxlen, ext_split[idx] );
|
nkeynes@1134 | 189 | }
|
nkeynes@1134 | 190 | fprintf( out, "\n" );
|
nkeynes@1134 | 191 | }
|
nkeynes@1134 | 192 | g_strfreev(ext_split);
|
nkeynes@1134 | 193 | g_free(ext_dup);
|
nkeynes@1134 | 194 | }
|
nkeynes@1134 | 195 |
|
nkeynes@1134 | 196 | void glPrintInfo( FILE *out )
|
nkeynes@1134 | 197 | {
|
nkeynes@687 | 198 | fprintf( out, "GL Vendor: %s\n", glGetString(GL_VENDOR) );
|
nkeynes@687 | 199 | fprintf( out, "GL Renderer: %s\n", glGetString(GL_RENDERER) );
|
nkeynes@687 | 200 | fprintf( out, "GL Version: %s\n", glGetString(GL_VERSION) );
|
nkeynes@1134 | 201 | if( glsl_is_supported() ) {
|
nkeynes@1134 | 202 | const char * version = glsl_get_version();
|
nkeynes@1134 | 203 | fprintf( out, "SL Version: %s\n", version );
|
nkeynes@1134 | 204 | }
|
nkeynes@736 | 205 |
|
nkeynes@1134 | 206 | fprintf( out, "GL Extensions:\n" );
|
nkeynes@1134 | 207 |
|
nkeynes@1134 | 208 | fprint_extensions( out, (const gchar *)glGetString(GL_EXTENSIONS) );
|
nkeynes@1134 | 209 | if( display_driver && display_driver->print_info ) {
|
nkeynes@1134 | 210 | fprintf( out, "\n");
|
nkeynes@1134 | 211 | display_driver->print_info(out);
|
nkeynes@687 | 212 | }
|
nkeynes@687 | 213 | }
|
nkeynes@1150 | 214 |
|
nkeynes@1150 | 215 | gboolean gl_check_error(const char *context)
|
nkeynes@1150 | 216 | {
|
nkeynes@1150 | 217 | GLint err = glGetError();
|
nkeynes@1150 | 218 | if( err != 0 ) {
|
nkeynes@1150 | 219 | const char *s;
|
nkeynes@1150 | 220 | switch( err ) {
|
nkeynes@1150 | 221 | case GL_INVALID_ENUM: s = "Invalid enum"; break;
|
nkeynes@1150 | 222 | case GL_INVALID_VALUE: s = "Invalid value"; break;
|
nkeynes@1150 | 223 | case GL_INVALID_OPERATION: s = "Invalid operation"; break;
|
nkeynes@1219 | 224 | #ifdef GL_STACK_OVERFLOW
|
nkeynes@1150 | 225 | case GL_STACK_OVERFLOW: s = "Stack overflow"; break;
|
nkeynes@1220 | 226 | #endif
|
nkeynes@1220 | 227 | #ifdef GL_STACK_UNDERFLOW
|
nkeynes@1150 | 228 | case GL_STACK_UNDERFLOW: s = "Stack underflow"; break;
|
nkeynes@1220 | 229 | #endif
|
nkeynes@1150 | 230 | case GL_OUT_OF_MEMORY: s = "Out of memory"; break;
|
nkeynes@1150 | 231 | default: s = "Unknown error"; break;
|
nkeynes@1150 | 232 | }
|
nkeynes@1150 | 233 | if( context ) {
|
nkeynes@1150 | 234 | WARN( "%s: GL error: %x (%s)\n", context, err, s );
|
nkeynes@1150 | 235 | } else {
|
nkeynes@1150 | 236 | WARN( "GL error: %x (%s)\n", err, s );
|
nkeynes@1150 | 237 | }
|
nkeynes@1159 | 238 | return FALSE;
|
nkeynes@1150 | 239 | }
|
nkeynes@1159 | 240 | return TRUE;
|
nkeynes@1150 | 241 | }
|