Why are OpenGL functions loaded at runtime instead of dynamically linked? - opengl

Users of OpenGL APIs typically use a library such as GLEW or glad to load OpenGL functions at runtime. Why is dynamic loading the preferred linking method?
Static linking is clearly not an option since programs using OpenGL are typically compiled by the author and distributed to users who have different OpenGL libraries for their particular graphics cards.
That leaves dynamic linking and dynamic loading. It seems like dynamic linking could work since we know the names of all the functions we'll want to access.
Is dynamic linking a viable option? If not, why? If so, why is dynamic loading preferred?

What you want is possible. You can make a loader that is a DLL/SO with a static interface, which will load function pointers from the implementation behind the scenes. It will pipe function calls to the implementation, based on which context is current. Indeed, this is essentially how modern Linux distros work.
This is also how WGL works on Windows... up though OpenGL version 1.1, at any rate.
The reason it is not "preferred" is because it requires effort. Someone has to create and maintain this intermediary library that sits between the application and the driver. Whenever a new extension comes out, the library has to be updated. Whenever a new version comes out, the library has to be updated. And then you have to make sure to link with the right version of this library.
That additional dependency worked out OK on Linux, because it was kept up to date. OpenGL32.dll on Windows however was not. WinNT adopted OpenGL for a while, but Microsoft had also gone off and createdpurchased their own graphics API for Win9x. So they never updated OpenGL32.dll for later OpenGL versions or new extensions. And if they were willing to be non-backwards compatible, I imagine they'd have ditched OpenGL32.dll entirely from their OS.
Since you need a runtime loader to work in Windows, it's easier to just make all platforms work using the same runtime loader (though obviously with a different function fetching the function pointers). And while you could create the kind of DLL you're talking about, it would just be one more OpenGL loading library, one more dependency you have to maintain. Without the centralization that Linux/MESA brings to the table, there's just no point in bothering.
I was thinking that an application could be dynamically linked directly to the driver itself without an intermediate library
You can do that. However, you would be linking to that particular driver's library with a static import library. This means that if you link against NVIDIA's implementation, your application will fail to run on AMD's implementation.
And you can't statically link to import libraries for both DLLs. The reason being that if one static import library cannot be loaded, your application terminates. So unless you have both AMD and NVIDIA's implementations on your machine, you would not be able to run on either one.
Also:
since even though the driver library can't be available at compile time, we still know the OpenGL function names
That assumes that their drivers directly export those names. That is hardly guaranteed.
After all, the WGL/GLX interface is to use wgl/glXGetProcAddress to get function pointers. If the interface between WGL/GLX and the driver is simply to pass the string to the driver's function, then the driver doesn't need to export any functions directly. It exposes its API through a single entrypoint, forcing runtime function pointer loading rather than allowing static linking.
My understanding is that OpenGL loading libraries LoadLibrary/dlopen from dll/dylib/so library files which are themselves graphics card drivers and those driver libraries have symbol tables with OpenGL functions. Which parts of that are wrong?
All parts of that are wrong. You link against the basic interface API (like WGL or GLX). The loading library uses that interface to load function pointers. How the interface API translates loading a function pointer into the driver DLL is a platform-specific issue. On Windows, wglGetProcAddress calls a function in the driver, passing the string name of the function and getting back the function pointer.
On Linux, GLX tends to manufacture functions internally, using late-binding techniques to load the actual driver function later. That is, you can call glXGetProcAddress with some bogus name, and it will return a function pointer shim that, when called, will load the function for the current context from the driver.

The OpenGL wiki gives some insight into the OpenGL function loading process. Not that using the GetProcAddress functions amounts to a form of dynamic linking done outside of the operating system itself. From Wikipedia:
In computing, a dynamic linker is the part of an operating system that loads and links the shared libraries needed by an executable when it is executed (at "run time"), by copying the content of libraries from persistent storage to RAM, and filling jump tables and relocating pointers.
Extensions for OpenGL come out all the time and can be either available or not depending on vendors and driver platforms. The OpenGL wiki states that on MacOSX:
GL functions on OSX have been weak linked since OSX 10.2; this means that you can call them directly and unimplemented extensions will resolve to NULL. Note that this means that you must parse the extension string to determine if a function is valid or not, or your program will crash.
So even if Apple maintains a library to dynamically link with, you still need to add a layer to check whether a given function is available or not depending on the supported OpenGL version and extensions.

Related

Why use 'glad' library for opengl initialization? [duplicate]

