OpenGL vs Direct3D, round #231489324

Well, I’ve been working with OpenGL a bit, trying to build an example program that shows how to load and animate a skinned BHM 3d scene. I haven’t really touched OpenGL since the Direct3D 8 days… so I find myself googling around for information on the newer features such as vertexbuffers and GLSL and such. I was a bit surprised that I still keep stumbling on OpenGL vs Direct3D articles and blogs, even very recent ones. While I don’t want to do a full comparison of both APIs, nor exclaim a ‘winner’, I’d like to comment on the subject from my perspective.

Now, before people accuse me of being pro-MS or pro-Direct3D just because I’ve been using it for years now… I would like to point out that before I went with a third-party API, I had written my own full software 3D engine in Java, which was fully platform-independent. So anyway, that is my perspective… I have experience with software renderers and ‘modern day’ Direct3D, and in a distant past I have used some OpenGL as well (at university and during an internship developing CAD/CAM software). I’m now trying to step back into the world of ‘modern day’ OpenGL.

One of the first steps on my journey was to visit the Wikipedia pages for OpenGL, to find some good links for documentation, useful libraries and whatnot. I stumbled upon a ‘comparison’ page on Wikipedia… Reading through it, I couldn’t shake the feeling that it was pretty biased (anti-MS mostly)… some information also seemed quite outdated. So I opened the discussion page… Indeed… apparently people have been bickering about this article for years already… I agree with the people who say that such an article should not be on Wikipedia in the first place. If it was just a dry feature comparison table… yes, then it would have a place on Wikipedia perhaps… But this article? No way. You shouldn’t even TRY to make it neutral, because you will never succeed.

When I continued my travels, I also found this recent blog. Again, it seems so skewed… I thought the whole OpenGL vs Direct3D-war was long over, since there haven’t really been any major OpenGL games since Doom 3 and related games (such as Quake 4/Quake Wars, built on the Doom 3 technology). That was more than 5 years ago. That would also have been around the same time that I chose Direct3D 8 over OpenGL.

Why Direct3D 8?

Let’s go back to around that time… I think it was around 2002 when I made my choice for Direct3D. In my case, multiplatform was not relevant. I mainly wanted a ‘toy engine’ for trying out rendering techniques and effects, and possibly create a demoscene release. I used Windows myself, and in the demoscene Windows is by far the most popular platform anyway, so the main disadvantage of Direct3D only being supported on Windows was not an issue. Hence, I could focus my decision mainly on the merits of each API and the ‘secondary benefits’ (documentation, driver support, community and such).

At the time, there really only was one proper OpenGL implementation on Windows, and that was nVidia’s. Since I wasn’t interested in producing code that ran only on nVidia hardware (like so many OpenGL demos of the day), this was a big disadvantage. Back in those days, programmable hardware was just becoming widely available, and where Microsoft gave us standardized vertexshaders and pixelshaders (and also quite flexible use of fixed-function operations), OpenGL had not standardized yet, so you had to rely on vendor-specific extensions.

I was also partial to ATi hardware at the time… first I went for a Radeon 8500, and later a Radeon 9600. So I was on the receiving end of ATi’s inferior OpenGL implementation at the time. I could notice this especially when Doom 3 launched. Initially it didn’t work very well at all on ATi hardware. ATi then did a lot of work on fine-tuning their OpenGL implementation… but it was tuned mainly to run Doom 3, not OpenGL in general.

I also liked the ‘centralized’ approach that Microsoft took with its DirectX SDK. In the SDK you find the libraries you need, example programs from absolute beginner to cutting-edge graphical effects, and nice documentation, not only specifying the API itself, but also explaining the underlying hardware pipeline, and mathematical backgrounds. I have to admit, I actually used some of the explanations and formulas in the DX SDK to implement some of the math and shading in my Java 3D engine. I modeled certain functions after their D3DX API.

Another thing that helped, was that I hung out in an Amiga channel on IRC at the time, and a friend there was the lead programmer at FutureMark. He is an expert at Direct3D, and he helped me a lot with implementing various rendering techniques, and optimizing for hardware. Using a 3D API is not as simple as calling some functions. If you want good performance, you have to know how the GPU works aswell, and what to do (or what not to do). He gave me a lot of tips on that.

I haven’t really looked back since I originally made the choice for Direct3D 8. I developed a basic renderer for Direct3D, and shortly after that, Direct3D 9 came along. I ported my code over to Direct3D 9, and got a Radeon 9600 for full Shader Model 2.0 support, and had been using that for years. In the meantime I paid a bit of attention to OpenGL every now and then, but more and more it seemed that new extensions in OpenGL were just mimicking Direct3D functionality at that point. It seemed like the war was already won by Direct3D. Shader Model 1.x was skipped altogether by the ARB, and their GLSL for Shader Model 2.0 and beyond seemed like almost a direct copy of Direct3D’s HLSL.

