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