1 /* Copyright (C) 2022 Wildfire Games.
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:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
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.
23 #include "precompiled.h"
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"
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>
42 # if defined(SDL_VIDEO_DRIVER_WAYLAND)
43 # include <glad/egl.h>
47 #endif // !CONFIG2_GLES
54 //----------------------------------------------------------------------------
56 //----------------------------------------------------------------------------
58 static const char* exts
= nullptr;
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;
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 const char* ogl_ExtensionString()
74 ENSURE(exts
&& "call ogl_Init before using this function");
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 static bool isImplementedInCore(const char* ext
)
87 #define MATCH(known_ext)\
88 if(!strcmp(ext, #known_ext))\
93 MATCH(GL_EXT_gpu_shader4
);
94 MATCH(GL_NV_conditional_render
);
95 MATCH(GL_ARB_color_buffer_float
);
96 MATCH(GL_ARB_depth_buffer_float
);
97 MATCH(GL_ARB_texture_float
);
98 MATCH(GL_EXT_packed_float
);
99 MATCH(GL_EXT_texture_shared_exponent
);
100 MATCH(GL_EXT_framebuffer_object
);
101 MATCH(GL_NV_half_float
);
102 MATCH(GL_ARB_half_float_pixel
);
103 MATCH(GL_EXT_framebuffer_multisample
);
104 MATCH(GL_EXT_framebuffer_blit
);
105 MATCH(GL_EXT_texture_integer
);
106 MATCH(GL_EXT_texture_array
);
107 MATCH(GL_EXT_packed_depth_stencil
);
108 MATCH(GL_EXT_draw_buffers2
);
109 MATCH(GL_EXT_texture_compression_rgtc
);
110 MATCH(GL_EXT_transform_feedback
);
111 MATCH(GL_APPLE_vertex_array_object
);
112 MATCH(GL_EXT_framebuffer_sRGB
);
116 MATCH(GL_ARB_pixel_buffer_object
);
117 MATCH(GL_EXT_texture_sRGB
);
121 MATCH(GL_ARB_shader_objects
);
122 MATCH(GL_ARB_vertex_shader
);
123 MATCH(GL_ARB_fragment_shader
);
124 MATCH(GL_ARB_shading_language_100
);
125 MATCH(GL_ARB_draw_buffers
);
126 MATCH(GL_ARB_texture_non_power_of_two
);
127 MATCH(GL_ARB_point_sprite
);
128 MATCH(GL_EXT_blend_equation_separate
);
132 MATCH(GL_ARB_vertex_buffer_object
);
133 MATCH(GL_ARB_occlusion_query
);
134 MATCH(GL_EXT_shadow_funcs
);
138 MATCH(GL_SGIS_generate_mipmap
);
139 MATCH(GL_NV_blend_square
);
140 MATCH(GL_ARB_depth_texture
);
141 MATCH(GL_ARB_shadow
);
142 MATCH(GL_EXT_fog_coord
);
143 MATCH(GL_EXT_multi_draw_arrays
);
144 MATCH(GL_ARB_point_parameters
);
145 MATCH(GL_EXT_secondary_color
);
146 MATCH(GL_EXT_blend_func_separate
);
147 MATCH(GL_EXT_stencil_wrap
);
148 MATCH(GL_ARB_texture_env_crossbar
);
149 MATCH(GL_EXT_texture_lod_bias
);
150 MATCH(GL_ARB_texture_mirrored_repeat
);
151 MATCH(GL_ARB_window_pos
);
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 MATCH(GL_EXT_blend_color
);
156 MATCH(GL_EXT_blend_minmax
);
157 MATCH(GL_EXT_blend_subtract
);
161 MATCH(GL_ARB_texture_compression
);
162 MATCH(GL_ARB_texture_cube_map
);
163 MATCH(GL_ARB_multisample
);
164 MATCH(GL_ARB_multitexture
);
165 MATCH(GL_ARB_transpose_matrix
);
166 MATCH(GL_ARB_texture_env_add
);
167 MATCH(GL_ARB_texture_env_combine
);
168 MATCH(GL_ARB_texture_env_dot3
);
169 MATCH(GL_ARB_texture_border_clamp
);
173 MATCH(GL_EXT_texture3D
);
175 MATCH(GL_EXT_packed_pixels
);
176 MATCH(GL_EXT_rescale_normal
);
177 MATCH(GL_EXT_separate_specular_color
);
178 MATCH(GL_SGIS_texture_edge_clamp
);
179 MATCH(GL_SGIS_texture_lod
);
180 MATCH(GL_EXT_draw_range_elements
);
181 // Skip the extensions that only affect the imaging subset
189 // check if the extension <ext> is supported by the OpenGL implementation.
190 // takes subsequently added core support for some extensions into account.
191 bool ogl_HaveExtension(const char* ext
)
193 ENSURE(exts
&& "call ogl_Init before using this function");
195 if(isImplementedInCore(ext
))
198 const char *p
= exts
, *end
;
200 // make sure ext is valid & doesn't contain spaces
201 if(!ext
|| ext
[0] == '\0' || strchr(ext
, ' '))
208 return false; // <ext> string not found - extension not supported
209 end
= p
+ strlen(ext
); // end of current substring
211 // make sure the substring found is an entire extension string,
212 // i.e. it starts and ends with ' '
213 if((p
== exts
|| p
[-1] == ' ') && // valid start AND
214 (*end
== ' ' || *end
== '\0')) // valid end
220 static int GLVersion
;
222 static int WGLVersion
;
223 #elif !CONFIG2_GLES && !OS_MACOSX && !OS_MAC
224 #if defined(SDL_VIDEO_DRIVER_X11)
225 static int GLXVersion
;
227 #if defined(SDL_VIDEO_DRIVER_WAYLAND)
228 static int EGLVersion
;
232 bool ogl_HaveVersion(int major
, int minor
)
234 return GLAD_MAKE_VERSION(major
, minor
) <= GLVersion
;
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).
244 // note: dummy parameter is necessary to access parameter va_list.
247 // rationale: this interface is more convenient than individual
248 // ogl_HaveExtension calls and allows reporting which extension is missing.
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 const char* ogl_HaveExtensions(int dummy
, ...)
261 va_start(args
, dummy
);
264 ext
= va_arg(args
, const char*);
265 // end of list reached; all were present => return 0.
269 // not found => return name of missing extension.
270 if(!ogl_HaveExtension(ext
))
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.
286 static void enableDummyFunctions()
292 static void GLAD_API_PTR
dummy_glDrawRangeElementsEXT(GLenum mode
, GLuint
, GLuint
, GLsizei count
, GLenum type
, GLvoid
* indices
)
294 glDrawElements(mode
, count
, type
, indices
);
297 static void GLAD_API_PTR
dummy_glActiveTextureARB(GLenum
UNUSED(texture
))
301 static void GLAD_API_PTR
dummy_glClientActiveTextureARB(GLenum
UNUSED(texture
))
305 static void GLAD_API_PTR
dummy_glMultiTexCoord2fARB(GLenum
UNUSED(target
), GLfloat s
, GLfloat t
)
310 static void GLAD_API_PTR
dummy_glMultiTexCoord3fARB(GLenum
UNUSED(target
), GLfloat s
, GLfloat t
, GLfloat r
)
312 glTexCoord3f(s
, t
, r
);
315 static void enableDummyFunctions()
317 // fall back to the dummy functions when extensions (or equivalent core support) are missing
319 if(!ogl_HaveExtension("GL_EXT_draw_range_elements"))
321 glDrawRangeElementsEXT
= reinterpret_cast<PFNGLDRAWRANGEELEMENTSEXTPROC
>(&dummy_glDrawRangeElementsEXT
);
324 if(!ogl_HaveExtension("GL_ARB_multitexture"))
326 glActiveTextureARB
= reinterpret_cast<PFNGLACTIVETEXTUREARBPROC
>(&dummy_glActiveTextureARB
);
327 glClientActiveTextureARB
= reinterpret_cast<PFNGLACTIVETEXTUREARBPROC
>(&dummy_glClientActiveTextureARB
);
328 glMultiTexCoord2fARB
= reinterpret_cast<PFNGLMULTITEXCOORD2FARBPROC
>(&dummy_glMultiTexCoord2fARB
);
329 glMultiTexCoord3fARB
= reinterpret_cast<PFNGLMULTITEXCOORD3FARBPROC
>(&dummy_glMultiTexCoord3fARB
);
333 #endif // #if CONFIG2_GLES
335 //----------------------------------------------------------------------------
337 const char* ogl_GetErrorName(GLenum err
)
339 #define E(e) case e: return #e;
344 E(GL_INVALID_OPERATION
)
347 E(GL_STACK_UNDERFLOW
)
350 E(GL_INVALID_FRAMEBUFFER_OPERATION
)
351 default: return "Unknown GL error";
356 static void dump_gl_error(GLenum err
)
358 debug_printf("OGL| %s (%04x)\n", ogl_GetErrorName(err
), err
);
361 void ogl_WarnIfErrorLoc(const char *file
, int line
)
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 bool error_enountered
= false;
366 GLenum first_error
= 0;
370 GLenum err
= glGetError();
371 if(err
== GL_NO_ERROR
)
374 if(!error_enountered
)
377 error_enountered
= true;
382 debug_printf("%s:%d: OpenGL error(s) occurred: %s (%04x)\n", file
, line
, ogl_GetErrorName(first_error
), (unsigned int)first_error
);
385 // ignore and reset the specified error (as returned by glGetError).
386 // any other errors that have occurred are reported as ogl_WarnIfError would.
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 bool ogl_SquelchError(GLenum err_to_ignore
)
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 bool error_enountered
= false;
396 bool error_ignored
= false;
397 GLenum first_error
= 0;
401 GLenum err
= glGetError();
402 if(err
== GL_NO_ERROR
)
405 if(err
== err_to_ignore
)
407 error_ignored
= true;
411 if(!error_enountered
)
414 error_enountered
= true;
419 debug_printf("OpenGL error(s) occurred: %04x\n", (unsigned int)first_error
);
421 return error_ignored
;
425 //----------------------------------------------------------------------------
426 // feature and limit detect
427 //----------------------------------------------------------------------------
430 bool ogl_Init(void* (load
)(const char*), void* hdc
)
431 #elif !CONFIG2_GLES && !OS_MACOSX && !OS_MAC
432 bool ogl_Init(void* (load
)(const char*), void* display
, int subsystem
)
434 bool ogl_Init(void* (load
)(const char*))
437 GLADloadfunc loadFunc
= reinterpret_cast<GLADloadfunc
>(load
);
441 #define LOAD_ERROR(ERROR_STRING) \
443 LOGERROR(ERROR_STRING); \
445 debug_printf(ERROR_STRING); \
448 GLVersion
= gladLoadGL(loadFunc
);
451 LOAD_ERROR("Failed to load OpenGL functions.");
455 WGLVersion
= gladLoadWGL(reinterpret_cast<HDC
>(hdc
), loadFunc
);
458 LOAD_ERROR("Failed to load WGL functions.");
461 # elif !OS_MACOSX && !OS_MAC
462 const SDL_SYSWM_TYPE sysWMType
= static_cast<SDL_SYSWM_TYPE
>(subsystem
);
463 # if defined(SDL_VIDEO_DRIVER_X11)
464 if (sysWMType
== SDL_SYSWM_X11
)
466 GLXVersion
= gladLoadGLX(reinterpret_cast<Display
*>(display
), DefaultScreen(display
), loadFunc
);
469 LOAD_ERROR("Failed to load GLX functions.");
474 # if defined(SDL_VIDEO_DRIVER_WAYLAND)
475 if (sysWMType
== SDL_SYSWM_WAYLAND
)
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
481 EGLVersion
= gladLoadEGL(nullptr, loadFunc
);
484 LOAD_ERROR("Failed to load EGL functions.");
491 GLVersion
= gladLoadGLES2(loadFunc
);
494 LOAD_ERROR("Failed to load GLES2 functions.");
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 exts
= reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS
));
504 ENSURE(exts
); // else: called before OpenGL is ready for use
505 have_12
= ogl_HaveVersion(1, 2);
506 have_13
= ogl_HaveVersion(1, 3);
507 have_14
= ogl_HaveVersion(1, 4);
508 have_15
= ogl_HaveVersion(1, 5);
509 have_20
= ogl_HaveVersion(2, 0);
510 have_21
= ogl_HaveVersion(2, 1);
511 have_30
= ogl_HaveVersion(3, 0);
513 enableDummyFunctions();
519 void ogl_SetVsyncEnabled(bool enabled
)
521 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 if (GLXVersion
&& ogl_HaveExtension("GLX_SGI_swap_control"))
528 glXSwapIntervalSGI(interval
);