OpenGL 2.0 was supposed to be the big API update that would push OpenGL beyond Direct3D again, but it was very late, and in the end it wasn’t really an API update at all. It wasn’t much more than pulling a set of extensions into the base. At that point OpenGL’s fate seemed to be sealed. Recently there was another ‘major’ update planned, with OpenGL 3.0… but it was the same story of OpenGL 2.0 all over again: too little, too late.

Fast-forward: present day

So, here I am today then, developing with OpenGL 3.2 at my disposal. Or not? If you have an nVidia or ATi GPU, then yes…. you can have OpenGL 3.2. My laptop however has an Intel IGP (GM965 chipset). It is a DirectX 10-capable part, with full SM 4.0 support. I’ve used it to develop some of my Direct3D 10/11 engine. However, the OpenGL drivers only support a bare minimum of the OpenGL 2.0 specification.

On my FreeBSD box, it is even worse. I have an Intel G31 chipset on there. That’s a DirectX 9-capable SM2.0 part. The OpenGL support is below 2.0. It doesn’t even support GLSL, and I haven’t had much luck trying to get the VBO extension to work. It just crashes.

So although the OpenGL API is at level 3.2, it doesn’t actually mean that it is supported by all vendors. With Direct3D the situation is different. With the downlevel functionality that I mentioned before, you can use the DirectX 11 API on any hardware that has DirectX 9 driver support. In practice that means the API works everywhere, even on Intel IGPs. And DirectX 9 itself also offers you shaders and vertex buffer functionality, which also works on these Intel IGPs, unlike in OpenGL, where the driver doesn’t expose it.

This means that although I originally wanted to make a multi-platform sample program for my BHM file format, it won’t actually work on my FreeBSD machine, because of poor driver support. The same machine can run the Direct3D 9, 10 and 11 versions of the skinned BHM file, if I install Windows Vista or 7 on it. I don’t think it would run the OpenGL version of the code under Windows either, because it lacks the same GLSL/VBO extensions there.

Another thing I noticed, is how some of these extensions are just one big kludge in OpenGL. Direct3D has had hardware vertex buffers as a fundamental part of the API ever since Direct3D 7. OpenGL has had vertex array support for a long time, but the functions only accepted a direct pointer as array input, so you could not use hardware buffers, only system memory. It’s not the same concept as Direct3D where you actually allocate your buffer directly in videomemory on the videocard.

So that’s where vertex buffer objects come in. They are an extension which allow you to allocate buffers in videomemory, which greatly improves performance. However, they are ‘hacked into’ the vertex array API. That’s where the nasty part is. You bind the hardware buffer to OpenGL, just before you use the standard glVertexPointer() function to set the buffer. Binding a hardware buffer will silently modify the behaviour of this function… Suddenly you are not actually setting a pointer anymore (despite the name). No, the GLvoid *pointer parameter is suddenly interpreted as an offset into the hardware buffer that you bound earlier. Aside from this very ugly and unintuitive approach, it also takes quite a few function calls to set it up… Namely, the glVertexPointer() function only sets the pointer for the position info. You need to call more gl*Pointer() functions to set your normals, texture coordinates, colour data and other per-vertex information, even if you just want to use a single vertex buffer with all per-vertex data packed together.

And then there is D3DX… Something I had taken for granted. OpenGL has no equivalent. Now I don’t even use all of the D3DX library. I mainly use the functions for loading textures, compiling shaders, and the math library, that gives me basic vector, matrix and quaternion operations. Especially these last few pose some problems. If you just want simple camera and object animation, OpenGL provides some basic functions to set up a view and perspective matrix, and it has a simple matrix stack which you can use for simple hierarchical object/scene graph animations. Basically you can’t do much more than multiplying matrices together.

However, when it comes to animated skinning animations, you will need quite a bit more than that. Although there are the ‘usual suspects’ in terms of companion libraries for OpenGL, such as GLU, GLUT and GLEW (which I use), there doesn’t seem to be a ‘go to’ math library for OpenGL. That’s rather strange, especially since you never actually hear anyone talk about this issue.

Now, technically I wouldn’t be bothered, because I had to write it all myself for my Java engine aswell, and I probably still have a C++ version of most of that code from earlier software renderers that I did… but I wanted to keep my sample application small and simple, and use standardized components as much as possible. After looking around a bit, I’ve decided to try the CML library.