This question already has answers here:
When do I need to use an OpenGL function loader?
(3 answers)
Closed 3 years ago.
I am learning now OpenGL and writing some geometrical abstractions over it for personal usage. I want my code to be maximally portable and I want to have GL context initialized in caller code (by means of FLTK, wxWidgets, WinAPI or any else supported platform).
I am reading now this popular article: https://learnopengl.com/Getting-started/Creating-a-window
Authors suggest there to use GLAD library, because this library provides creation of GL context (which does not fit me) and also it is used for 'retrieving addresses of functions' (quote: "Since there are many different versions of OpenGL drivers, the location of most of its functions is not known at compile-time and needs to be queried at run-time.").
My question is the following:
Does compiler really cannot get addresses of Opengl functions from GL-related .obj files which I specify in Linker Settings of Visual Studio? Do I really need to use wglGetProcAddress routine to refer to OpenGL functions? Is it possible to use OpenGL functions like any other functions from linked headers and .obj files?
Authors suggest there to use GLAD library, because this library provides creation of GL context (which does not fit me)
No, GLAD does not create or manage GL contexts in any way, and the website https://learnopengl.com/Getting-started/Creating-a-window never claims otherwise. They use GLFW for context and window management.
Does compiler really cannot get addresses of Opengl functions from GL-related .obj files which I specify in Linker Settings of Visual Studio?
No.
First of all, you do not specify OpenGL-related .obj files for the linker, but on Windows, you might use opengl32.lib, which is the import library file for opengl32.dll which comes with every windows version since Windows 95.
However, this DLL does not contain the OpenGL implementation you are typically using, but it contains Microsoft's OpenGL 1.1 GDI software rasterizer. The actual OpenGL implementation on windows is provided by an Installable Client Driver (ICD) which comes with your graphics driver. For OpenGL 1.0 and 1.1 functions, opengl32.dll will act as a trampoline and will forward the calls to the actual ICD DLL.
If you want to call any OpenGL function beyond OpenGL 1.1 (and that one is from 1997), you have to use the OpenGL extension mechanism in every case, as opengl32.dll does not provide these entry points at all, and the compiler/linker will of course not find them.

Why is OpenGL designed in a way that the actual functions have to be loaded manually at runtime?

I just had a read through Loading OpenGL Functions and wondered why OpenGL is designed that way, instead of the easy way, providing a dynamic library and the according headers?
providing a dynamic library and the according headers?
Then you have a misunderstanding of how a "dynamic library" works.
Let's take Windows as an example. On Windows, if all you had was "a dynamic library and the according headers", you would still be unable to just load that DLL and use it. Even the use of __declspec(dllimport) declarations in the header is insufficient. You need one more thing: an import library. This is a .lib that effectively does the job of loading function pointers for you when you load that DLL.
In short, it's syntactic sugar: nice to have, but not essential. A lot like an OpenGL Loading Library.
On Linux, things are a bit different. SO files are more self contained; they basically store the equivalent of an import library within them. So there, you could in theory get by with "a dynamic library and the according headers".
Even so, it's still sugar; you can dynamically load them and fetch function pointers manually if you wish.
All of that sugar theoretically could be used with OpenGL. But let's think about the ramifications of that.
On Windows, the import library is statically linked to your application. Therefore, it must work; if it fails to find the DLL, or that DLL does not provide the functions it ought to, then your program cannot run. At all. That's what most of those "missing DLL" errors are about; some static import library trying to find a DLL that doesn't exist.
So... what if I want to write an application that uses OpenGL 3.3 as a minimum, but is able to use features from OpenGL 4.5 if they are present? I can't link to the OpenGL 4.5 import library, since it'll fail to load on amy 3.3 implementation. So I have to link to the import library for OpenGL 3.3. But then, how do I access the 4.5 functions? That's right: I have to load function pointers if they exist.
So most people are going to have to load some functions anyway. It's much cleaner to just load the whole thing. That way, you don't have to have versioned import libraries.
Also, by loading all OpenGL functions dynamically, you don't have to wait for your OS vendor to update their DLL before you can use the next set of OpenGL functions. The main DLL simply provides a connection to the IHV driver DLL (or Open source driver on Linux) that actually implements OpenGL.
Note that Microsoft's OpenGL32.dll still only provides OpenGL 1.1. So I'd say we've benefited from not waiting on them ;)
Also, we have "the easy way": use an OpenGL Loading Library.

Getting OpenGL API functions

