Friday, 16 March 2012

CRT, Upscaling and 2D Games

I'm a big fan of Super Street Fighter 2 Turbo and I play it on GGPO, so I scrutinise every aspect of my setup to achieve the best image and sound quality, and also eliminate lag or at least keep it to the bare minimum.

Just recently I decided to pick up a Sony Trinitron E530 CRT monitor to use with my Xbox so I have a complete setup to take with me to tournaments, but I also now use it at home for when I play Super Turbo on my laptop.  The CRT simply has way better colours and black levels than the laptop panel, not to mention way better motion (this is a huge thing in a reaction based game like ST) so that you can recognise individual frames rather than it being a bit blurry from motion.

The way I have my GGPO configured gives me a perfect 60fps offline, but I got a lot of complaints about lag, despite the fact that it certainly wasn't anything to do with my laptop or the internet connection.  I asked someone on to share their config file and I found that while the framerate was not as good, I didn't get any complaints about lag.  I can only guess that maybe the Vsync was causing a conflict with the usual way the emulators sync when playing online.

My usual setting before getting the CRT was to use a bilinear filter at the panel's native 1920x1080 resolution and it looked really good, however the config file that I now use, uses a nearest neighbor filter which can look ugly when scaling by a non-whole number.  It was when I was using my CRT when this became apparent.  The default resolution that was set for the monitor when I plugged it in was 1024x768, so I thought, "Ok, I'll go with that".  When I put the game into fullscreen, I noticed the hatching effect on the Super and life meters wasn't consistent in size.

Left: 100%, Right 400%
The hatching pattern in the life and super bars are 1 pixel blocks
Above we have a 100% view of Super Turbo and to the right of it a 4x zoom to show the hatching pattern.  As you can see, the pattern is a grid of single red coloured pixels.  The problem with playing a game like Super Turbo at fullscreen using the basic renderer in GGPO is that it doesn't do any resampling of the image - it literally just multiplies the amount of pixels which means you get this inconsistent effect:

The observant of you will notice that the life bars in the first and second screenshots are different sizes.  The reason for this is because Super Turbo is a 4:3 aspect ratio game, but has a resolution of 384x224 which is closer to widescreen which needs to be stretched to 4:3 for the game to look correct.  This means that the first image was at it's native resolution/aspect ratio, but the second image was with the game running at 1024x768, which is a correct 4:3 aspect ratio.

If we go back to the first image, you can see that the hatching is nice and clean, and the large version is clean too since it's a simple 4x upscale.  The problem arises when you are trying to play a game like this which needs the aspect ratio correcting or that it's native resolution does not translate well to your upscaled resolution.

Since the game's resolution is neither 16:9 or 4:3, it means that in whichever situation you are in, the width and height are being scaled at different factors.  For example to take the 384x224 image from ST and display it on a 4:3 screen at 1024x768, the width is upscaled by 2.666x and the height by 3.429x.  For 1920x1080 that would be 5x for the width and 4.821x for the height, or if you were to run it at 1080p with the correct aspect ratio (1440x1080) it would mean the width gets stretched by 3.75x and height by 4.281x.

Because of how nearest neighbor works where it just increases the number of pixels by whole numbers rather than drawing any inbetween pixels, it causes a problem when being asked to upscale by a non-whole number.

The red blocks should all be the same size, but upscaling to a non-whole number causes this uneven effect since nearest neighbor only uses whole pixels
There are only 2 ways round this, and that is to either use a smarter upscaling algorithm like cubic or linear, or if you have a CRT, to set a custom resolution.

Solution: Custom Resolution
Because of how CRTs work in that they don't have a native resolution as such, it means you can send pretty much any resolution to it up to its supported maximum and it just displays it, without scaling.  If you are lucky, you may even be able to find monitors that will run arcade games at their native resolution but these are rare since a lot of PC monitors won't do this as the resolution and refresh rate is usually too low.  Some people prefer using a bit of upscaling in the emulator to avoid the scanline effect caused by low resolution games on CRT, but then again some people like scanlines and even buy additional hardware to emulate them!

This non-native resolution trait of CRT monitors is particularly useful for 2D/sprite based games which have these weird resolutions that are neither 16:9 or 4:3 since we can set the emulator to upscale the image by whole numbers to avoid the scaling artifacts caused by nearest neighbor.

For example upscaling 384x224 by 4x to 1536x896, the monitor will then display that resolution and fill the screen without additional upscaling.  Rather than being stuck at one resolution like an LCD at 1920x1080, a CRT is able to "adapt" it's resolution, and would be able to display something like 1920x768 should you wish.  It simply means the width would be the same, but it would change the vertical resolution to fill the screen.