Some more thoughts on past and present

The way it looks to me is that OpenGL is just surpassed… It once started as a software rendering API with SGI, which later implemented some early 3D acceleration. It was a good API at the time, but it seems that when videocards became GPUs (basically when they got T&L support, and required vertex data to be stored in videomemory), the API was surpassed by the state of the hardware. Although SGI had an eye on the future with their extension mechanism, it has come to a point where you basically rely on these extensions almost exclusively, and the original idea of the API is lost completely (just like how VBOs basically bypass and ‘redefine’ how OpenGL’s vertex array functionality work, using GLSL will bypass most of the fixed-function lighting and shading and replace it with extension-based functionality).

Microsoft had the same problem, but they decided early on that they would not stick with a single API very long. Every major version of Direct3D means a new API. Sometimes these changes are minor (eg the move from Direct3D 8 to 9, or from 10 to 11), and sometimes they are major (like the move from Direct3D 9 to 10). But by moving to a new API, Microsoft has the freedom to finetune it for the current (or upcoming) generation of hardware, and in some cases, Microsoft can also introduce a new driver infrastructure to better suit the new hardware. In practice this has meant that Direct3D has generally adopted new hardware features sooner, and compatibility and performance were better, at least in the case of vendor-agnostic support (especially nVidia would often offer new functionality through vendor-specific OpenGL extensions almost immediately). With OpenGL, the idea to redesign the API was proposed with the original 2.0 spec, but it never became a reality. With 3.0, another opportunity to redesign the API was lost.

I wouldn’t be surprised if the hardware vendors were against renewing the API, because it meant they had to implement all the changes in their driver… In the Direct3D model, Microsoft supplies the user runtime, and the driver only needs to implement the basic lowlevel operations. This means that hardware vendors have less work to do, which also means less chance of bugs or inconsistencies. Aside from that, Microsoft has the WHQL program, which tests the driver functionality, so bugs and inconsistencies are less likely to slip through. These factors probably contribute to the good Direct3D driver quality across vendors.

The irony seems to be that OpenGL’s life on the PC started in the reverse situation… OpenGL was originally developed for workstations that had far more powerful CPUs and video chips than your average PC. Initially only high-end 3D accelerator cards for PCs had OpenGL support, not the average consumer-level 3D game accelerators, which didn’t support most of OpenGL’s feature set anyway. OpenGL was just overkill. This naturally meant that OpenGL wasn’t very useful for games initially. Early 3D games mainly supported the vendor-specific APIs, where Glide was by far the best-supported, because of 3dfx’s large marketshare with their Voodoo accelerator series.

That’s where Microsoft comes in with Direct3D. People often claim that Microsoft developed Direct3D only to destroy OpenGL… but the fact is that OpenGL was never meant as a gaming API, and in the early days of 3D acceleration, OpenGL was not supported by consumer-level cards. The first version of Direct3D came with DirectX 2.0, in 1996. The main reason for Direct3D is the same reason as all the other DirectX components: vendor-agnostic low-level hardware support. At this point, every vendor had their own proprietary 3D API, and game developers were required to build in separate support for each API. In practice this was too much work, so most games only worked on the most popular option: 3dfx Glide.

So Microsoft wanted to create an API that would work on all common 3D accelerator hardware, and was more akin to Glide than to OpenGL, a simple lowlevel API that would only support the actual functionality of the hardware. It took Microsoft some time to get it right, which was especially difficult since the hardware advanced at such a breakneck pace.

One of the first games that actually used OpenGL was GLQuake… But did it really use OpenGL? Technically it didn’t. It used a small subset of the API, which could also work on simple game cards. 3dfx didn’t have a full OpenGL implementation, instead they had ‘miniGL’, which only implemented the subset for running GLQuake. Many other vendors didn’t have any kind of OpenGL implementation at all, at that point (this was around 1997). Here is an excerpt from the GLQuake readme file, which will give you an idea of the state of OpenGL at the time:

Theoretically, glquake will run on any compliant OpenGL that supports the
texture objects extensions, but unless it is very powerfull hardware that
accelerates everything needed, the game play will not be acceptable.  If it
has to go through any software emulation paths, the performance will likely
by well under one frame per second.

At this time (march ’97), the only standard opengl hardware that can play
glquake reasonably is an intergraph realizm, which is a VERY expensive card.
3dlabs has been improving their performance significantly, but with the
available drivers it still isn’t good enough to play.  Some of the current
3dlabs drivers for glint and permedia baords can also crash NT when exiting
from a full screen run, so I don’t recommend running glquake on 3dlabs
hardware.

