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 : }
|