If you were to send the upscaled 1536x896 game image to a 1080p LCD panel, it would then have to upscale it again in the monitor logic board to fill the screen (otherwise you would have big black borders where the outer pixels were not in use).  This is bad because generally any kind of LCD based upscaling creates input lag which is why you should always try to use a resolution that matches the number of pixels on your display.  In the case of a PC, it is preferable to use a non-integer upscaled resolution like 1920x1080 with a bit of ugly scaling than it would be to let the monitor handle the upscaling, which is usually not only ugly, but adds input delay.  Some consumer TVs have as much as 50ms input lag, which is around 3 frames.  That's simply unacceptable in a game where reversals must be performed with frame accuracy.

So bear in mind then, that the following guide mainly applies to CRT, but if you don't care about upscaling or incorrect aspect ratios, then these methods can also be applied to LCD.

Creating a Custom Resolution for GGPO
In order to find and create the correct custom resolution for you, you need to know:
1) The native resolution of the game
2) The maximum, native or recommended resolution for your monitor
This assumes that the game was intended for use on 4:3 screens.

In my case, the resolution of the game is 384x224 and the recommended resolution for my CRT is 1600x1200.  If we work back from the width for example, and calculate that 1600/384 is 4.166r, the resulting number you get is not a whole number so you will get the ugly scaling when using nearest neighbor.  In this intance we take the nearest whole number and multiply that by the width of the game, so 384x4=1536 which is our new upscaled width.  Doing the same for the height I get 1200/224=5.357, and if we take 5 as the scaling factor, 5x224 gives us 1120 pixels.

That gives us an integer scaled resolution of 1536x1120.  It's what I would describe as a bastard resolution as it doesn't fit any of the nice round resolutions that you usually find in your graphics driver like 1024x768, 1280x960 or 1600x1200, so we will have to deal with that later but first on to adding the resolution to GGPO.

Adding the Resolution to GGPO
1) First get to a window where you can browse your HDD (C: usually).  If you don't have an icon for "Computer" on your desktop, you can find it in the start menu.

2) Look for the GGPO config folder.  Where it is may depend on your Operating System, or where you put it but generally speaking you need to go into Program Files (x86)\GGPO\config

3) At this point make a copy/backup of ggpofba.ini as this is the settings file that we will be editing.  Once you have done that, open it by double clicking or in a text editor.

4) In the file you will notice the following chunk of text which are the custom resolutions.
// The preset resolutions appearing in the menu
VidPreset[0].nWidth 400
VidPreset[0].nHeight 300
VidPreset[1].nWidth 640
VidPreset[1].nHeight 480
VidPreset[2].nWidth 1024
VidPreset[2].nHeight 768
VidPreset[3].nWidth 1280
VidPreset[3].nHeight 960

In case you haven't figured it out already, we need to add another couple of lines to this with our own resolution which would look like this:
VidPreset[4].nWidth 1536
VidPreset[4].nHeight 1120

5) With that done, save and close ggpofba.ini and run the emulator.  Click Video>Fullscreen Resolution>Other and you should see your new resolution in there.  Select it, then press Alt and Enter to go fullscreen and try it out.

Do not worry if you get a message that the emulator couldn't set the resolution blah blah blah with a load of random numbers.  It generally means that your video driver doesn't see this as a valid resolution, which brings us on to the next part...

Adding the Custom Resolution to the Video Driver
This next part is written for adding the custom resolution to nvidia graphics cards.  If you have an ATI GPU or something else, you should google for advice.

1) On your desktop, right click and choose NVIDIA control panel, then under the Display tab select Change resolution and then Customize.  A popup with a list of standard resolutions will pop up, but we just want to click Create Custom Resolution.

2) Enter your resolution and the refresh rate (usually 60Hz).  I found keeping the timing set to auto works for me, but if you get tearing or judder you may be able to fix that with different timings.  The basic auto timings only allow you to specify an integer framerate, but most games were designed to run at 59.94Hz (the NTSC field rate) so if you can't get smooth scrolling motion, it may be worth playing around with custom timings where you can specify 59.94 fps.

It may also depend on the emulator as some may run at 60fps and others may run at 59.94fps.  In GGPO there is an option to force 60fps, and triple buffering/VSync also helps eliminate tearing but having said that, I think those last 3 settings were what was causing the apparent lag with GGPO before - so try to stick with the basic settings.

Once you've tested the resolution and it has passed, it is automatically saved.  You should now be able to run GGPO again at the new resolution.