Legacy Catalyst driver problems with OpenGL

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!

This entry was posted in Hardware news, OpenGL, Software development, Software news and tagged , , , , , , , , , , , . Bookmark the permalink.

12 Responses to Legacy Catalyst driver problems with OpenGL

  1. Pingback: Legacy Catalyst driver problems with OpenGL, AMD replies | Scali's blog

  2. milton says:

    im having the same problem but with radeon 7970, only opengl 1.1 there is no aticfx32.dll here dont know which dlls i have to delete

    • Scali says:

      The Radeon 7970 does not use the legacy driver, so you should not have to delete any files.
      If you don’t have these files, then I guess your driver did not install properly. I suggest downloading the latest Catalyst drivers from amd.com and installing them, that should fix the missing files.

  3. Hinagiku says:

    I ran into the same problem after re-installing my old X1900 to RMA a defective HD7850. To avoid manually deleting files, I tried the Catalyst Uninstall Utility first. Installing the Legacy Drivers afterwards, OpenGL finally worked just fine.

    • Scali says:

      I had a Radeon 5770, probably with much older drivers. Perhaps they fixed the uninstaller now.
      But that still wouldn’t solve the issue if you try to use an X1900 and a 5000+ series card together.

  4. Pingback: How To Fix Atioglxx Run Dll Problems Errors - Windows Vista, Windows 7 & 8

  5. Rajveer says:

    In my engine, every frame I call GetDC and ReleaseDC around wglMakeCurrent multiple times (once for each window), followed by GetDC and ReleaseDC around SwapBuffers for each window after they’ve been drawn to, and everything works fine (this is without CS_OWNDC). I never considered that I could be getting back a different device context, but everything seems to work fine, so am I just lucky?

    • Scali says:

      Well, if you use wglMakeCurrent() every time, then you reconnect the OpenGL context to the DC, so then you’re probably safe.
      But that takes extra overhead… the ‘classic’ way of rendering OpenGL is to only use wglMakeCurrent() once, and use CS_OWNDC to make sure you will get the DC with the OpenGL context attached every time.

      • Rajveer says:

        Let’s say I have 2 windows, then per frame the way I currently (and perhaps incorrectly) do this is as follows:

        HDC deviceContext = GetDC(hWnd1);
        wglMakeCurrent(deviceContext, renderContext);
        ReleaseDC(hWnd, deviceContext);

        //DRAW TO WINDOW 1

        HDC deviceContext = GetDC(hWnd2);
        wglMakeCurrent(deviceContext, renderContext);
        ReleaseDC(hWnd, deviceContext);

        //DRAW TO WINDOW 2

        HDC deviceContext = GetDC(hWnd1);
        SwapBuffers(deviceContext );
        ReleaseDC(hWnd1, deviceContext );

        HDC deviceContext = GetDC(hWnd2);
        SwapBuffers(deviceContext );
        ReleaseDC(hWnd2, deviceContext );

        wglMakeCurrent(0, 0);

        So at the time of the SwapBuffers() call for hWnd1, the OpenGL context is still connected to the device context of hWnd2, but this seems to work fine.

      • Scali says:

        So at the time of the SwapBuffers() call for hWnd1, the OpenGL context is still connected to the device context of hWnd2

        This sounds like it may not be correct, and therefore it may just ‘accidentally’ work on some drivers, just like the problem I described above.
        Anyway, if you are rendering to multiple windows like that, you can consider using multiple threads… That is what I did: each window got its own thread, and I connected the context to it only once.

  6. Rajveer says:

    I’ve tested it on Intel, AMD and Nvidia hardware and it seems to work on all of them. I think it works because by time I perform my SwapBuffers, the window’s buffers have already been drawn to, and the device context is only used to swap the buffers of the window it’s associated with at the time. Although I agree, the classical way is probably more efficient.

    When drawing with a separate thread per window, you must have had a separate OpenGL context for each thread right? I’ve read that rendering from multiple threads can degrade performance as for each call the driver has to switch to the context for that thread, but how did you find it in reality?

    • Scali says:

      When drawing with a separate thread per window, you must have had a separate OpenGL context for each thread right? I’ve read that rendering from multiple threads can degrade performance as for each call the driver has to switch to the context for that thread, but how did you find it in reality?

      On Intel and AMD it worked fine, the GPU resources were scheduled very evenly over all active windows, just like in D3D… on nVidia I ran into a strange issue at the time, where performance was very jumpy. I reported it, and nVidia fixed it with a driver update. See also this blog: https://scalibq.wordpress.com/2012/05/25/the-story-of-multi-monitor-rendering/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s