Fixes calls to SetVertexAttributeFormat with zero stride.
[0ad.git] / source / lib / ogl.cpp
bloba6e9e74a6241938d06f3251d2b4045f16ac81ced
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"
25 #include "lib/ogl.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"
33 #if !CONFIG2_GLES
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
47 #endif // !CONFIG2_GLES
49 #include <stdio.h>
50 #include <string.h>
51 #include <stdarg.h>
54 //----------------------------------------------------------------------------
55 // extensions
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");
75 return exts;
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))\
89 return true;
91 if(have_30)
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);
114 if(have_21)
116 MATCH(GL_ARB_pixel_buffer_object);
117 MATCH(GL_EXT_texture_sRGB);
119 if(have_20)
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);
130 if(have_15)
132 MATCH(GL_ARB_vertex_buffer_object);
133 MATCH(GL_ARB_occlusion_query);
134 MATCH(GL_EXT_shadow_funcs);
136 if(have_14)
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);
159 if(have_13)
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);
171 if(have_12)
173 MATCH(GL_EXT_texture3D);
174 MATCH(GL_EXT_bgra);
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
184 #undef MATCH
185 return false;
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))
196 return true;
198 const char *p = exts, *end;
200 // make sure ext is valid & doesn't contain spaces
201 if(!ext || ext[0] == '\0' || strchr(ext, ' '))
202 return false;
204 for(;;)
206 p = strstr(p, ext);
207 if(!p)
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
215 return true;
216 p = end;
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
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, ...)
258 const char* ext;
260 va_list args;
261 va_start(args, dummy);
262 for(;;)
264 ext = va_arg(args, const char*);
265 // end of list reached; all were present => return 0.
266 if(!ext)
267 break;
269 // not found => return name of missing extension.
270 if(!ogl_HaveExtension(ext))
271 break;
273 va_end(args);
275 return 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.
284 #if CONFIG2_GLES
286 static void enableDummyFunctions()
290 #else
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)
307 glTexCoord2f(s, 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;
340 switch (err)
342 E(GL_INVALID_ENUM)
343 E(GL_INVALID_VALUE)
344 E(GL_INVALID_OPERATION)
345 #if !CONFIG2_GLES
346 E(GL_STACK_OVERFLOW)
347 E(GL_STACK_UNDERFLOW)
348 #endif
349 E(GL_OUT_OF_MEMORY)
350 E(GL_INVALID_FRAMEBUFFER_OPERATION)
351 default: return "Unknown GL error";
353 #undef E
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;
368 for(;;)
370 GLenum err = glGetError();
371 if(err == GL_NO_ERROR)
372 break;
374 if(!error_enountered)
375 first_error = err;
377 error_enountered = true;
378 dump_gl_error(err);
381 if(error_enountered)
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;
399 for(;;)
401 GLenum err = glGetError();
402 if(err == GL_NO_ERROR)
403 break;
405 if(err == err_to_ignore)
407 error_ignored = true;
408 continue;
411 if(!error_enountered)
412 first_error = err;
414 error_enountered = true;
415 dump_gl_error(err);
418 if(error_enountered)
419 debug_printf("OpenGL error(s) occurred: %04x\n", (unsigned int)first_error);
421 return error_ignored;
425 //----------------------------------------------------------------------------
426 // feature and limit detect
427 //----------------------------------------------------------------------------
429 #if OS_WIN
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)
433 #else
434 bool ogl_Init(void* (load)(const char*))
435 #endif
437 GLADloadfunc loadFunc = reinterpret_cast<GLADloadfunc>(load);
438 if (!loadFunc)
439 return false;
441 #define LOAD_ERROR(ERROR_STRING) \
442 if (g_Logger) \
443 LOGERROR(ERROR_STRING); \
444 else \
445 debug_printf(ERROR_STRING); \
447 #if !CONFIG2_GLES
448 GLVersion = gladLoadGL(loadFunc);
449 if (!GLVersion)
451 LOAD_ERROR("Failed to load OpenGL functions.");
452 return false;
454 # if OS_WIN
455 WGLVersion = gladLoadWGL(reinterpret_cast<HDC>(hdc), loadFunc);
456 if (!WGLVersion)
458 LOAD_ERROR("Failed to load WGL functions.");
459 return false;
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);
467 if (!GLXVersion)
469 LOAD_ERROR("Failed to load GLX functions.");
470 return false;
473 # endif
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
480 // call it twice.
481 EGLVersion = gladLoadEGL(nullptr, loadFunc);
482 if (!EGLVersion)
484 LOAD_ERROR("Failed to load EGL functions.");
485 return false;
488 # endif
489 # endif
490 #else
491 GLVersion = gladLoadGLES2(loadFunc);
492 if (!GLVersion)
494 LOAD_ERROR("Failed to load GLES2 functions.");
495 return false;
497 #endif
498 #undef LOAD_ERROR
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();
515 return true;
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);
529 #else
530 UNUSED2(interval);
531 #endif
532 #else
533 UNUSED2(interval);
534 #endif