3dfx has provided an opengl32.dll that implements everything glquake needs,
but it is not a full opengl implementation.  Other opengl applications are
very unlikely to work with it, so consider it basically a “glquake driver”.

This quick evolution of hardware meant that the gap between OpenGL’s featureset and the hardware was closing rapidly. Aside from that, most vendors quickly dropped out of the 3D race, and the few that remained, offered useful OpenGL drivers. This meant that OpenGL was now a good alternative to Direct3D for vendor-agnostic 3D accelerated games. This is when the ‘golden age’ of OpenGL in PC gaming started.

Because of OpenGL’s relatively high-level nature, there was a certain gray area between the API level and the hardware level. Initially, 3D accelerators could do little more than fill a few scanlines between a left and a right edge, basically just the last step of a software rasterizer, the ‘inner loop’. The CPU still had to do all the setup for the triangles.

As the acceleration became more advanced, more of this triangle setup was offloaded to the videochip. With OpenGL, this was all hidden from the user, and the acceleration could easily be incorporated ‘behind’ the API, inside the driver. More lowlevel APIs required a redesign to allow more acceleration. This also meant that older applications could not benefit from the newer, more efficient rendering methods. With OpenGL, the driver was free to use the new functionality for older applications aswell.

Especially with the advent of hardware T&L on the nVidia GeForce cards (which was the first time that the term ‘GPU’ was used), this was quite an advantage for OpenGL. Games could automatically make use of the new hardware T&L, which also relieved the CPU, and resulted in a significant performance boost. In this era, various games had the option to either use Direct3D or OpenGL (originally because OpenGL support was not always available, or didn’t work properly, so there was an alternative). Gamers quickly found out that these games ran much faster in OpenGL mode. For this reason, it seems that many gamers still think that OpenGL is a faster API, even today. But more on performance later.

Microsoft needed to redesign their API again, to leverage the new hardware T&L functionality. Direct3D 7 would be the first API that took advantage of hardware T&L. This proved to be a turning point… Namely, all this time, OpenGL was ‘ahead’ of the hardware functionality, so new acceleration features could be implemented ‘under the hood’ quite easily, and would automatically be used with any OpenGL application, old and new. All this time, Microsoft had been chasing the hardware developments, and had to throw their API around a few times, because of its lowlevel nature, and very close ties to the underlying hardware. But now that Direct3D had T&L support, it supported it the ‘proper way’. It was still quite lowlevel, and the programmer had very good control of how to handle the vertex data. This meant that you could get excellent performance out of the GPU.

OpenGL now started to run into the problem that I already mentioned earlier… Using the standard API methods for rendering (such as glVertexPointer()) was not enough to get good performance from the GPU. It was now up to the driver to try and optimize the drawing code on-the-fly, and try to buffer it in videomemory itself. This worked okay for static geometry, but with dynamic geometry, the driver would not do a good enough job. Only with extensions could you give the programmer the control that he has in Direct3D 7, and drive the GPU to the maximum. And these extensions were not yet standardized in OpenGL, so you had to implement it separately for each vendor.

Another thing is that both the hardware and the Direct3D API had ‘matured’ at this point. The original Direct3D API was very cumbersome to use, with its execute buffer system. Microsoft had already tackled this a new API in Direct3D 5, which introduced the DrawPrimitive() and DrawIndexedPrimitive() functions to draw a list of geometry more easily and more efficiently. In Direct3D 7, they just added the use of vertexbuffers and indexbuffers to these functions, rather than passing them data directly from system memory, and that was that. So for a programmer it wasn’t that big of a step to upgrade. Most of the API remained pretty much the same.

With Direct3D 8 and 9, the upgrades were also not that large. Some things were simplified (you no longer had to rely on DirectDraw to manage the actual window), and other features were just added to the API in a relatively non-intrusive way (mainly shader functionality). But at the same time, Direct3D always kept that lowlevel control for the programmer, so he had the freedom to tweak the GPU for maximum performance.

Since the introduction of Direct3D 7, OpenGL quickly dropped in popularity. The performance advantage was no longer there. And although Direct3D was never a very easy API to use, OpenGL became about as difficult to use, because you had to rely on extensions to tweak for maximum performance, and to make use of new features such as programmable shaders. The APIs were now pretty evenly matched in terms of usability, features, and performance.

