Stuff

I spent some time today trying to get some Direct3D 9 code to switch the display into 50Hz refresh so it could display PAL video. Displaying PAL video on a 60Hz display doesn't look very good because of the difference in frame rates -- you get a beat at the difference between the rates, 10Hz in this case -- and so it's better to change the display mode to match. Problem is, a 50Hz mode wasn't showing up in the mode list for my monitor, so I opened the NVIDIA control panel and added a custom 50Hz mode. The test ran fine and the monitor happily switched into a PAL-compatible refresh rate.

Then I tried the Direct3D 9 code, and it refused to do the mode switch.

Strangely, the old ChangeDisplaySettings() call had no problem selecting the 50Hz mode and the control panel showed 50Hz available, but a check with the DirectX Caps Viewer revealed that the mode wasn't showing up in the Direct3D APIs. Checking the docs for the IDirect3D9::CreateDevice() method revealed this ominous comment:

An unsupported refresh rate will default to the closest supported refresh rate below it. For example, if the application specifies 63 hertz, 60 hertz will be used. There are no supported refresh rates below 57 hertz.

It would be disappointing if the Direct3D 9 API was ignorant of a few continents worth of video standards, but fortunately this doesn't seem to be true at least on Windows 7. Some 56Hz modes were showing up and I could switch to those just fine, so that statement appears to be wrong.

A bit of digging with WinDbg -- okay, a frustrating amount of digging -- showed that on Windows 7, d3d9.dll uses the EnumDisplaySettings() API to enumerate available video modes. This function unfortunately only returns modes that are determined to be compatible with the current monitor. Even worse, if you happen to have your desktop set to an "unsupported" resolution, Direct3D grudgingly temporarily returns the mode in its list, which can lead to confusion. I pulled the EDID stream for the monitor and it showed that the monitor was only saying that it supported refresh rates in the 56-75Hz range, even though it actually supports both 50Hz and 85Hz as well. EnumDisplaySettingsEx() with the EDS_RAWMODE flag did return the 50Hz mode, but that wouldn't help.

Plan B was to try to use the D3DPRESENTFLAG_UNPRUNEDMODE flag, which supposedly allows access to raw modes. It requires using Direct3D 9Ex instead of Direct3D 9, but fortunately my code worked both ways. Unfortunately, there still isn't a way to enumerate the hidden modes, and setting that flag didn't seem to do anything. More digging revealed that when 9Ex is in use, d3d9.dll uses D3DKMTGetDisplayModeList() to enumerate the modes instead. That function does return all modes, but farther down the line a function called d3d9!BuildModeTableLH() has a hardcoded check for the D3DKMDT_DISPLAYMODE_FLAGS::ValidatedAgainstMonitorCaps bit and drops any modes that are missing it:

This prevents 9Ex applications from switching to unvalidated modes even if the UNPRUNEDMODE flag is set. I'm not actually sure what that flag does anymore; according to Google no one seems to be publicly using this flag and maybe this is why. Hacking out the check works, but that's not a shipping solution.

And no, unchecking the "hide modes that the monitor cannot display" checkbox doesn't affect any of this. I unchecked that a long time ago.

In the end, I wasn't able to find a programmatic way to cleanly enable these modes for Direct3D 9 applications. What did work is to override the EDID stream for the monitor to enlarge the vertical refresh rate range to 50-75Hz. This requires a collection of tools and generating a replacement monitor INF, so it's also not a shipping solution, but it appears to be the only choice if the desired custom mode falls outside of the EDID specs.

Appendix

While tracing through the mode enumeration logic in d3d9.dll I also discovered a weakness in its IDirect3D9::EnumAdapterModes() implementation. In Windows 7, and presumably in Vista, this function is implemented on top of the internal version of IDirect3D9Ex::EnumAdapterModesEx(), which returns an extended mode structure and has additional filtering options. The non-Ex version calls through to the -Ex version, discards modes that don't pass a default filter, and then translates the mode structure. When you realize that the API for both functions is to take a mode index, though, you might see the problem. When the non-Ex version is called requesting the Nth mode, it can't simply pass through the value N since it uses a filter, so what it does is to count from 0 on up until it finds the Nth mode that passes the filter. Unlike EnumDisplaySettings() there is no caching of the filtered mode list and therefore enumerating all modes with IDirect3D::EnumAdapterModes() is an O(N^2) operation. It's best to cache the mode list if you need it frequently.

11 comments | Aug 23, 2011 at 19:01 | default

Comments

Comments posted:

People who are not as good in hacking into system calls could just switch to 75 Hz mode. 50 Hz display rate is bad for eyes anyway.

Kasuha - 23 08 11 - 22:50

a) Unless your video is 25FPS, this will give you even more beating (at 25 Hz) for 50FPS video

and

2. 50 Hz on an LCD screen hasn't been bad for anyone's eyes since the advent of LCD screens

Leak - 24 08 11 - 04:49

