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: 1 207 0.5 %
Date: 2023-01-19 00:18:29 Functions: 2 19 10.5 %

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

Generated by: LCOV version 1.13