I'm looking at the OpenGL wiki page, and I was curious about the following line:
For reasons that are ultimately irrelevant to this discussion, you must
manually load functions via a platform-specific API call. This boilerplate
work is done with various OpenGL loading libraries; these make this process
smooth. You are strongly advised to use one. —OpenGL Wiki
Intuitively, you would think they would just provide a header for you to include. So, what are the historic reasons for this?
EDIT :
Thanks for the answers, I see now that that OpenGL supports multiple implementations of its functions, so there is no one single DLL/SO that everyone links to. I also found these quotes helpful:
The OpenGL library supports multiple implementations of its functions. From MSDN
To accommodate this variety, we specify ideal behavior instead of actual behavior for certain GL operations. From GL Spec
When you run your program, opengl32.dll gets loaded and it checks in the Windows registry if there is a true GL driver. If there is, it will load it. For example, ATI's GL driver name starts with atioglxx.dll and NVIDIA's GL driver is nvoglv32.dll. The actual names change from release versions GL FAQ
I also found that Intel doesn't provide up-to-date implementation for OpenGL, so even though I have an i7-2500, I only have OpenGL 3.0 :(.
It has nothing to do with history. That's just how it is.
OpenGL implementations, by and large, come from some form of shared library (DLL/SO). However, this is complicated by the fact that you don't own the OpenGL "library"; it's part of the system infrastructure of whatever platform your code is running on. So you don't even know what specific DLL you might be using on someone else's computer.
Therefore, in order to get OpenGL function pointers, you must use the existing infrastructure to load them.
One of the main reasons is extensions: On every platform, OpenGL has always supported platform-specific extensions. These couldn't be part of the official headers, as they were usually added by the vendors in-between spec updates. In addition, those vendor-specific extensions may live in completely weird DLL/SO, for instance, deep inside the driver. There is also no guarantee that the driver DLL exports them under their "canonical" name, so OpenGL relied very early on platform-specific stuff to load a function pointer. This makes the whole extension thing feasible.
On all platforms, you usually do get some OpenGL without using extensions (OpenGL 1.4 or so), but as the extension method was successful and is easy to implement, everyone uses it now (similar for OpenCL!)
On the Mac, you don't need to load function pointers, you just include a header and link against the OpenGL framework.

DirectX multiple DLL versions?

I see that DirectX 9 has multiple DLL versions, like
d3d9.dll
d3d9_25.dll
d3d9_42.dll
d3d9_43.dll
I'm wondering what the differences are, and how I can detect it? I have always been using GetModuleHandle("d3d9.dll");, but if an application has d3d9_43.dll, then my code will not detect that the application is using DirectX, will I? How would I go about detecting it without hardcoding all the DLL versions?
If code uses D3D9, it will always use d3d9.dll at some point. Typically this is in the import table, but it can be dynamically linked (LoadLibrary). d3d9.dll provides the init code for D3D (and COM, when needed), and exports Direct3DCreate9 to create a new IDirect3D9Object.
d3d9.dll is used regardless of version, the version is specified by passing D3D_SDK_VERSION to Direct3DCreate9 as the only parameter. This indicates what version should be used, not any DLL names.
The numbered files most often seen are D3D X files. These are versioned, with _43 referring to the June 2010 SDK (each SDK release has one, I believe). If a program needs the helper functions provided in D3DX, the latest version from the SDK is typically linked against, with the numbers keeping them separate.
The D3D9_[number] files which are not part of D3DX follow the same system (and likely have the same SDK version to number relation) and similarly provide version-specific features.
This is not say d3d9.dll is not used; a vast supermajority of software uses that file (in fact, I've not personally seen a D3D9 app which didn't). Linking against D3D9.dll and passing the SDK version is the proper and recommended method for using D3D9. Testing for it is safe and reliable, and the simplest way to check the version is to intercept the call to Direct3DCreate9.

What's the simplest way to write portable dynamically loadable libraries in C++?

I'm working on a project which has multiple similar code paths which I'd like to separate from the main project into plugins. The project must remain cross-platform compatible, and all of the dynamic library loading APIs I've looked into are platform specific.
What's the simplest way to create a dynamic library loading system which can be compiled and run on multiple operating systems without extra modification of the code? Ideally, I'd like to write one plugin, and have it work on all the operating systems the project supports.
Thanks.
You will have to use platform dependent code for the loading system. It's different loading a DLL on Windows than loading a shared object in Unix. But, with a couple of #ifdef you will be able to have mostly the same code base in the loader.
Having said that, I think you can make your plugins platform independent. Of course, you will have to compile it for every platform, but the code will be 99% the same.
Dynamic library loading an Windows and Unix/Linux works with 3 functions. A pair of functions to load/unload the libraries, and another function to get the address of a function in the library. You can easily write a wrapper around these three functions to provide cross operating systems suppport.
Ideally, I'd like to write one plugin, and have it work on all the operating systems the project supports.
Few things from top of my head:
Avoid static objects in the dynamic libraries. Provision proper initialization methods/functions to allocate the objects. The issues which occur during library being loaded by the OS (this is when the c'tors for static objects are called) are very hard to debug - next only to the multi-threading issues.
Interface headers may not contain code. No inline methods, no preprocessor defines. That is to avoid tainting application with the code from particular version of library, making it impossible to replace the library at later time.
Interface headers may not contain implementation classes themselves - only abstract classes and factory functions. Similar to the previous point - to avoid application depend on the particular version of the classes. Factories are needed as a way for user application to instantiate the concrete implementation classes.
When introducing new version of an interface, to keep things somehow backward compatible, do not modify existing abstract class - create new abstract class inherited from it and add new methods there. Change factory to return the new version. (Recall MS' IInterface, IInterface2, IInterface3 and so on.) In the implementation, use newer version of the abstract class. That by polymorphism would make the implementation backward compatible with the older interface versions. (That obviously calls for periodic interface maintenance and clean-ups - to remove the old cruft.)
Take a look at the boost.extension library, it is not really part of boost, but you can find it in the sandbox. It is kind of frozen also, but overall the library is stable and easy to use.