LCOV - code coverage report
Current view: top level - source/lib - ogl.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 191 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 17 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : #include "precompiled.h"
      24             : 
      25             : #include "lib/ogl.h"
      26             : 
      27             : #include "lib/code_annotation.h"
      28             : #include "lib/config2.h"
      29             : #include "lib/debug.h"
      30             : #include "lib/external_libraries/libsdl.h"
      31             : #include "ps/CLogger.h"
      32             : 
      33             : #if !CONFIG2_GLES
      34             : # if OS_WIN
      35             : #  include <glad/wgl.h>
      36             : # elif !OS_MACOSX && !OS_MAC
      37             : #  include <glad/glx.h>
      38             : # endif
      39             : #endif
      40             : 
      41             : #include <stdio.h>
      42             : #include <string.h>
      43             : #include <stdarg.h>
      44             : 
      45             : 
      46             : //----------------------------------------------------------------------------
      47             : // extensions
      48             : //----------------------------------------------------------------------------
      49             : 
      50             : static const char* exts = nullptr;
      51             : 
      52             : static bool have_30 = false;
      53             : static bool have_21 = false;
      54             : static bool have_20 = false;
      55             : static bool have_15 = false;
      56             : static bool have_14 = false;
      57             : static bool have_13 = false;
      58             : static bool have_12 = false;
      59             : 
      60             : 
      61             : // return a C string of unspecified length containing a space-separated
      62             : // list of all extensions the OpenGL implementation advertises.
      63             : // (useful for crash logs).
      64           0 : const char* ogl_ExtensionString()
      65             : {
      66           0 :     ENSURE(exts && "call ogl_Init before using this function");
      67           0 :     return exts;
      68             : }
      69             : 
      70             : 
      71             : // paranoia: newer drivers may forget to advertise an extension
      72             : // indicating support for something that has been folded into the core.
      73             : // we therefore check for all extensions known to be offered by the
      74             : // GL implementation present on the user's system; ogl_HaveExtension will
      75             : // take this into account.
      76             : // the app can therefore just ask for extensions and not worry about this.
      77           0 : static bool isImplementedInCore(const char* ext)
      78             : {
      79             : #define MATCH(known_ext)\
      80             :     if(!strcmp(ext, #known_ext))\
      81             :         return true;
      82             : 
      83           0 :     if(have_30)
      84             :     {
      85           0 :         MATCH(GL_EXT_gpu_shader4);
      86           0 :         MATCH(GL_NV_conditional_render);
      87           0 :         MATCH(GL_ARB_color_buffer_float);
      88           0 :         MATCH(GL_ARB_depth_buffer_float);
      89           0 :         MATCH(GL_ARB_texture_float);
      90           0 :         MATCH(GL_EXT_packed_float);
      91           0 :         MATCH(GL_EXT_texture_shared_exponent);
      92           0 :         MATCH(GL_EXT_framebuffer_object);
      93           0 :         MATCH(GL_NV_half_float);
      94           0 :         MATCH(GL_ARB_half_float_pixel);
      95           0 :         MATCH(GL_EXT_framebuffer_multisample);
      96           0 :         MATCH(GL_EXT_framebuffer_blit);
      97           0 :         MATCH(GL_EXT_texture_integer);
      98           0 :         MATCH(GL_EXT_texture_array);
      99           0 :         MATCH(GL_EXT_packed_depth_stencil);
     100           0 :         MATCH(GL_EXT_draw_buffers2);
     101           0 :         MATCH(GL_EXT_texture_compression_rgtc);
     102           0 :         MATCH(GL_EXT_transform_feedback);
     103           0 :         MATCH(GL_APPLE_vertex_array_object);
     104           0 :         MATCH(GL_EXT_framebuffer_sRGB);
     105             :     }
     106           0 :     if(have_21)
     107             :     {
     108           0 :         MATCH(GL_ARB_pixel_buffer_object);
     109           0 :         MATCH(GL_EXT_texture_sRGB);
     110             :     }
     111           0 :     if(have_20)
     112             :     {
     113           0 :         MATCH(GL_ARB_shader_objects);
     114           0 :         MATCH(GL_ARB_vertex_shader);
     115           0 :         MATCH(GL_ARB_fragment_shader);
     116           0 :         MATCH(GL_ARB_shading_language_100);
     117           0 :         MATCH(GL_ARB_draw_buffers);
     118           0 :         MATCH(GL_ARB_texture_non_power_of_two);
     119           0 :         MATCH(GL_ARB_point_sprite);
     120           0 :         MATCH(GL_EXT_blend_equation_separate);
     121             :     }
     122           0 :     if(have_15)
     123             :     {
     124           0 :         MATCH(GL_ARB_vertex_buffer_object);
     125           0 :         MATCH(GL_ARB_occlusion_query);
     126           0 :         MATCH(GL_EXT_shadow_funcs);
     127             :     }
     128           0 :     if(have_14)
     129             :     {
     130           0 :         MATCH(GL_SGIS_generate_mipmap);
     131           0 :         MATCH(GL_NV_blend_square);
     132           0 :         MATCH(GL_ARB_depth_texture);
     133           0 :         MATCH(GL_ARB_shadow);
     134           0 :         MATCH(GL_EXT_fog_coord);
     135           0 :         MATCH(GL_EXT_multi_draw_arrays);
     136           0 :         MATCH(GL_ARB_point_parameters);
     137           0 :         MATCH(GL_EXT_secondary_color);
     138           0 :         MATCH(GL_EXT_blend_func_separate);
     139           0 :         MATCH(GL_EXT_stencil_wrap);
     140           0 :         MATCH(GL_ARB_texture_env_crossbar);
     141           0 :         MATCH(GL_EXT_texture_lod_bias);
     142           0 :         MATCH(GL_ARB_texture_mirrored_repeat);
     143           0 :         MATCH(GL_ARB_window_pos);
     144             : 
     145             :         // These extensions were added to GL 1.2, but as part of the optional
     146             :         // imaging subset; they're only guaranteed as of GL 1.4:
     147           0 :         MATCH(GL_EXT_blend_color);
     148           0 :         MATCH(GL_EXT_blend_minmax);
     149           0 :         MATCH(GL_EXT_blend_subtract);
     150             :     }
     151           0 :     if(have_13)
     152             :     {
     153           0 :         MATCH(GL_ARB_texture_compression);
     154           0 :         MATCH(GL_ARB_texture_cube_map);
     155           0 :         MATCH(GL_ARB_multisample);
     156           0 :         MATCH(GL_ARB_multitexture);
     157           0 :         MATCH(GL_ARB_transpose_matrix);
     158           0 :         MATCH(GL_ARB_texture_env_add);
     159           0 :         MATCH(GL_ARB_texture_env_combine);
     160           0 :         MATCH(GL_ARB_texture_env_dot3);
     161           0 :         MATCH(GL_ARB_texture_border_clamp);
     162             :     }
     163           0 :     if(have_12)
     164             :     {
     165           0 :         MATCH(GL_EXT_texture3D);
     166           0 :         MATCH(GL_EXT_bgra);
     167           0 :         MATCH(GL_EXT_packed_pixels);
     168           0 :         MATCH(GL_EXT_rescale_normal);
     169           0 :         MATCH(GL_EXT_separate_specular_color);
     170           0 :         MATCH(GL_SGIS_texture_edge_clamp);
     171           0 :         MATCH(GL_SGIS_texture_lod);
     172           0 :         MATCH(GL_EXT_draw_range_elements);
     173             :         // Skip the extensions that only affect the imaging subset
     174             :     }
     175             : 
     176             : #undef MATCH
     177             :     return false;
     178             : }
     179             : 
     180             : 
     181             : // check if the extension <ext> is supported by the OpenGL implementation.
     182             : // takes subsequently added core support for some extensions into account.
     183           0 : bool ogl_HaveExtension(const char* ext)
     184             : {
     185           0 :     ENSURE(exts && "call ogl_Init before using this function");
     186             : 
     187           0 :     if(isImplementedInCore(ext))
     188             :         return true;
     189             : 
     190           0 :     const char *p = exts, *end;
     191             : 
     192             :     // make sure ext is valid & doesn't contain spaces
     193           0 :     if(!ext || ext[0] == '\0' || strchr(ext, ' '))
     194             :         return false;
     195             : 
     196           0 :     for(;;)
     197             :     {
     198           0 :         p = strstr(p, ext);
     199           0 :         if(!p)
     200             :             return false; // <ext> string not found - extension not supported
     201           0 :         end = p + strlen(ext); // end of current substring
     202             : 
     203             :         // make sure the substring found is an entire extension string,
     204             :         // i.e. it starts and ends with ' '
     205           0 :         if((p == exts || p[-1] == ' ') &&   // valid start AND
     206           0 :            (*end == ' ' || *end == '\0'))   // valid end
     207             :             return true;
     208             :         p = end;
     209             :     }
     210             : }
     211             : 
     212             : static int GLVersion;
     213             : #if OS_WIN
     214             : static int WGLVersion;
     215             : #elif !CONFIG2_GLES && !OS_MACOSX && !OS_MAC
     216             : static int GLXVersion;
     217             : #endif
     218             : 
     219           0 : bool ogl_HaveVersion(int major, int minor)
     220             : {
     221           0 :     return GLAD_MAKE_VERSION(major, minor) <= GLVersion;
     222             : }
     223             : 
     224             : 
     225             : // check if all given extension strings (passed as const char* parameters,
     226             : // terminated by a 0 pointer) are supported by the OpenGL implementation,
     227             : // as determined by ogl_HaveExtension.
     228             : // returns 0 if all are present; otherwise, the first extension in the
     229             : // list that's not supported (useful for reporting errors).
     230             : //
     231             : // note: dummy parameter is necessary to access parameter va_list.
     232             : //
     233             : //
     234             : // rationale: this interface is more convenient than individual
     235             : // ogl_HaveExtension calls and allows reporting which extension is missing.
     236             : //
     237             : // one disadvantage is that there is no way to indicate that either one
     238             : // of 2 extensions would be acceptable, e.g. (ARB|EXT)_texture_env_dot3.
     239             : // this is isn't so bad, since they wouldn't be named differently
     240             : // if there weren't non-trivial changes between them. for that reason,
     241             : // we refrain from equivalence checks (which would boil down to
     242             : // string-matching known extensions to their equivalents).
     243           0 : const char* ogl_HaveExtensions(int dummy, ...)
     244             : {
     245           0 :     const char* ext;
     246             : 
     247           0 :     va_list args;
     248           0 :     va_start(args, dummy);
     249           0 :     for(;;)
     250             :     {
     251           0 :         ext = va_arg(args, const char*);
     252             :         // end of list reached; all were present => return 0.
     253           0 :         if(!ext)
     254             :             break;
     255             : 
     256             :         // not found => return name of missing extension.
     257           0 :         if(!ogl_HaveExtension(ext))
     258             :             break;
     259             :     }
     260           0 :     va_end(args);
     261             : 
     262           0 :     return ext;
     263             : }
     264             : 
     265             : 
     266             : // to help when running with no hardware acceleration and only OpenGL 1.1
     267             : // (e.g. testing the game in virtual machines), we define dummy versions of
     268             : // some extension functions which our graphics code assumes exist.
     269             : // it will render incorrectly but at least it shouldn't crash.
     270             : 
     271             : #if CONFIG2_GLES
     272             : 
     273             : static void enableDummyFunctions()
     274             : {
     275             : }
     276             : 
     277             : #else
     278             : 
     279           0 : static void GLAD_API_PTR dummy_glDrawRangeElementsEXT(GLenum mode, GLuint, GLuint, GLsizei count, GLenum type, GLvoid* indices)
     280             : {
     281           0 :     glDrawElements(mode, count, type, indices);
     282           0 : }
     283             : 
     284           0 : static void GLAD_API_PTR dummy_glActiveTextureARB(GLenum UNUSED(texture))
     285             : {
     286           0 : }
     287             : 
     288           0 : static void GLAD_API_PTR dummy_glClientActiveTextureARB(GLenum UNUSED(texture))
     289             : {
     290           0 : }
     291             : 
     292           0 : static void GLAD_API_PTR dummy_glMultiTexCoord2fARB(GLenum UNUSED(target), GLfloat s, GLfloat t)
     293             : {
     294           0 :     glTexCoord2f(s, t);
     295           0 : }
     296             : 
     297           0 : static void GLAD_API_PTR dummy_glMultiTexCoord3fARB(GLenum UNUSED(target), GLfloat s, GLfloat t, GLfloat r)
     298             : {
     299           0 :     glTexCoord3f(s, t, r);
     300           0 : }
     301             : 
     302           0 : static void enableDummyFunctions()
     303             : {
     304             :     // fall back to the dummy functions when extensions (or equivalent core support) are missing
     305             : 
     306           0 :     if(!ogl_HaveExtension("GL_EXT_draw_range_elements"))
     307             :     {
     308           0 :         glDrawRangeElementsEXT = reinterpret_cast<PFNGLDRAWRANGEELEMENTSEXTPROC>(&dummy_glDrawRangeElementsEXT);
     309             :     }
     310             : 
     311           0 :     if(!ogl_HaveExtension("GL_ARB_multitexture"))
     312             :     {
     313           0 :         glActiveTextureARB = reinterpret_cast<PFNGLACTIVETEXTUREARBPROC>(&dummy_glActiveTextureARB);
     314           0 :         glClientActiveTextureARB = reinterpret_cast<PFNGLACTIVETEXTUREARBPROC>(&dummy_glClientActiveTextureARB);
     315           0 :         glMultiTexCoord2fARB = reinterpret_cast<PFNGLMULTITEXCOORD2FARBPROC>(&dummy_glMultiTexCoord2fARB);
     316           0 :         glMultiTexCoord3fARB = reinterpret_cast<PFNGLMULTITEXCOORD3FARBPROC>(&dummy_glMultiTexCoord3fARB);
     317             :     }
     318           0 : }
     319             : 
     320             : #endif  // #if CONFIG2_GLES
     321             : 
     322             : //----------------------------------------------------------------------------
     323             : 
     324           0 : const char* ogl_GetErrorName(GLenum err)
     325             : {
     326             : #define E(e) case e: return #e;
     327           0 :     switch (err)
     328             :     {
     329             :     E(GL_INVALID_ENUM)
     330           0 :     E(GL_INVALID_VALUE)
     331           0 :     E(GL_INVALID_OPERATION)
     332             : #if !CONFIG2_GLES
     333           0 :     E(GL_STACK_OVERFLOW)
     334           0 :     E(GL_STACK_UNDERFLOW)
     335             : #endif
     336           0 :     E(GL_OUT_OF_MEMORY)
     337           0 :     E(GL_INVALID_FRAMEBUFFER_OPERATION)
     338           0 :     default: return "Unknown GL error";
     339             :     }
     340             : #undef E
     341             : }
     342             : 
     343           0 : static void dump_gl_error(GLenum err)
     344             : {
     345           0 :     debug_printf("OGL| %s (%04x)\n", ogl_GetErrorName(err), err);
     346           0 : }
     347             : 
     348           0 : void ogl_WarnIfErrorLoc(const char *file, int line)
     349             : {
     350             :     // glGetError may return multiple errors, so we poll it in a loop.
     351             :     // the debug_printf should only happen once (if this is set), though.
     352           0 :     bool error_enountered = false;
     353           0 :     GLenum first_error = 0;
     354             : 
     355           0 :     for(;;)
     356             :     {
     357           0 :         GLenum err = glGetError();
     358           0 :         if(err == GL_NO_ERROR)
     359             :             break;
     360             : 
     361           0 :         if(!error_enountered)
     362           0 :             first_error = err;
     363             : 
     364           0 :         error_enountered = true;
     365           0 :         dump_gl_error(err);
     366           0 :     }
     367             : 
     368           0 :     if(error_enountered)
     369           0 :         debug_printf("%s:%d: OpenGL error(s) occurred: %s (%04x)\n", file, line, ogl_GetErrorName(first_error), (unsigned int)first_error);
     370           0 : }
     371             : 
     372             : // ignore and reset the specified error (as returned by glGetError).
     373             : // any other errors that have occurred are reported as ogl_WarnIfError would.
     374             : //
     375             : // this is useful for suppressing annoying error messages, e.g.
     376             : // "invalid enum" for GL_CLAMP_TO_EDGE even though we've already
     377             : // warned the user that their OpenGL implementation is too old.
     378           0 : bool ogl_SquelchError(GLenum err_to_ignore)
     379             : {
     380             :     // glGetError may return multiple errors, so we poll it in a loop.
     381             :     // the debug_printf should only happen once (if this is set), though.
     382           0 :     bool error_enountered = false;
     383           0 :     bool error_ignored = false;
     384           0 :     GLenum first_error = 0;
     385             : 
     386           0 :     for(;;)
     387             :     {
     388           0 :         GLenum err = glGetError();
     389           0 :         if(err == GL_NO_ERROR)
     390             :             break;
     391             : 
     392           0 :         if(err == err_to_ignore)
     393             :         {
     394             :             error_ignored = true;
     395             :             continue;
     396             :         }
     397             : 
     398           0 :         if(!error_enountered)
     399           0 :             first_error = err;
     400             : 
     401           0 :         error_enountered = true;
     402           0 :         dump_gl_error(err);
     403             :     }
     404             : 
     405           0 :     if(error_enountered)
     406           0 :         debug_printf("OpenGL error(s) occurred: %04x\n", (unsigned int)first_error);
     407             : 
     408           0 :     return error_ignored;
     409             : }
     410             : 
     411             : 
     412             : //----------------------------------------------------------------------------
     413             : // feature and limit detect
     414             : //----------------------------------------------------------------------------
     415             : 
     416             : #if OS_WIN
     417             : bool ogl_Init(void* (load)(const char*), void* hdc)
     418             : #elif !CONFIG2_GLES && !OS_MACOSX && !OS_MAC
     419           0 : bool ogl_Init(void* (load)(const char*), void* display)
     420             : #else
     421             : bool ogl_Init(void* (load)(const char*))
     422             : #endif
     423             : {
     424           0 :     GLADloadfunc loadFunc = reinterpret_cast<GLADloadfunc>(load);
     425           0 :     if (!loadFunc)
     426             :         return false;
     427             : 
     428             : #define LOAD_ERROR(ERROR_STRING) \
     429             :     if (g_Logger) \
     430             :         LOGERROR(ERROR_STRING); \
     431             :     else \
     432             :         debug_printf(ERROR_STRING); \
     433             : 
     434             : #if !CONFIG2_GLES
     435           0 :     GLVersion = gladLoadGL(loadFunc);
     436           0 :     if (!GLVersion)
     437             :     {
     438           0 :         LOAD_ERROR("Failed to load OpenGL functions.");
     439           0 :         return false;
     440             :     }
     441             : # if OS_WIN
     442             :     WGLVersion = gladLoadWGL(reinterpret_cast<HDC>(hdc), loadFunc);
     443             :     if (!WGLVersion)
     444             :     {
     445             :         LOAD_ERROR("Failed to load WGL functions.");
     446             :         return false;
     447             :     }
     448             : # elif !OS_MACOSX && !OS_MAC
     449           0 :     GLXVersion = gladLoadGLX(reinterpret_cast<Display*>(display), DefaultScreen(display), loadFunc);
     450           0 :     if (!GLXVersion)
     451             :     {
     452           0 :         LOAD_ERROR("Failed to load GLX functions.");
     453           0 :         return false;
     454             :     }
     455             : # endif
     456             : #else
     457             :     GLVersion = gladLoadGLES2(loadFunc);
     458             :     if (!GLVersion)
     459             :     {
     460             :         LOAD_ERROR("Failed to load GLES2 functions.");
     461             :         return false;
     462             :     }
     463             : #endif
     464             : #undef LOAD_ERROR
     465             : 
     466             :     // cache extension list and versions for oglHave*.
     467             :     // note: this is less about performance (since the above are not
     468             :     // time-critical) than centralizing the 'OpenGL is ready' check.
     469           0 :     exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
     470           0 :     ENSURE(exts);   // else: called before OpenGL is ready for use
     471           0 :     have_12 = ogl_HaveVersion(1, 2);
     472           0 :     have_13 = ogl_HaveVersion(1, 3);
     473           0 :     have_14 = ogl_HaveVersion(1, 4);
     474           0 :     have_15 = ogl_HaveVersion(1, 5);
     475           0 :     have_20 = ogl_HaveVersion(2, 0);
     476           0 :     have_21 = ogl_HaveVersion(2, 1);
     477           0 :     have_30 = ogl_HaveVersion(3, 0);
     478             : 
     479           0 :     enableDummyFunctions();
     480             : 
     481           0 :     return true;
     482             : }
     483             : 
     484             : 
     485           0 : void ogl_SetVsyncEnabled(bool enabled)
     486             : {
     487             : #if !CONFIG2_GLES && OS_WIN
     488             :     int interval = enabled ? 1 : 0;
     489             :     if (ogl_HaveExtension("WGL_EXT_swap_control"))
     490             :         wglSwapIntervalEXT(interval);
     491             : #elif !CONFIG2_GLES && !OS_MACOSX && !OS_MAC
     492           0 :     int interval = enabled ? 1 : 0;
     493           0 :     if (ogl_HaveExtension("GLX_SGI_swap_control"))
     494           0 :         glXSwapIntervalSGI(interval);
     495             : #else
     496             :     UNUSED2(enabled);
     497             : #endif
     498           0 : }

Generated by: LCOV version 1.13