Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ogl.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2013 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 /*
24  * OpenGL helper functions.
25  */
26 
27 #include "precompiled.h"
28 #include "lib/ogl.h"
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdarg.h>
33 
35 #include "lib/debug.h"
36 #include "lib/sysdep/gfx.h"
37 #include "lib/res/h_mgr.h"
38 
39 #if MSC_VERSION
40 #pragma comment(lib, "opengl32.lib")
41 #endif
42 
43 
44 //----------------------------------------------------------------------------
45 // extensions
46 //----------------------------------------------------------------------------
47 
48 // define extension function pointers
49 extern "C"
50 {
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;
55 #undef FUNC3
56 #undef FUNC2
57 #undef FUNC
58 }
59 
60 
61 static const char* exts = NULL;
63 
64 
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()
69 {
70  ENSURE(exts && "call ogl_Init before using this function");
71  return exts;
72 }
73 
74 
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)
82 {
83 #define MATCH(known_ext)\
84  if(!strcmp(ext, #known_ext))\
85  return true;
86 
87  if(have_30)
88  {
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);
109  }
110  if(have_21)
111  {
112  MATCH(GL_ARB_pixel_buffer_object);
113  MATCH(GL_EXT_texture_sRGB);
114  }
115  if(have_20)
116  {
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);
125  }
126  if(have_15)
127  {
128  MATCH(GL_ARB_vertex_buffer_object);
129  MATCH(GL_ARB_occlusion_query);
130  MATCH(GL_EXT_shadow_funcs);
131  }
132  if(have_14)
133  {
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);
148 
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);
154  }
155  if(have_13)
156  {
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);
166  }
167  if(have_12)
168  {
169  MATCH(GL_EXT_texture3D);
170  MATCH(GL_EXT_bgra);
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
178  }
179 
180 #undef MATCH
181  return false;
182 }
183 
184 
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)
188 {
189  ENSURE(exts && "call ogl_Init before using this function");
190 
191  if(isImplementedInCore(ext))
192  return true;
193 
194  const char *p = exts, *end;
195 
196  // make sure ext is valid & doesn't contain spaces
197  if(!ext || ext[0] == '\0' || strchr(ext, ' '))
198  return false;
199 
200  for(;;)
201  {
202  p = strstr(p, ext);
203  if(!p)
204  return false; // <ext> string not found - extension not supported
205  end = p + strlen(ext); // end of current substring
206 
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
211  return true;
212  p = end;
213  }
214 }
215 
216 
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)
220 {
221  int desired_major, desired_minor;
222  if(sscanf_s(desired_version, "%d.%d", &desired_major, &desired_minor) != 2)
223  {
224  DEBUG_WARN_ERR(ERR::LOGIC); // invalid version string
225  return false;
226  }
227 
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);
233  int major, minor;
234  if(!version ||
235  (sscanf_s(version, "%d.%d", &major, &minor) != 2 &&
236  sscanf_s(version, "OpenGL ES %d.%d", &major, &minor) != 2))
237  {
238  DEBUG_WARN_ERR(ERR::LOGIC); // GL_VERSION invalid
239  return false;
240  }
241 
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);
245 }
246 
247 
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).
253 //
254 // note: dummy parameter is necessary to access parameter va_list.
255 //
256 //
257 // rationale: this interface is more convenient than individual
258 // ogl_HaveExtension calls and allows reporting which extension is missing.
259 //
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, ...)
267 {
268  const char* ext;
269 
270  va_list args;
271  va_start(args, dummy);
272  for(;;)
273  {
274  ext = va_arg(args, const char*);
275  // end of list reached; all were present => return 0.
276  if(!ext)
277  break;
278 
279  // not found => return name of missing extension.
280  if(!ogl_HaveExtension(ext))
281  break;
282  }
283  va_end(args);
284 
285  return ext;
286 }
287 
288 
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.
293 
294 #if CONFIG2_GLES
295 
296 static void enableDummyFunctions()
297 {
298 }
299 
300 #else
301 
302 static void GL_CALL_CONV dummy_glDrawRangeElementsEXT(GLenum mode, GLuint, GLuint, GLsizei count, GLenum type, GLvoid* indices)
303 {
304  glDrawElements(mode, count, type, indices);
305 }
306 
308 {
309 }
310 
312 {
313 }
314 
315 static void GL_CALL_CONV dummy_glMultiTexCoord2fARB(int, float s, float t)
316 {
317  glTexCoord2f(s, t);
318 }
319 
320 static void GL_CALL_CONV dummy_glMultiTexCoord3fARB(int, float s, float t, float r)
321 {
322  glTexCoord3f(s, t, r);
323 }
324 
325 static void enableDummyFunctions()
326 {
327  // fall back to the dummy functions when extensions (or equivalent core support) are missing
328 
329  if(!ogl_HaveExtension("GL_EXT_draw_range_elements"))
330  {
331  pglDrawRangeElementsEXT = &dummy_glDrawRangeElementsEXT;
332  }
333 
334  if(!ogl_HaveExtension("GL_ARB_multitexture"))
335  {
336  pglActiveTextureARB = &dummy_glActiveTextureARB;
337  pglClientActiveTextureARB = &dummy_glClientActiveTextureARB;
338  pglMultiTexCoord2fARB = &dummy_glMultiTexCoord2fARB;
339  pglMultiTexCoord3fARB = &dummy_glMultiTexCoord3fARB;
340  }
341 }
342 
343 #endif // #if CONFIG2_GLES
344 
346 {
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) \
357  pname = NULL; \
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)
365 #undef FUNC3
366 #undef FUNC2
367 #undef FUNC23
368 #undef FUNC
369 
371 }
372 
373 
374 //----------------------------------------------------------------------------
375 
376 static void dump_gl_error(GLenum err)
377 {
378  debug_printf(L"OGL| ");
379 #define E(e) case e: debug_printf(L"%ls\n", WIDEN(#e)); break;
380  switch (err)
381  {
382  E(GL_INVALID_ENUM)
383  E(GL_INVALID_VALUE)
384  E(GL_INVALID_OPERATION)
385 #if !CONFIG2_GLES
386  E(GL_STACK_OVERFLOW)
387  E(GL_STACK_UNDERFLOW)
388 #endif
389  E(GL_OUT_OF_MEMORY)
391  default: debug_printf(L"Unknown GL error: %04x\n", err); break;
392  }
393 #undef E
394 }
395 
396 #ifndef ogl_WarnIfError
397  // don't include this function if it has been defined (in ogl.h) as a no-op
399 {
400  // glGetError may return multiple errors, so we poll it in a loop.
401  // the debug_printf should only happen once (if this is set), though.
402  bool error_enountered = false;
403  GLenum first_error = 0;
404 
405  for(;;)
406  {
407  GLenum err = glGetError();
408  if(err == GL_NO_ERROR)
409  break;
410 
411  if(!error_enountered)
412  first_error = err;
413 
414  error_enountered = true;
415  dump_gl_error(err);
416  }
417 
418  if(error_enountered)
419  debug_printf(L"OpenGL error(s) occurred: %04x\n", (unsigned int)first_error);
420 }
421 #endif
422 
423 
424 // ignore and reset the specified error (as returned by glGetError).
425 // any other errors that have occurred are reported as ogl_WarnIfError would.
426 //
427 // this is useful for suppressing annoying error messages, e.g.
428 // "invalid enum" for GL_CLAMP_TO_EDGE even though we've already
429 // warned the user that their OpenGL implementation is too old.
430 bool ogl_SquelchError(GLenum err_to_ignore)
431 {
432  // glGetError may return multiple errors, so we poll it in a loop.
433  // the debug_printf should only happen once (if this is set), though.
434  bool error_enountered = false;
435  bool error_ignored = false;
436  GLenum first_error = 0;
437 
438  for(;;)
439  {
440  GLenum err = glGetError();
441  if(err == GL_NO_ERROR)
442  break;
443 
444  if(err == err_to_ignore)
445  {
446  error_ignored = true;
447  continue;
448  }
449 
450  if(!error_enountered)
451  first_error = err;
452 
453  error_enountered = true;
454  dump_gl_error(err);
455  }
456 
457  if(error_enountered)
458  debug_printf(L"OpenGL error(s) occurred: %04x\n", (unsigned int)first_error);
459 
460  return error_ignored;
461 }
462 
463 
464 //----------------------------------------------------------------------------
465 // feature and limit detect
466 //----------------------------------------------------------------------------
467 
468 GLint ogl_max_tex_size = -1; // [pixels]
469 GLint ogl_max_tex_units = -1; // limit on GL_TEXTUREn
470 
471 // call after each video mode change, since thereafter extension functions
472 // may have changed [address].
473 void ogl_Init()
474 {
475  // cache extension list and versions for oglHave*.
476  // note: this is less about performance (since the above are not
477  // time-critical) than centralizing the 'OpenGL is ready' check.
478  exts = (const char*)glGetString(GL_EXTENSIONS);
479  ENSURE(exts); // else: called before OpenGL is ready for use
480  have_12 = ogl_HaveVersion("1.2");
481  have_13 = ogl_HaveVersion("1.3");
482  have_14 = ogl_HaveVersion("1.4");
483  have_15 = ogl_HaveVersion("1.5");
484  have_20 = ogl_HaveVersion("2.0");
485  have_21 = ogl_HaveVersion("2.1");
486  have_30 = ogl_HaveVersion("3.0");
487 
489 
490  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ogl_max_tex_size);
491 #if !CONFIG2_GLES
492  glGetIntegerv(GL_MAX_TEXTURE_UNITS, &ogl_max_tex_units);
493 #endif
494 }
GLint ogl_max_tex_size
Definition: ogl.cpp:468
static void GL_CALL_CONV dummy_glMultiTexCoord3fARB(int, float s, float t, float r)
Definition: ogl.cpp:320
const Status LOGIC
Definition: status.h:409
static bool isImplementedInCore(const char *ext)
Definition: ogl.cpp:81
static const char * exts
Definition: ogl.cpp:61
static void GL_CALL_CONV dummy_glClientActiveTextureARB(int)
Definition: ogl.cpp:311
static void importExtensionFunctions()
Definition: ogl.cpp:345
#define E(e)
#define GL_CALL_CONV
Definition: ogl.h:128
bool ogl_SquelchError(GLenum err_to_ignore)
ignore and reset the specified OpenGL error.
Definition: ogl.cpp:430
bool ogl_HaveVersion(const char *desired_version)
make sure the OpenGL implementation version matches or is newer than the given version.
Definition: ogl.cpp:219
static void enableDummyFunctions()
Definition: ogl.cpp:325
static bool have_15
Definition: ogl.cpp:62
static void GL_CALL_CONV dummy_glActiveTextureARB(int)
Definition: ogl.cpp:307
static bool have_21
Definition: ogl.cpp:62
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
GLint ogl_max_tex_units
[pixels]
Definition: ogl.cpp:469
const char * ogl_ExtensionString()
get a list of all supported extensions.
Definition: ogl.cpp:68
static void dump_gl_error(GLenum err)
Definition: ogl.cpp:376
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
bool ogl_HaveExtension(const char *ext)
check if an extension is supported by the OpenGL implementation.
Definition: ogl.cpp:187
void ogl_Init()
initialization: import extension function pointers and do feature detect.
Definition: ogl.cpp:473
static bool have_14
Definition: ogl.cpp:62
#define sscanf_s
Definition: secure_crt.h:118
#define GL_INVALID_FRAMEBUFFER_OPERATION
Definition: ogl.h:112
void ogl_WarnIfError()
raise a warning (break into the debugger) if an OpenGL error is pending.
Definition: ogl.cpp:398
static bool have_20
Definition: ogl.cpp:62
static bool have_13
Definition: ogl.cpp:62
static bool have_30
Definition: ogl.cpp:62
static bool have_12
Definition: ogl.cpp:62
static void GL_CALL_CONV dummy_glMultiTexCoord2fARB(int, float s, float t)
Definition: ogl.cpp:315
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
#define MATCH(known_ext)
static void GL_CALL_CONV dummy_glDrawRangeElementsEXT(GLenum mode, GLuint, GLuint, GLsizei count, GLenum type, GLvoid *indices)
Definition: ogl.cpp:302
const char * ogl_HaveExtensions(int dummy,...)
check if a list of extensions are all supported (as determined by ogl_HaveExtension).
Definition: ogl.cpp:266