Thursday, May 31, 2018

The role of the graphics card driver in real-time rendering

Common computer wisdom accentuates the importance of having the graphics card driver for your computer always up to date. Updates to this driver can fix bugs and misbehavior in games, and sometimes even make games run faster.

However, have you ever wondered why that is, especially in the case of the latter? Shouldn't it be the graphics card itself (in addition to your other hardware) that solely determines how fast a game runs? Why does the graphics card driver sometimes have such a great impact on the rendering speed of a video game? What role does it play in the whole process, and why is it so crucial?

On a more fundamental level, what exactly does a graphics card driver do?

(Note that the explanation below is a simplification, perhaps even an oversimplification. But it is so for the sake of brevity and clarity, even if it might not be technically 100% accurate in every single detail.)

Most typically, a program like a video game uses a graphical application program interface definition like DirectX, OpenGL, or Vulkan. These are not program libraries per se, but rather they are API specifications. In essence, what this means that the program will have code to make certain system function calls for different types of tasks related to rendering, such as loading a bunch of vertices into the graphics card memory, or compiling a fragment shader.

The implementations of these functions will be provided either by the running operating system (eg. Windows), or the graphics driver. Essentially, what's happening eg. in the case of DirectX, is this:


The program executable binary will have metadata that tells to the operating system "I need to call these system functions from these locations within myself". When the operating system loads the binary in order to be run, it modifies it in order to redirect those calls to the appropriate system implementation.

If the graphics card driver provides an implementation for a specific function, it will direct the program binary to call that. In some cases the implementation is not in the driver, and thus a kind of default implementation is provided by the DirectX runtime, which the operating system is running. (Sometimes if a graphics driver doesn't provide an implementation for something, the DirectX runtime essentially needs to emulate that feature. This is why there needs to be a DirectX library in the system at all.) With some implementations the DirectX implementation may call the implementation in the graphics driver.

So it ends up like this:


For most functions, the graphics driver needs to provide an implementation because only it knows how exactly to interact with the graphics card hardware, and what kind of data it needs.

For example, the graphics card driver has a compiler implementation that compiles vertex and fragment shaders into the machine code that the graphics card uses.

In the case of DirectX and Vulkan (and some other APIs, like Metal), the original shader source code is compiled into a hardware-independent intermediate bytecode form, when the program binary itself is being built. At runtime this intermediate form is sent to the graphics driver, which produces from it the actual machine code used by the graphics card.

In the case of OpenGL, and its shading language (glsl), OpenGL doesn't specify any such intermediate form, and the graphics card driver literally has a compiler implementation that takes the original glsl source code in textual form and compiles it into machine code.

This explains why the graphics card driver has such an important role in the entire process, and also why it has such a big impact in the performance of games. Oftentimes graphics card manufacturers will publish an updated driver that's specifically optimized for a particular new video game, and will make it run measurably faster (eg. several frames per second faster than with the old driver).

How? What they do is that they examine what kind of calls the game is doing, and with what kind of data. For example, they may notice that the game is doing a lot of calls with certain data in a certain way, and that if the graphics driver reorganizes that data in a certain manner, it makes it more efficient for the graphics card hardware to handle (without changing the behavior).

Likewise they may notice that the game uses certain types of shader code quite a lot and heavily, and may notice that the shader compiler can optimize that code to shave off a few clock cycles (again without changing its behavior), which may end up speeding up the overall rendering by several frames per second.

No comments:

Post a Comment