1 /* Copyright (C) 2017 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.
24 * OpenGL helper functions.
27 #include "precompiled.h"
34 #include "lib/external_libraries/libsdl.h"
35 #include "lib/debug.h"
36 #include "lib/sysdep/gfx.h"
37 #include "lib/res/h_mgr.h"
40 #pragma comment(lib, "opengl32.lib")
44 //----------------------------------------------------------------------------
46 //----------------------------------------------------------------------------
48 // define extension function pointers
51 #define FUNC(ret, name, params) ret (GL_CALL_CONV *p##name) params;
52 #define FUNC2(ret, nameARB, nameCore, version, params) ret (GL_CALL_CONV *p##nameARB) params;
53 #define FUNC3(ret, nameARB, nameCore, version, params) ret (GL_CALL_CONV *p##nameCore) params;
54 #include "lib/external_libraries/glext_funcs.h"
61 static const char* exts
= NULL
;
62 static bool have_30
, have_21
, have_20
, have_15
, have_14
, have_13
, have_12
;
65 // return a C string of unspecified length containing a space-separated
66 // list of all extensions the OpenGL implementation advertises.
67 // (useful for crash logs).
68 const char* ogl_ExtensionString()
70 ENSURE(exts
&& "call ogl_Init before using this function");
75 // paranoia: newer drivers may forget to advertise an extension
76 // indicating support for something that has been folded into the core.
77 // we therefore check for all extensions known to be offered by the
78 // GL implementation present on the user's system; ogl_HaveExtension will
79 // take this into account.
80 // the app can therefore just ask for extensions and not worry about this.
81 static bool isImplementedInCore(const char* ext
)
83 #define MATCH(known_ext)\
84 if(!strcmp(ext, #known_ext))\
89 MATCH(GL_EXT_gpu_shader4
);
90 MATCH(GL_NV_conditional_render
);
91 MATCH(GL_ARB_color_buffer_float
);
92 MATCH(GL_ARB_depth_buffer_float
);
93 MATCH(GL_ARB_texture_float
);
94 MATCH(GL_EXT_packed_float
);
95 MATCH(GL_EXT_texture_shared_exponent
);
96 MATCH(GL_EXT_framebuffer_object
);
97 MATCH(GL_NV_half_float
);
98 MATCH(GL_ARB_half_float_pixel
);
99 MATCH(GL_EXT_framebuffer_multisample
);
100 MATCH(GL_EXT_framebuffer_blit
);
101 MATCH(GL_EXT_texture_integer
);
102 MATCH(GL_EXT_texture_array
);
103 MATCH(GL_EXT_packed_depth_stencil
);
104 MATCH(GL_EXT_draw_buffers2
);
105 MATCH(GL_EXT_texture_compression_rgtc
);
106 MATCH(GL_EXT_transform_feedback
);
107 MATCH(GL_APPLE_vertex_array_object
);
108 MATCH(GL_EXT_framebuffer_sRGB
);
112 MATCH(GL_ARB_pixel_buffer_object
);
113 MATCH(GL_EXT_texture_sRGB
);
117 MATCH(GL_ARB_shader_objects
);
118 MATCH(GL_ARB_vertex_shader
);
119 MATCH(GL_ARB_fragment_shader
);
120 MATCH(GL_ARB_shading_language_100
);
121 MATCH(GL_ARB_draw_buffers
);
122 MATCH(GL_ARB_texture_non_power_of_two
);
123 MATCH(GL_ARB_point_sprite
);
124 MATCH(GL_EXT_blend_equation_separate
);
128 MATCH(GL_ARB_vertex_buffer_object
);
129 MATCH(GL_ARB_occlusion_query
);
130 MATCH(GL_EXT_shadow_funcs
);
134 MATCH(GL_SGIS_generate_mipmap
);
135 MATCH(GL_NV_blend_square
);
136 MATCH(GL_ARB_depth_texture
);
137 MATCH(GL_ARB_shadow
);
138 MATCH(GL_EXT_fog_coord
);
139 MATCH(GL_EXT_multi_draw_arrays
);
140 MATCH(GL_ARB_point_parameters
);
141 MATCH(GL_EXT_secondary_color
);
142 MATCH(GL_EXT_blend_func_separate
);
143 MATCH(GL_EXT_stencil_wrap
);
144 MATCH(GL_ARB_texture_env_crossbar
);
145 MATCH(GL_EXT_texture_lod_bias
);
146 MATCH(GL_ARB_texture_mirrored_repeat
);
147 MATCH(GL_ARB_window_pos
);
149 // These extensions were added to GL 1.2, but as part of the optional
150 // imaging subset; they're only guaranteed as of GL 1.4:
151 MATCH(GL_EXT_blend_color
);
152 MATCH(GL_EXT_blend_minmax
);
153 MATCH(GL_EXT_blend_subtract
);
157 MATCH(GL_ARB_texture_compression
);
158 MATCH(GL_ARB_texture_cube_map
);
159 MATCH(GL_ARB_multisample
);
160 MATCH(GL_ARB_multitexture
);
161 MATCH(GL_ARB_transpose_matrix
);
162 MATCH(GL_ARB_texture_env_add
);
163 MATCH(GL_ARB_texture_env_combine
);
164 MATCH(GL_ARB_texture_env_dot3
);
165 MATCH(GL_ARB_texture_border_clamp
);
169 MATCH(GL_EXT_texture3D
);
171 MATCH(GL_EXT_packed_pixels
);
172 MATCH(GL_EXT_rescale_normal
);
173 MATCH(GL_EXT_separate_specular_color
);
174 MATCH(GL_SGIS_texture_edge_clamp
);
175 MATCH(GL_SGIS_texture_lod
);
176 MATCH(GL_EXT_draw_range_elements
);
177 // Skip the extensions that only affect the imaging subset
185 // check if the extension <ext> is supported by the OpenGL implementation.
186 // takes subsequently added core support for some extensions into account.
187 bool ogl_HaveExtension(const char* ext
)
189 ENSURE(exts
&& "call ogl_Init before using this function");
191 if(isImplementedInCore(ext
))
194 const char *p
= exts
, *end
;
196 // make sure ext is valid & doesn't contain spaces
197 if(!ext
|| ext
[0] == '\0' || strchr(ext
, ' '))
204 return false; // <ext> string not found - extension not supported
205 end
= p
+ strlen(ext
); // end of current substring
207 // make sure the substring found is an entire extension string,
208 // i.e. it starts and ends with ' '
209 if((p
== exts
|| p
[-1] == ' ') && // valid start AND
210 (*end
== ' ' || *end
== '\0')) // valid end
217 // check if the OpenGL implementation is at least at <version>.
218 // (format: "%d.%d" major minor)
219 bool ogl_HaveVersion(const char* desired_version
)
221 int desired_major
, desired_minor
;
222 if(sscanf_s(desired_version
, "%d.%d", &desired_major
, &desired_minor
) != 2)
224 DEBUG_WARN_ERR(ERR::LOGIC
); // invalid version string
228 // guaranteed to be of the form "major.minor[.release][ vendor-specific]"
229 // or "OpenGL ES major.minor[.release][ vendor-specific]".
230 // we won't distinguish GLES 2.0 from GL 2.0, but that's okay since
231 // they're close enough.
232 const char* version
= (const char*)glGetString(GL_VERSION
);
235 (sscanf_s(version
, "%d.%d", &major
, &minor
) != 2 &&
236 sscanf_s(version
, "OpenGL ES %d.%d", &major
, &minor
) != 2))
238 DEBUG_WARN_ERR(ERR::LOGIC
); // GL_VERSION invalid
242 // note: don't just compare characters - major and minor may be >= 10.
243 return (major
> desired_major
) ||
244 (major
== desired_major
&& minor
>= desired_minor
);
248 // check if all given extension strings (passed as const char* parameters,
249 // terminated by a 0 pointer) are supported by the OpenGL implementation,
250 // as determined by ogl_HaveExtension.
251 // returns 0 if all are present; otherwise, the first extension in the
252 // list that's not supported (useful for reporting errors).
254 // note: dummy parameter is necessary to access parameter va_list.
257 // rationale: this interface is more convenient than individual
258 // ogl_HaveExtension calls and allows reporting which extension is missing.
260 // one disadvantage is that there is no way to indicate that either one
261 // of 2 extensions would be acceptable, e.g. (ARB|EXT)_texture_env_dot3.
262 // this is isn't so bad, since they wouldn't be named differently
263 // if there weren't non-trivial changes between them. for that reason,
264 // we refrain from equivalence checks (which would boil down to
265 // string-matching known extensions to their equivalents).
266 const char* ogl_HaveExtensions(int dummy
, ...)
271 va_start(args
, dummy
);
274 ext
= va_arg(args
, const char*);
275 // end of list reached; all were present => return 0.
279 // not found => return name of missing extension.
280 if(!ogl_HaveExtension(ext
))
289 // to help when running with no hardware acceleration and only OpenGL 1.1
290 // (e.g. testing the game in virtual machines), we define dummy versions of
291 // some extension functions which our graphics code assumes exist.
292 // it will render incorrectly but at least it shouldn't crash.
296 static void enableDummyFunctions()
302 static void GL_CALL_CONV
dummy_glDrawRangeElementsEXT(GLenum mode
, GLuint
, GLuint
, GLsizei count
, GLenum type
, GLvoid
* indices
)
304 glDrawElements(mode
, count
, type
, indices
);
307 static void GL_CALL_CONV
dummy_glActiveTextureARB(int)
311 static void GL_CALL_CONV
dummy_glClientActiveTextureARB(int)
315 static void GL_CALL_CONV
dummy_glMultiTexCoord2fARB(int, float s
, float t
)
320 static void GL_CALL_CONV
dummy_glMultiTexCoord3fARB(int, float s
, float t
, float r
)
322 glTexCoord3f(s
, t
, r
);
325 static void enableDummyFunctions()
327 // fall back to the dummy functions when extensions (or equivalent core support) are missing
329 if(!ogl_HaveExtension("GL_EXT_draw_range_elements"))
331 pglDrawRangeElementsEXT
= &dummy_glDrawRangeElementsEXT
;
334 if(!ogl_HaveExtension("GL_ARB_multitexture"))
336 pglActiveTextureARB
= &dummy_glActiveTextureARB
;
337 pglClientActiveTextureARB
= &dummy_glClientActiveTextureARB
;
338 pglMultiTexCoord2fARB
= &dummy_glMultiTexCoord2fARB
;
339 pglMultiTexCoord3fARB
= &dummy_glMultiTexCoord3fARB
;
343 #endif // #if CONFIG2_GLES
345 static void importExtensionFunctions()
347 // It should be safe to load the ARB function pointers even if the
348 // extension isn't advertised, since we won't actually use them without
349 // checking for the extension.
350 // (TODO: this calls ogl_HaveVersion far more times than is necessary -
351 // we should probably use the have_* variables instead)
352 // Note: the xorg-x11 implementation of glXGetProcAddress doesn't return NULL
353 // if the function is unsupported (i.e. the rare case of a driver not reporting
354 // its supported version correctly, see http://trac.wildfiregames.com/ticket/171)
355 #define FUNC(ret, name, params) p##name = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#name);
356 #define FUNC23(pname, ret, nameARB, nameCore, version, params) \
358 if(ogl_HaveVersion(version)) \
359 pname = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#nameCore); \
360 if(!pname) /* use the ARB name if the driver lied about what version it supports */ \
361 pname = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#nameARB);
362 #define FUNC2(ret, nameARB, nameCore, version, params) FUNC23(p##nameARB, ret, nameARB, nameCore, version, params)
363 #define FUNC3(ret, nameARB, nameCore, version, params) FUNC23(p##nameCore, ret, nameARB, nameCore, version, params)
364 #include "lib/external_libraries/glext_funcs.h"
370 enableDummyFunctions();
374 //----------------------------------------------------------------------------
376 const char* ogl_GetErrorName(GLenum err
)
378 #define E(e) case e: return #e;
383 E(GL_INVALID_OPERATION
)
386 E(GL_STACK_UNDERFLOW
)
389 E(GL_INVALID_FRAMEBUFFER_OPERATION
)
390 default: return "Unknown GL error";
395 static void dump_gl_error(GLenum err
)
397 debug_printf("OGL| %s (%04x)\n", ogl_GetErrorName(err
), err
);
400 void ogl_WarnIfErrorLoc(const char *file
, int line
)
402 // glGetError may return multiple errors, so we poll it in a loop.
403 // the debug_printf should only happen once (if this is set), though.
404 bool error_enountered
= false;
405 GLenum first_error
= 0;
409 GLenum err
= glGetError();
410 if(err
== GL_NO_ERROR
)
413 if(!error_enountered
)
416 error_enountered
= true;
421 debug_printf("%s:%d: OpenGL error(s) occurred: %s (%04x)\n", file
, line
, ogl_GetErrorName(first_error
), (unsigned int)first_error
);
425 // ignore and reset the specified error (as returned by glGetError).
426 // any other errors that have occurred are reported as ogl_WarnIfError would.
428 // this is useful for suppressing annoying error messages, e.g.
429 // "invalid enum" for GL_CLAMP_TO_EDGE even though we've already
430 // warned the user that their OpenGL implementation is too old.
431 bool ogl_SquelchError(GLenum err_to_ignore
)
433 // glGetError may return multiple errors, so we poll it in a loop.
434 // the debug_printf should only happen once (if this is set), though.
435 bool error_enountered
= false;
436 bool error_ignored
= false;
437 GLenum first_error
= 0;
441 GLenum err
= glGetError();
442 if(err
== GL_NO_ERROR
)
445 if(err
== err_to_ignore
)
447 error_ignored
= true;
451 if(!error_enountered
)
454 error_enountered
= true;
459 debug_printf("OpenGL error(s) occurred: %04x\n", (unsigned int)first_error
);
461 return error_ignored
;
465 //----------------------------------------------------------------------------
466 // feature and limit detect
467 //----------------------------------------------------------------------------
469 GLint ogl_max_tex_size
= -1; // [pixels]
470 GLint ogl_max_tex_units
= -1; // limit on GL_TEXTUREn
472 // call after each video mode change, since thereafter extension functions
473 // may have changed [address].
476 // cache extension list and versions for oglHave*.
477 // note: this is less about performance (since the above are not
478 // time-critical) than centralizing the 'OpenGL is ready' check.
479 exts
= (const char*)glGetString(GL_EXTENSIONS
);
480 ENSURE(exts
); // else: called before OpenGL is ready for use
481 have_12
= ogl_HaveVersion("1.2");
482 have_13
= ogl_HaveVersion("1.3");
483 have_14
= ogl_HaveVersion("1.4");
484 have_15
= ogl_HaveVersion("1.5");
485 have_20
= ogl_HaveVersion("2.0");
486 have_21
= ogl_HaveVersion("2.1");
487 have_30
= ogl_HaveVersion("3.0");
489 importExtensionFunctions();
491 glGetIntegerv(GL_MAX_TEXTURE_SIZE
, &ogl_max_tex_size
);
493 glGetIntegerv(GL_MAX_TEXTURE_UNITS
, &ogl_max_tex_units
);