I’ve tried relying on the opengl version but (1) you can’t retrieve the version prior to creating a context and
You can’t retrieve function pointers or check the extension string without a context either. Not on Win32.
(2) I’ve had more than one GPU driver telling to support a core profile but retrieving certain functions from it returned NULL.
This sometimes happens with extensions too.
So if I want glCompileShader I do something like this:
You realize that those are two different functions with two different behaviors, yes?
For example, you cannot assume that glCompileShaderARB can be fed shaders from GLSL versions other than 1.10. Oh sure, it might work. And it might not. It might work today and break tomorrow. It might work for most of your post-1.10 shaders, then break for something new you pass. Or whatever. The spec says that it only handles GLSL version 1.10, period.
More important is the fact that the two functions do not have the same interface. glCompileShader takes a GLuint, while glCompileShaderARB takes a GLhandleARB, which is usually a 32-bit signed integer, but on MacOSX (for a reason that escapes me) is a pointer. Your code will fail in a 64-bit MacOSX build, since GLuints are always 32-bits in size.
Only you won’t get a compilation error. Since you cast the function pointer to a function that takes a GLuint, C will dutifully obey you. And thus, the error you get is that you’ll call a function that expects a parameter of a different size. Which leads to undefined behavior in C, and God only knows what kind of error you’ll get at runtime. It could be somewhere in the driver, long after the function call returned. It could be stack trashing that only shows up a few million lines of code away. It could be nothing… until an unfortunate memory allocation or rare combination of code causes it to appear. And every time you attempt to debug it or change your code to find it, it will appear in a completely different location.
In short, what you’re doing is both dangerous and non-portable.
Also, there is no glCompileShaderEXT and there never was. You cannot blindly assume that every core function has an ARB and EXT equivalent.
- no failing incorrectly if a function is not supported (optional functionality should not make the game bail out)
- failing if driver claims core but fails to deliver a function (for optional functionality treat it as extension not existing)
- retrieving core function over extension function if existing (extension function as fallback if core not found).
You see a better way to deal with problematic GPU drivers and core/extension muddle?
It all depends on how you define “better”. I prize de-facto correctness (ie: writing towards the specification), because at least then if my program doesn’t do what it’s supposed to, I know who to blame. Also, if I need to do implementation-specific fixes, I have a spec-correct baseline to start from.
Thus, if an implementation claims to provide version X.Y, but not all of the X.Y functions are actually exposed, then I would say that the implementation is too broken for the program to continue or recover. Even if the functions are for optional features of my program. An implementation that is so broken that it can’t even expose broken versions of functions that it claims to support should not be trusted. I certainly would be disinclined to trust that the functions such an implementation actually provides do what they say that they do.
Extensions should only be used as “fallbacks” to core functions if those are so-called “backwards-compatibility” extensions. That is, if they lack an extension suffix. Otherwise, you’re code should be doing the actual work of using the extension feature.
You should never assume that, for example, “glBindBufferRangeEXT” (from EXT_transform_feedback) does the exact same thing as “glBindBufferRange”. For example, there is nothing that says that glBindBufferRangeEXT can ever be used with the GL_UNIFORM_BUFFER binding point. Whereas ARB_uniform_buffer_object does state that it can be used with glBindBufferRange.
To be sure, it will probably work. But it might not. And you certainly have no foundation to stand on when submitting a bug report to an IHV if you want to make it work. The various specifications have no guarantees on the matter.
Yes, this means that if you can’t get some functionality through core or a BC-extension, then you’re going to have to put different branches for different extensions into your actual rendering code, rather than just swapping function pointers and pretending that there’s no difference. Yes, this means more work for you. But it also means that your actual rendering code will be better able to handle oddball failures, and it won’t try to demand the implementation do things that it doesn’t have to.
You don’t want to accidentally rely on implementation-defined behavior.