XNA: GetCapabilities throws an undocumented InvalidOperationException

BUG: Using XNA Game Studio 2.0 on a multi-monitor system with one of the adapters that has hardware acceleration turned down will result in an undocumented and allegedly "unexpected" exception when calling GetCapabilities on that adapter. This also happens when calling the default Run() method.
            foreach (GraphicsAdapter adapter in GraphicsAdapter.Adapters)
            {
                System.Diagnostics.Debug.Write("Adaptor Name: ");
                System.Diagnostics.Debug.WriteLine(adapter.Description);
                System.Diagnostics.Debug.WriteLine(adapter.DeviceName);
                GraphicsDeviceCapabilities caps = adapter.GetCapabilities(DeviceType.Hardware);
                if (caps.MaxPixelShaderProfile < ShaderProfile.PS_2_0)
                {
                    System.Diagnostics.Debug.WriteLine("This adapter doesn't support PS 2.0");
                }
            }
            using (Game1 game = new Game1())
            {
                game.Run();
            }


Description: using the default Windows XNA project on my triple-monitor system (two monitors driven by an NVidia GeForce 6200 TurboCache PCI-E card, one driven by a 6200 PCI card), I was getting the above pictured error coming when calling game.Run(); "A first chance exception of type 'System.InvalidOperationException' occurred in Microsoft.Xna.Framework.dll Additional information: An unexpected error has occurred."

In my little bit of reading about XNA to this point, I knew that XNA requires Pixel Shader 1.1 support as a minimum (although, a reference rasterizer is available, but not well documented). To verify that my cards met this requirement, I inserted code to interrogate each adapter on my system, which only pushed the "unexpected error" earlier in the code.

I did some further research, and GeForce 6200 cards implement the Pixel Shader 3.0 spec, so I'm comfortably within the required hardware for XNA.

Reading the documentation for GraphicsAdapter::GetCapabilities, there's no mention of raising an InvalidOperationException.

I fired up the "DirectX Caps Viewer", which is part of the DirectX 9.0c SDK, and looked at "DirectX Graphics Adapters/NVIDIA GeForce 6200 TurboCache(TM)/D3D Device Types" for all three adapters. Two out of the three adapters had HAL and Reference devices, the third one just had a reference device. Six hours later, it occurred to me to check the hardware acceleration settings for that device:


Setting the acceleration slider to "Full" allows the program to run without raising the exception.

Workaround: It's troublesome that the very code that is supplied to check the capabilities of hardware fails, depending on the configuration of that hardware. If you're getting this exception, perhaps you can trap it and provide useful feedback to the users. I'm worried, though, because the exception is so general. It may well be raised by other undocumented failure cases, making the feedback you provide only marginally useful.

Using GraphicsAdapter::CheckDeviceType seems marginally helpful:

            foreach (GraphicsAdapter adapter in GraphicsAdapter.Adapters)
            {
                System.Diagnostics.Debug.Write("Adaptor Name: ");
                System.Diagnostics.Debug.WriteLine(adapter.Description);
                System.Diagnostics.Debug.WriteLine(adapter.DeviceName);

                SurfaceFormat curFormat = adapter.CurrentDisplayMode.Format;

                if (!adapter.CheckDeviceType(DeviceType.Hardware, curFormat, curFormat, false))
                {
                    System.Diagnostics.Debug.WriteLine("This adapter doesn't have hardware support");
                }
                else
                {
                    GraphicsDeviceCapabilities caps = adapter.GetCapabilities(DeviceType.Hardware);
                    if (caps.MaxPixelShaderProfile < ShaderProfile.PS_2_0)
                    {
                        System.Diagnostics.Debug.WriteLine("This adapter doesn't support PS 2.0");
                    }
                }
            }
            using (Game1 game = new Game1())
            {
                game.Run();
            }
In it, I check to see if the device has hardware acceleration. This isn't sufficient to fix the problem, as GraphicsDeviceManager doesn't make this check in FindBestPlatformDevice, which is called within Game.Run().

To really fix this problem, it seems like you'd want to subclass off of GraphicsDeviceManager, and discard non-hardware-accelerated devices in your own implementation of FindBestPlatformDevice. (This would probably be similar to the code in the "How To: Restrict Graphics Devices to Widescreen Aspect Ratios in Full-Screen Mode" sample.)