From 2f0c785ba701f330c0fcc6fbbcf484af4a522144 Mon Sep 17 00:00:00 2001 From: janwas Date: Thu, 16 Jun 2005 22:21:12 +0000 Subject: [PATCH] reorganize code; add paranoid checking for core support of extensions in case the driver forgot to advertise some; remove deadwood (oglPrintErrors - oglCheck does the same); prefix the feature/limit variables with ogl_; add oglHaveExtensions call This was SVN commit r2396. --- source/lib/ogl.cpp | 308 +++++++++++++++++++++++++++++++-------------- source/lib/ogl.h | 91 +++++++------- 2 files changed, 264 insertions(+), 135 deletions(-) diff --git a/source/lib/ogl.cpp b/source/lib/ogl.cpp index d4ddef5d38..a2370ae43d 100755 --- a/source/lib/ogl.cpp +++ b/source/lib/ogl.cpp @@ -1,3 +1,21 @@ +// OpenGL helpers +// +// Copyright (c) 2002-2005 Jan Wassenberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// Contact info: +// Jan.Wassenberg@stud.uni-karlsruhe.de +// http://www.stud.uni-karlsruhe.de/~urkt/ + #include "precompiled.h" #include "lib.h" @@ -12,11 +30,15 @@ #ifdef _MSC_VER #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "glu32.lib") - // cannot get rid of glu32 - seems to be loaded by opengl32, - // even though dependency walker marks it as demand-loaded. + // glu32 is required - it is apparently pulled in by opengl32, + // even though depends.exe marks it as demand-loaded. #endif +//---------------------------------------------------------------------------- +// extensions +//---------------------------------------------------------------------------- + // define extension function pointers extern "C" { @@ -29,13 +51,86 @@ extern "C" static const char* exts = NULL; +static bool have_14, have_13, have_12; -// check if the extension is supported by the OpenGL implementation +// return a C string of unspecified length containing a space-separated +// list of all extensions the OpenGL implementation advertises. +// (useful for crash logs). +const char* oglExtList() +{ + assert(exts && "call oglInit before using this function"); + return exts; +} + + +// paranoia: newer drivers may forget to advertise an extension +// indicating support for something that has been folded into the core. +// we therefore check for all extensions known to be offered by the +// GL implementation present on the user's system; oglHaveExtension will +// take this into account. +// the app can therefore just ask for extensions and not worry about this. +static bool isImplementedInCore(const char* ext) +{ +#define MATCH(known_ext)\ + if(!strcmp(ext, #known_ext))\ + return true; + + if(have_14) + { + MATCH(GL_SGIS_generate_mipmap); + MATCH(GL_NV_blend_square); + MATCH(GL_ARB_depth_texture); + MATCH(GL_ARB_shadow); + MATCH(GL_EXT_fog_coord); + MATCH(GL_EXT_multi_draw_arrays); + MATCH(GL_ARB_point_parameters); + MATCH(GL_EXT_secondary_color); + MATCH(GL_EXT_blend_func_separate); + MATCH(GL_EXT_stencil_wrap); + MATCH(GL_ARB_texture_env_crossbar); + MATCH(GL_EXT_texture_lod_bias); + MATCH(GL_ARB_texture_mirrored_repeat); + MATCH(GL_ARB_window_pos); + } + if(have_13) + { + MATCH(GL_ARB_texture_compression); + MATCH(GL_ARB_texture_cube_map); + MATCH(GL_ARB_multisample); + MATCH(GL_ARB_multitexture); + MATCH(GL_ARB_transpose_matrix); + MATCH(GL_ARB_texture_env_add); + MATCH(GL_ARB_texture_env_combine); + MATCH(GL_ARB_texture_env_dot3); + MATCH(GL_ARB_texture_border_clamp); + } + if(have_12) + { + MATCH(GL_EXT_texture3d); + MATCH(GL_EXT_bgra); + MATCH(GL_EXT_packed_pixels); + MATCH(GL_EXT_rescale_normal); + MATCH(GL_EXT_separate_specular_color); + MATCH(GL_SGIS_texture_edge_clamp); + MATCH(GL_SGIS_texture_lod); + MATCH(GL_EXT_draw_range_elements); + } + +#undef MATCH + return false; +} + + +// check if the extension is supported by the OpenGL implementation. +// takes subsequently added core support for some extensions into account. bool oglHaveExtension(const char* ext) { assert(exts && "call oglInit before using this function"); + if(isImplementedInCore(ext)) + return true; + const char *p = exts, *end; // make sure ext is valid & doesn't contain spaces @@ -51,9 +146,9 @@ bool oglHaveExtension(const char* ext) // make sure the substring found is an entire extension string, // i.e. it starts and ends with ' ' - if(p == exts || *(p-1) == ' ') // valid start? - if(*end == ' ' || *end == '\0') // valid end? - return true; + if((p == exts || p[-1] == ' ') && // valid start AND + (*end == ' ' || *end == '\0')) // valid end + return true; p = end; } } @@ -78,18 +173,78 @@ bool oglHaveVersion(const char* desired_version) return false; } - return (major > desired_major - || (major == desired_major && minor >= desired_minor)); + return (major > desired_major) || + (major == desired_major && minor >= desired_minor); } -#ifdef OGL_CHECKS +// check if all given extension strings (passed as const char* parameters, +// terminated by a 0 pointer) are supported by the OpenGL implementation, +// as determined by oglHaveExtension. +// returns 0 if all are present; otherwise, the first extension in the +// list that's not supported (useful for reporting errors). +// +// note: dummy parameter is necessary to access parameter va_list. +// +// +// rationale: this interface is more convenient than individual +// oglHaveExtension calls and allows reporting which extension is missing. +// +// one disadvantage is that there is no way to indicate that either one +// of 2 extensions would be acceptable, e.g. (ARB|EXT)_texture_env_dot3. +// this is isn't so bad, since they wouldn't be named differently +// if there weren't non-trivial changes between them. for that reason, +// we refrain from equivalence checks (which would boil down to +// string-matching known extensions to their equivalents). +const char* oglHaveExtensions(int dummy, ...) +{ + const char* ext; + + va_list args; + va_start(args, dummy); + for(;;) + { + ext = va_arg(args, const char*); + // end of list reached; all were present => return 0. + if(!ext) + break; + + // not found => return name of missing extension. + if(!oglHaveExtension(ext)) + break; + } + va_end(args); + + return ext; +} + + +static void importExtensionFunctions() +{ +#define FUNC(ret, name, params) *(void**)&name = SDL_GL_GetProcAddress(#name); +#define FUNC2(ret, nameARB, nameCore, version, params) \ + nameARB = NULL; \ + if(oglHaveVersion(version)) \ + *(void**)&nameARB = SDL_GL_GetProcAddress(#nameCore); \ + if(!nameARB) /* use the ARB name if the driver lied about what version it supports */ \ + *(void**)&nameARB = SDL_GL_GetProcAddress(#nameARB); +#include "glext_funcs.h" +#undef FUNC2 +#undef FUNC + // It should be safe to load the ARB function pointers even if the + // extension isn't advertised, since we won't actually use them without + // checking for the extension. +} + + +//---------------------------------------------------------------------------- + void oglCheck() { - unsigned int err = glGetError(); - if (err != GL_NO_ERROR) + GLenum err = glGetError(); + if(err != GL_NO_ERROR) { - debug_printf("GL errors!\n"); + debug_printf("GL error: "); #define E(e) case e: debug_printf("%s\n", #e); break; switch (err) @@ -106,33 +261,16 @@ void oglCheck() debug_break(); } } -#endif // #ifdef OGL_CHECKS - -void oglPrintErrors() -{ -#define E(e) case e: debug_printf("%s\n", #e); break; - - for(;;) - switch(glGetError()) - { - E(GL_INVALID_ENUM) - E(GL_INVALID_VALUE) - E(GL_INVALID_OPERATION) - E(GL_STACK_OVERFLOW) - E(GL_STACK_UNDERFLOW) - E(GL_OUT_OF_MEMORY) - - default: - return; - } -} -int max_tex_size; // [pixels] -int tex_units; -int max_VAR_elements = -1; // GF2: 64K; GF3: 1M -bool tex_compression_avail; // S3TC / DXT{1,3,5} -int video_mem; // [MiB]; approximate +//---------------------------------------------------------------------------- +// feature and limit detect +//---------------------------------------------------------------------------- + +int ogl_max_tex_size = -1; // [pixels] +int ogl_max_tex_units = -1; // limit on GL_TEXTUREn +int ogl_max_VAR_elements = -1; // GF2: 64K; GF3: 1M +int ogl_tex_compression_supported = -1; // S3TC / DXT{1,3,5} // gfx_card and gfx_drv_ver are unchanged on failure. @@ -167,77 +305,64 @@ int ogl_get_gfx_info() } -const char* oglExtList() +static void CALL_CONV emulate_glCompressedTexImage2D(GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); + +static void detectFeatures() { - assert(exts && "call oglInit before using this function"); - return exts; -} - - -void CALL_CONV oglEmulateCompressedTexImage2D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); - -// call after each video mode change -void oglInit() -{ - exts = (const char*)glGetString(GL_EXTENSIONS); - if(!exts) - { - debug_warn("oglInit called before OpenGL is ready for use"); - } - - // import functions -#define FUNC(ret, name, params) *(void**)&name = SDL_GL_GetProcAddress(#name); -#define FUNC2(ret, nameARB, nameCore, version, params) \ - nameARB = NULL; \ - if(oglHaveVersion(version)) \ - *(void**)&nameARB = SDL_GL_GetProcAddress(#nameCore); \ - if(!nameARB) /* use the ARB name if the driver lied about what version it supports */ \ - *(void**)&nameARB = SDL_GL_GetProcAddress(#nameARB); -#include "glext_funcs.h" -#undef FUNC2 -#undef FUNC - // It should be safe to load the ARB function pointers even if the - // extension isn't advertised, since we won't actually use them without - // checking for the extension. - - // detect OpenGL / graphics card caps - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); - glGetIntegerv(GL_MAX_TEXTURE_UNITS, &tex_units); - // make sure value is -1 if not supported + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ogl_max_tex_size); + glGetIntegerv(GL_MAX_TEXTURE_UNITS, &ogl_max_tex_units); + // make sure value remains -1 if not supported if(oglHaveExtension("GL_NV_vertex_array_range")) - glGetIntegerv(GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV, &max_VAR_elements); + glGetIntegerv(GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV, &ogl_max_VAR_elements); - tex_compression_avail = (oglHaveExtension("GL_ARB_texture_compression") || oglHaveVersion("1.3")) && - (oglHaveExtension("GL_EXT_texture_compression_s3tc")); + ogl_tex_compression_supported = oglHaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", 0) == 0; // TODO: GL_S3_s3tc? It uses different enumerants (GL_RGB_S3TC etc), so the // texture loading code would need to be changed; and it is not clear whether // it supports the full range of DXT1/3/5. (There seems to be no specification; // and many header files don't have GL_RGBA_DXT5_S3TC, suggesting that the // drivers don't all support that.) - if(!tex_compression_avail) + if(!ogl_tex_compression_supported) { // If there's no hardware support for compressed textures, do the // decompression in software (but first let the user know it's probably not // going to be very fast). wdisplay_msg(L"Performance warning", L"Your graphics card does not support compressed textures. The game will try to continue anyway, but may be slower than expected. Please try updating your graphics drivers; if that doesn't help, please try upgrading your hardware."); - // TODO: i18n - glCompressedTexImage2DARB = oglEmulateCompressedTexImage2D; - - // Leave tex_compression_avail == false, so that it indicates the presence + // TODO: i18n + glCompressedTexImage2DARB = emulate_glCompressedTexImage2D; + + // Leave ogl_tex_compression_supported == 0, so that it indicates the presence // of hardware-supported texture compression. } - - video_mem = (SDL_GetVideoInfo()->video_mem) / 1048576; // [MiB] - // TODO: add sizeof(FB)? } -void CALL_CONV oglEmulateCompressedTexImage2D - (GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, - GLsizei imageSize, const GLvoid* data) +// call after each video mode change, since thereafter extension functions +// may have changed [address]. +void oglInit() +{ + // cache extension list and versions for oglHave*. + // note: this is less about performance (since the above are not + // time-critical) than centralizing the 'OpenGL is ready' check. + exts = (const char*)glGetString(GL_EXTENSIONS); + if(!exts) + { + debug_warn("oglInit called before OpenGL is ready for use"); + } + have_12 = oglHaveVersion("1.2"); + have_13 = oglHaveVersion("1.3"); + have_14 = oglHaveVersion("1.4"); + + importExtensionFunctions(); + + detectFeatures(); +} + + +static void CALL_CONV emulate_glCompressedTexImage2D( + GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid* data) { // Software emulation of compressed-texture support, for really old // cards/drivers that can't do it (but which do support everything else @@ -245,7 +370,7 @@ void CALL_CONV oglEmulateCompressedTexImage2D // textures, and are slow anyway, so it's not going to be a pleasant way // of playing; but at least it's better than nothing. - GLenum base_fmt = (internalformat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ? GL_RGB : GL_RGBA); + GLenum base_fmt = (internalformat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT)? GL_RGB : GL_RGBA; // TODO: handle small (<4x4) images correctly GLsizei blocks_w = (GLsizei)(round_up(width, 4) / 4); @@ -375,7 +500,8 @@ void CALL_CONV oglEmulateCompressedTexImage2D } } - glTexImage2D(target, level, base_fmt==GL_RGB? GL_RGB8 : GL_RGBA8, width, height, border, base_fmt, GL_UNSIGNED_BYTE, rgb_data); + const GLint int_fmt = (base_fmt == GL_RGB)? GL_RGB8 : GL_RGBA8; + glTexImage2D(target, level, int_fmt, width, height, border, base_fmt, GL_UNSIGNED_BYTE, rgb_data); free(rgb_data); } diff --git a/source/lib/ogl.h b/source/lib/ogl.h index d3f51eab56..9b65287157 100755 --- a/source/lib/ogl.h +++ b/source/lib/ogl.h @@ -1,15 +1,6 @@ #ifndef __OGL_H__ #define __OGL_H__ - -// Enable oglCheck(), which breaks into the debugger whenever -// an OpenGL call fails. (Then insert dozens of calls to oglCheck() -// to locate the cause of the problem.) -#if !( defined(NDEBUG) || defined(TESTING) ) -# define OGL_CHECKS -#endif - - #ifdef __cplusplus extern "C" { #endif @@ -20,7 +11,7 @@ extern "C" { // -// OpenGL header +// bring in the platform's OpenGL headers (with fixes, if necessary) // #ifdef __APPLE__ @@ -31,11 +22,6 @@ extern "C" { # include #endif - -// -// glext -// - // if gl.h provides real prototypes for 1.2 / 1.3 functions, // exclude the corresponding function pointers in glext_funcs.h #ifdef GL_VERSION_1_2 @@ -52,16 +38,41 @@ extern "C" { # include # ifdef _WIN32 # include -# endif +# endif #endif #define GL_TEXTURE_IMAGE_SIZE_ARB 0x86A0 // -// function pointer declarations +// extensions // +// check if the extension is supported by the OpenGL implementation. +// takes subsequently added core support for some extensions into account. +extern bool oglHaveExtension(const char* ext); + +// check if the OpenGL implementation is at least at . +// (format: "%d.%d" major minor) +extern bool oglHaveVersion(const char* version); + +// check if all given extension strings (passed as const char* parameters, +// terminated by a 0 pointer) are supported by the OpenGL implementation, +// as determined by oglHaveExtension. +// returns 0 if all are present; otherwise, the first extension in the +// list that's not supported (useful for reporting errors). +// +// note: dummy parameter is necessary to access parameter va_list. +// +// rationale: see source. +extern const char* oglHaveExtensions(int dummy, ...); + +// return a C string of unspecified length containing a space-separated +// list of all extensions the OpenGL implementation advertises. +// (useful for crash logs). +extern const char* oglExtList(void); + + #ifdef _WIN32 # define CALL_CONV __stdcall #else @@ -77,29 +88,31 @@ extern "C" { // leave CALL_CONV defined for ogl.cpp - // -// OpenGL util +// limit / feature detect // -extern int max_tex_size; // [pixels] -extern int tex_units; -extern int max_VAR_elements; // GF2: 64K; GF3: 1M -extern bool tex_compression_avail; // S3TC / DXT{1,3,5} -extern int video_mem; // [MiB]; approximate +extern int ogl_max_tex_size; // [pixels] +extern int ogl_max_tex_units; // limit on GL_TEXTUREn +extern int ogl_max_VAR_elements; // GF2: 64K; GF3: 1M +extern int ogl_tex_compression_supported; // S3TC / DXT{1,3,5} + +// set detect.cpp gfx_card[] and gfx_drv_ver[]. +// (called by detect.cpp get_gfx_info()). +// +// fails if OpenGL not ready for use. +// gfx_card and gfx_drv_ver are unchanged on failure. +extern int ogl_get_gfx_info(void); -// check if the extension is supported by the OpenGL implementation -extern bool oglHaveExtension(const char* ext); +// +// misc +// -// check if the OpenGL implementation is at least at . -// (format: "%d.%d" major minor) -extern bool oglHaveVersion(const char* version); - -// print all OpenGL errors -extern void oglPrintErrors(void); - -#ifdef OGL_CHECKS +// in non-release builds, enable oglCheck, which breaks into the debugger +// if an OpenGL error was raised since the last call. +// add these calls everywhere to close in on the error cause. +#ifndef NDEBUG extern void oglCheck(void); #else # define oglCheck() @@ -110,16 +123,6 @@ extern void oglCheck(void); // fails if OpenGL not ready for use. extern void oglInit(void); -// set detect.cpp gfx_card[] and gfx_drv_ver[]. -// (called by detect.cpp get_gfx_info()). -// -// fails if OpenGL not ready for use. -// gfx_card and gfx_drv_ver are unchanged on failure. -extern int ogl_get_gfx_info(void); - -// return a NULL-terminated string (of unlimited length) containing -// a space-separated list of supported extensions. -extern const char* oglExtList(void); #ifdef __cplusplus }