From this point on, Microsoft could actually get ahead of OpenGL, because Microsoft defines new API specs together with the major 3D hardware vendors, incorporating all the main upcoming features for future hardware in the new API version. OpenGL was generally updated ‘after the fact’, by taking extensions that vendors have added to their drivers, and generalizing them into a vendor-agnostic version under the “EXT” or “ARB” label. In practice this meant that although Direct3D 8 had support for the first generation of shaders from the GeForce 3 and Radeon 8500 cards, an equivalent EXT or ARB extension for OpenGL never emerged.

With Direct3D 9, Microsoft introduced Shader Model 2.0, for the new Radeon 9700 videocards, and a high-level programming language for shaders, called HLSL. Although OpenGL eventually got equivalent functionality in the form of GLSL, it took way too long, and developers just weren’t going to wait for it. Theoretically it was an option to write a vendor-specific path for each vendor, as there were technically only two vendors left that mattered (nVidia and ATi), not many game developers chose that path. The only major OpenGL game released in this era was Doom 3, which actually did contain multiple specific renderpaths. Originally it had a path specific for ATi and for nVidia, but during development, ARB extensions became available which made the ATi path obsolete, so it was dropped. The NV-path remained in the final release (NV hardware could execute the ARB path, but the NV path was about twice as fast).

Direct3D 10 was again a big step in API design, difficult for developers to adapt aswell. And it required Windows Vista as a minimum… But there was no OpenGL update to take advantage of this situation. The opportunity seems to have passed now, with Windows 7 being adopted quickly, and quite a few major games are already using Direct3D 10, some even Direct3D 11, so developers have already adapted.

So… performance?

I’ve touched on performance already… As I said, OpenGL clearly had an advantage in the early days… between 1997 and 2000, more or less. But what has happened since? An interesting report can be found on this blog. They originally had a multiplatform renderer which used OpenGL on Windows. They then ‘shoehorned’ Direct3D 9 into there. And the result is that Direct3D 9 is actually significantly faster in most cases.

It shows that OpenGL certainly isn’t the faster API like it was in those early days. It might not necessarily be the API itself, but rather the driver implementation. OpenGL needs to manage quite a few states, especially with the use of all these extensions which silently modify API behaviour. Direct3D 9 is optimized very well these days. Although technically Direct3D 10 and 11 should cut down on the state management overhead, in practice Direct3D 9 still delivers the same performance, or in some cases even better. It is THAT optimized. It could be that OpenGL just isn’t at the same level (probably related to the fact that so few games make use of it, so OpenGL performance is not a very interesting thing to optimize for, as a driver developer).

One thing we probably CAN conclude however, is that the Windows drivers for OpenGL are generally the most optimized. There aren’t many games that are available on more than one platform, but Doom 3 seems to be a good example. And Doom 3 runs slightly faster on Windows than on linux. That may seem a bit odd, as there is a myth related to the “OpenGL is faster” myth, that stemmed from the early days of OpenGL… and that is the myth that linux runs games faster than Windows.

I believe this myth is rooted in the fact that the sourcecode for the Quake games was released by ID. This allowed linux users to compile and optimize for their systems. If you were to benchmark those against a vanilla binary Windows version from release, it’s no wonder that the linux version is faster. I’ve seen similar situations on Windows, where there were replacement DLLs available for games, which made use of new 3DNow! or SSE instructions to greatly accelerate some parts of the game.

To conclude…

Well, what to take away from this article? I’m not quite sure. My personal stance however, is that in the end it is the hardware that determines performance, provided you use the API correctly. In most practical cases I don’t think there will be a large difference between OpenGL and Direct3D performance in general. When it comes to using the API as a programmer… I think I have to discern two separate situations:

  1. ‘Getting something on screen’, when you are new to programming with 3D acceleration.
  2. Building a serious 3D application with up-to-date performance and visual quality.

In the first case, I think it is a clear win for OpenGL. It has a very easy to use immediate mode, which means the learning curve isn’t very steep (at first). Getting a few triangles on screen, lighting them and moving them around is very easy. Direct3D has no ‘easy’ mode, you need to get down and dirty right away, and mess with vertex buffers, render states, shaders and whatnot, to get anything at all on screen.

However, when we get more serious, we get into OpenGL’s extensions again, and then OpenGL loses its advantage. In my opinion the extensions actually make it more complicated than it has to be. Direct3D is a bit more straightforward. You can basically do things only one way in Direct3D, and that is the ‘right’ way in terms of performance. And once you start bypassing the legacy OpenGL functionality and go for the newer extensions, OpenGL starts to look remarkably similar to Direct3D. Which is only logical, since both Direct3D and the OpenGL extensions were designed for the same type of hardware, to perform the same type of functionality.

In the end, what is everyone getting so worked-up about?

This entry was posted in Uncategorized. Bookmark the permalink.

Leave a comment