I hit a similar problem a while back with the laptop. My TV has a native resolution of 1366x768, and I wanted to get the laptop to provide that on the VGA connector. Unfortuantly the stock IBM drivers for the graphics chip (an Intel 852GM or 855GM) claim the hardware only supports the classic 4:3 ones.

The solution I found was to use Intel's dev kit to build your own driver package, and since this chip is intended for embedded use the packager lets you specify whatever monitor timings you want. Even better, it actually lets you just enter 1366x768 (though I think it may have needed 1368 due to alignment) and the packager will work out the rest of the timings for you.

@Kasuha: that requires your display to support 75Hz. Some LCDs max out at 60Hz.

What Leak said -- plus, 50 Hz isn't all that worse for the eyes. After all, millions of Europeans were exposed to 50 Hz field rate on CRT TVs for multiple decades and you'd be hard pressed to find evidence that their eyes suffered from significantly more strain than those from Americans or Japanese :)

I can confirm, 50Hz bob-mode on 60Hz has a very visible jitter. Every LCD television I've ever seen offered 50Hz, but none of the regular PC monitors.

Gabest - 24 08 11 - 06:59

Try an Amiga in 50i mode on a CRT with NTSC phosphors, and we'll see how your eyes fare....

LCD screens go below 50Hz. My current laptop goes to 40Hz in power saving mode without visible effects other than jitter on 50/60Hz material. It's a bit evil though because some of the APIs lie about refresh rate when these low-refresh power saving modes are used and then vsync algorithms fail horribly.

Phaeron - 24 08 11 - 09:06

I'm one of those Europeans who was looking at 50 Hz TV for over 40 years (and at 50 Hz "TV monitors" attached to computers for over 10 years too) so I know what I'm talking about. A CRT is clearly blinking if you are not looking straight into it which may get very annoying, and it's tiring even if you don't see the blinking. On LCDs it may be different, depending on how fast they are. But if the LCD is slow enough to display at 50 Hz without visible blinking then I believe it's blending frames enough at 75 Hz to not show any beating too. And if it's fast then 75 Hz is clearly better.
100 Hz would be best for that purpose but I'm afraid not many LCDs can do it.

Well... that's for perfectionists. Personally I'm watching PAL videos on a 60 Hz LCD quite often and I don't complain.

Kasuha - 25 08 11 - 07:21

LCDs don't blink - their picture is static (though they do use dithering to display certain colours, but that's independent of the input refresh rate; however due to this dithering you can get blinking effects with certain patterns).

ender - 28 08 11 - 04:07

There's something seriously broken in Windows 7 that causes problems when trying to use it with 15KHz arcade monitors and the MAME emulator.

1) For some reason, Windows 7 doesn't allow applications to switch from interlaced to non-interlaced resolutions or vice verse. This worked on XP. I'm not sure about Vista.

2) Windows 7 reports refresh rates at 50% of their actual value when running an interlaced resolutions. This causes vector based games, that are typically run at 800x600 interlaced, to run at half-speed.

At the moment, for running the MAME emulator in an arcade cabinet on a 15KHz arcade monitor, the best OS is still Windows XP x64, believe it or not.

I haven't tried this in Windows 7 / Vista, but that Direct3D filtering resolutions issue seems to have been there since Windows XP at least. I found that problem also when trying to make Mame accept non standard resolutions if a monitor with a valid EDID is attached.

There is a workaround however, provided you want to support the old DirectDraw interface in your app. The IDirectDraw7::SetDisplayMode method is able to switch to any video mode returned by EnumDisplaySettingsEx with the EDS_RAWMODE flag set. Unfortunately, I couldn't find a way to force Direct3D to work with that mode afterwards, so you need to stick with DirectDraw.

Finally, there is a way you can send any arbitrary refresh rate to your monitor, completely bypassing Windows. You need to interface with PowerStrip's API for that. Actually, from Windows point of view, refresh rate values are just labels returned by the video driver which can have anything behind, so Windows itself is not "aware" of the actual refresh rate but for the value the driver says it is. So you can just enable standard 1920x1080@60 and then use PowerStrip's API to reprogram the video mode on the fly, if you know how to do this. We're just starting to experience with this functionality here:

I used to hack the monitor settings (in the registry) on each of my PC.
I tweaked it to say the monitor supports 85 Hz minimum (CRTs).
Otherwise Windows (XP and 2000 and 20003) defaulted to flickering 60 HZ each time a program (games mostly) used a new resolution.

David Balažic - 03 09 11 - 01:36

Comment form

Please keep comments on-topic for this entry. If you have unrelated comments about VirtualDub, the forum is a better place to post them.

Name:

Remember personal info?
YesNo

Email (Optional):

Your email address is only revealed to the blog owner and is not shown to the public.

URL (Optional):

Comment:

/

An authentication dialog may appear when you click Post Comment. Simply type in "post" as the user and "now" as the password. I have had to do this to stop automated comment spam.

Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.