Because of the work I have done on the iOS and Android renderers, my OpenGL engine had to stop being dependent on GLUT. This was a good thing anyway, since GLUT is outdated, and no longer maintained, and has various issues. Anyway, I decided that now would be a good time to remove the GLUT code from the Windows code at least, and then try to host it in a .NET form (there will be a BHM library for .NET soon, and a simple tool).
Normally you are told that you always need to use the CS_OWNDC style for the window that you are attaching OpenGL to. Now the problem is, in .NET this style does not exist. At first I just ignored it, as I was not quite sure what the significance of that flag was anyway, regarding OpenGL, or whether or not .NET would set it by default anyway (after all, it’s still using native windows underneath). And I just happened to be developing on my laptop, which ran the code just fine on its Intel X3100 IGP.
When I tested the code on my desktop, with an nVidia GeForce GTX460, the code failed. That is, it initalized correctly, and the renderloop was running, but nothing was being drawn on my window. So I tested the native code with CS_OWNDC, and then the OpenGL code would run as expected. So the problem is related to the DC somehow.
At first I tried to manually set the CS_OWNDC style on the window class of the .NET window in native code. However, that did not seem to work either. If I tried GetDC() after that, it returned an invalid handle. So I decided to dive a little deeper into CS_OWNDC, and found this page: http://blogs.msdn.com/b/oldnewthing/archive/2006/06/01/612970.aspx (Great blog anyway, full of interesting Windows factoids, and I really like the author’s style).
So, apparently CS_OWNDC caches the DC for your window, and makes sure you always receive the same DC when you call GetDC(). It never destroys or reinitializes this DC, so the state becomes persistent, unlike with regular windows.
And that explains what I have been seeing: As a nice programmer, I try to call GetDC() only when I need the DC, and do ReleaseDC() as soon as I’m done. Now the problem here is, I attach the OpenGL context to the DC, then I release it again. Now when I call GetDC() again with CS_OWNDC, I get the same DC, with the OpenGL context attached. So if I then want to call SwapBuffers(), it works fine. If I don’t have CS_OWNDC, I can get an entirely different DC, and SwapBuffers() won’t do anything, because there is no OpenGL context attached to that particular DC.
So, what to do when you can’t set CS_OWNDC? Well, just hog the DC handle. I just do a GetDC() when I create and attach the OpenGL context, and I don’t ReleaseDC() until the OpenGL context is discarded. Effectively this is no different from CS_OWNDC, as the DC would persist in the DC cache anyway, as long as the window was alive. However, what is different is that if you call GetDC() multiple times for the same window, you get different DCs everytime. As a result, the application behaves exactly like other ‘normal’ windows, without persistent state, and you won’t run into strange problems when you think you have two different DC’s, but they are actually the same one. Also, CS_OWNDC can conflict with CS_CLASSDC, a problem that we do not have now. So I personally think that hogging the DC handle is a slightly nicer way than using the CS_OWNDC flag.
It’s just a tad strange that:
1) OpenGL on Windows promotes, even forces, such dirty use of DC’s. The preferred usage of DCs on Windows in normal applications is to release a DC as soon as you’re done. Why does OpenGL want to attach itself to the DC in the first place? Direct3D is attached to the window handle instead, DC’s are not used at all.
2) Apparently on some systems it will work if you don’t use CS_OWNDC, yet GetDC() and ReleaseDC() everytime, while on others it doesn’t. I wonder why it works anyway… Does the driver do something clever under the bonnet, so that it attaches to the window anyway, rather than just the DC? Or does it somehow make the system return the same DC everytime (either through setting CS_OWNDC silently, or some other magic)? Or does it just hold a copy of the DC internally, somehow?
Anyway, this was basically just an introduction… now to get to the real topic… I still have an older development system, which I normally use mainly for music/recording/etc. It has an ATi Radeon X1900XTX 512 videocard. I figured it’d be interesting to use that system to test my code on: Does it behave like Intel regarding the DC’s, or like nVidia?
Well, I’ll just tell you right away that it behaves like Intel: no problems when not using CS_OWNDC. Which is as OpenGL should be working (on other platforms, you attach it to the window as well, not to something similar to a DC)… On the other hand nVidia treats DC’s as they should be treated, so technically they are correct. But, the real story is about how I got OpenGL to work on that box.
Namely, when I first tried my OpenGL code, it just didn’t work… Oh that’s right, I forgot. For some reason the OpenGL driver did not install properly in Windows 7 x64 and Vista x64. I also had a copy of XP on there, and that’s the only one where OpenGL worked properly. I knew that the card had always supported OpenGL before, and the lack of OpenGL in Windows 7 and Vista was probably just some configuration issue… It’s just that when I first noticed the problem, I did not pay much attention to it. I rarely use OpenGL at all, and especially not on that box.
However, it was quite annoying right now, so I figured I’d try to find out why it didn’t work. At first I just tried to google, and found this page: http://larsmichelsen.com/windows/opengl-in-windows-7-with-legacy-ati-geforce-x1900-gt/
Yup, that sounds like the problem I have. His solution did not work for me however. So I started digging deeper. Initially I thought the problem was somewhere in atioglxx.dll, so I tried some older versions. For some strange reason, a really old version (8.1) worked. Aha… so I may be on to something then?
Well not really… I tried many other versions, and didn’t find any that worked. So then I decided to just run GPU Caps Viewer in the debugger. It did not crash, but it only reported OpenGL 1.1 GDI with 2 extensions. So apparently something went wrong somewhere during loading and initializing atioglxx.dll. Now that I had trapped the exception in the debugger, I could see what the problem was, and where it occured. As it turned out, it was a null pointer exception, but not in atioglxx.dll itself, but in aticfx32.dll instead. It seemed to call some initialization function, which filled some structure with data. One of these was a pointer, that apparently failed to initialize, since it was 0. However, the library proceeded to use the pointer without any sanity checks, and as such, it crashed. Apparently at that point the exception handling in opengl32.dll kicks in, and it tries to load another driver. So if you don’t run inside a debugger, you never see the problem, and OpenGL appears to load… just not with the hardware accelerated driver you’re expecting.
So I inspected aticfx32.dll a bit further… I noticed that it had a newer date than most of the other driver files… Hmmm… So I looked inside the unpacked installer directory to see if it was the correct version. And well… it was not a version conflict. No, that entire file is not in the legacy drivers at all! So why is it on my system, and why is the OpenGL driver trying to load it?
Well, I know why it is on my system: I once had the Radeon 5770 installed in that box. Apparently the driver uninstaller did not clean up all files nicely.
Why is the OpenGL driver trying to load it? Beats me. Sounds like the OpenGL driver is from a shared codebase, and this library contains some code specific to newer GPUs.
And then, even if this library is accidentally loaded, why does it not have robust sanity checks? Well, that’s just the poor standards that the AMD driver development team has become famous for. I’m sorry to disappoint those who thought it was just a myth.
Anyway, after I deleted the aticfx32.dll and various other suspicious ati dlls with the same date, OpenGL magically started to work, at last! At least I now know what the problem is.
It’s just a complicated problem, created by various levels of failure in the AMD driver packages.
If the drivers uninstalled cleanly, the problem would never have presented itself.
If the libraries didn’t have their behaviour depending on what libraries they may or may not encounter on the system, the problem would not have presented itself. By extension, it also means that OpenGL will never work on a Radeon X1900 card if you have a second, newer Radeon card in the same system.
If the libraries had proper sanity checks, the problem would not have presented itself.
If AMD did not have separate legacy and current driver packages, the problem would not have presented itself. What is with that anyway? The Radeon X1900 is not even that old. It still supports the minimum requirements for Windows 7 and 8. It’s from 2006, and it already went into ‘legacy’ support back in 2009, I believe. Even before Windows 7 was out. As a result, there are no official Windows 7 drivers. Instead, you install the Vista driver package under Windows 7 (which is WDDM 1.0 rather than 1.1). It appears that AMD has dropped support completely anyway, since the last legacy driver release is from February 2010.
If you look at how nVidia handles driver support… nVidia’s 7-series is from that era as well, and they are still fully supported by the current driver package. Even nVidia’s 6-series are still supported by the latest drivers, and those cards date from 2004!