Last week we looked at the new controller menu for FEZ 1.12...

... so as I hinted at the end of that post, let's look at the rest of the menus.

Right now 1.11's game and video options menus look a little like this:

You're all familiar with this. Not a big list of options, is it?

How about this then:

There's a LOT to cover this week, so I'm just going to run through all of these step-by-step to explain what it took to actually get here. Despite what people think, these options can be anything but simple to implement, particularly the more advanced video settings. Making a UI element isn't the end of the road!

The good news is that a number of them were previously available as command line arguments, so options like Pause on Lost Focus were ready to go from the beginning. Even then, some of them required a little bit of work... Steamworks, for example, could be disabled with --disable-steamworks, but the option was designed to only work on startup, and could still fail given certain situations. In 1.12, the option still requires starting a new instance of the game to work, but the failing cases have been removed; if Steamworks fails in any way (including the Steamworks DLL not even being in the folder), we switch off the Steamworks functionality and your game will continue to work just fine. Offline mode is now as simple as just booting the game; the option is now simply there if you want to temporarily silence features like achievements and leaderboards while you're playing. Speaking of which, you'll know when Steamworks is disabled when the leaderboards/achievements are not selectable in the main menu.

The game option I really want to put some light on is the singlethreaded mode. Again, this was a command line option, but we've actually done quite a bit of work with the game's threading work, even when using multithreading.

In 1.11, the game makes a fair number of GL calls on separate threads that get funneled into the main thread by a really inefficient Queue of lambda functions, a system that's been around as early as the MonoGame-SDL2 days. This prevents actual threaded GL, which is crashy and terrible, from occuring, but the cost is worse memory performance in addition to loading times taking a whole lot longer since you may only be loading a single object per frame, rather than a large batch like you'd want. FEZ only calls the GL on a thread when loading assets, so it'd actually be safe to batch everything and continue loading without any drawing conflicts occurring in the game, but FNA can't assume that for every game.

The solution is that we actually disable the lambda system in the FNA build, as is allowed by the build options...

... and instead use a custom funneling system in the FEZ engine to defer GL loading calls. This allows the game to keep using threads without ever using the GL on them, and it's heaps faster too.

But, if I'm being honest, it's still not as fast as just turning on singlethreaded mode (as an aside, this is usually the case for every game out there; loading times are usually caused by the loading screens themselves rather than actual time spent loading assets). If you have a fast enough PC, the game won't even have load times in singlethreaded mode; the game will just transition from room to room instantly without any intermediate loading screens at all. I wanted singlethreaded mode to be on by default, but low-end machines that aren't fast enough would have experienced some really ugly stuttering during load times, so the compromise was to just have loading screens for all setups. If loading screens bother you as much as they bother me, try turning this off and see if room transitions remain nice and smooth.

But you don't care about threads or Steamworks, you care about fancy graphics options! So let's talk about those now.

Lighting was previously a command line option, so we just threw that into the menu. If you've got a low-end machine, see if this doesn't speed things up. It skips an entire rendering pass, so this is probably the easiest way to get some cycles back.

The resolution/screenmode/borderless options are what get interesting. For those of you on Linux or OSX this won't actually be new, but Windows users may be pleased to know that the window handling in 1.12 is much MUCH better than before.

1.11, being on MonoGame, was using OpenTK for window management, and it was just awful to work with even on Windows (Linux was unforgivably bad, hence the creation of MonoGame-SDL2). Now that we're on FNA, you now get the same windowing system that Linux and OSX had, as it uses SDL2 to deal with the window instead. Fullscreen in particular is infinitely safer; any and all positive integers for width/height are valid resolutions even in fullscreen mode. If you want to fake supersampled anti-aliasing, just set the width/height to, say, double your monitor's native resolution, and you'll get 2xSSAA! As a sample, here's a dump of a 10240x5760 faux-backbuffer taken with a modified FNA library:

However, this means that fullscreen mode will not be able to toggle borderless. This is because borderless will actually be on in fullscreen mode, as is the behavior of SDL_WINDOW_FULLSCREEN_DESKTOP, which FNA uses over traditional fullscreen:

So in reality, the borderless mode is now just for windowed modes. On Linux/Mac this isn't new (for OSX this is actually how we support desktop Spaces), but Windows users might wonder what that's about. Well, there it is. I've whined about fullscreen support in games before, if anyone wants to know why I insist on doing it like this:

But, this isn't all we've done with resolutions. Here's a screenshot of the game on my display at native resolution:

Notice anything different? Here's a hint: you'll only notice if you weren't playing in a 16:9 resolution.

So yes, this week I got rid of the damned letterboxing that plagued resolutions like the one I was playing in. This is something that's been on the TODO forever, but there was a fair amount of work involved in fixing the game to work with various aspect ratios. Renaud had at least done the work of avoiding pillarboxing in 1.11, but I reworked the aspect ratio calculation once again in 1.12 to remove the letterboxing as well.

In the old version, the view scale was calculated with the supported viewport heights (720, 1080, 1440, etc.). In the new version, the view scale is now calculated like this (pseudocode):

float aspectRatio = Width / Height;
if (aspectRatio > SixteenByNine)
    ViewScale = Height / (1280 * aspectRatio);
    ViewScale = Width / (720 * aspectRatio);

What we're essentially doing is taking the default viewport of 1280x720 and determining the scale at which the viewport-dependent elements will place/scale themselves correctly in the given dimensions. For example, 1920x1200's ViewScale would be ~1.042, since 16:10 is slightly taller than 16:9, but 3840x1080's ViewScale would be ~0.24, since it's much wider than 16:9. By calculating it this way, elements actually position/scale themselves correctly even when leaving the game's intended aspect ratio, both for wider and taller ratios.

There are two notable exceptions to this rule. The first turned out to be a number of UI elements where the vertical positioning was based on a set of constant values; since resolutions that use a vertical scale would now break away from these values, additional scaling has to be done on those values as well.

The second exception is the camera. Here's the problem (pseudocode):

cameraRadius /= ViewScale;

A simple line, but it's needed to keep Gomez the same size relative to the window height. Without this, you would see a wee bit too much of what's going on outside the intended viewport even at a reasonable aspect ratio (i.e. not UberWidescreen):

The result looks like this, in 1280x720 and 1280x800 respectively.

Note the location of the pillar on the right.

By scaling in this way, the FOV of taller resolutions, such as 1920x1200, would actually be lower than 1920x1080, because the camera fits itself to the vertical FOV, per the viewport's height that this scale previously referred to, and so the camera actually sits closer to Gomez here.

For whatever reason some games actually ship like this, even though 16:10 should logically just be 16:9 but taller, but as a 16:10 user I don't put up with that behavior. The fix for this is to just enforce a minimum horizontal FOV for taller aspect ratios (pseudocode):

cameraRadius /= ViewScale;
if (AspectRatio < SixteenByNine)
    cameraRadius *= SixteenByNine / AspectRatio;

So now 1280x800 looks correct:

This seems like a lot of nitpicking from a 16:10 user, but keep in mind that there still exist a number of 4:3 users out there, and there may even be aspect ratios that are vertically dominant! Without enforcing a minimum, a lot of the visual effects from things like rotation get totally cut off, and even for a faux-2D game this can be incredibly disorienting on top of simply not looking as nice (and could even make players sick, as low-FOV first-person shooters often do).

We were hoping to get away with doing all this without having to change anything in the assets, but sadly we had a few instances of what I call the Pee-Wee Bike Chain:

You know that opening scene with what you probably thought were infinitely tall pillars?


With all this newfound real estate, it'd be nice if we could keep the speed up, right? This is where features like hardware instancing come into play. On top of the numerous speed boosts we get simply by upgrading to FNA, we also get a lot of XNA4 features that weren't in MonoGame at the time (and for some platforms, still aren't available). Those of you familiar with the FEZ engine may remember that Renaud's FEZ GDC presentation had some slides about vfetch instancing:

Because vfetch instancing wasn't available on the PC, and because hardware instancing was not available in MonoGame, FEZ PC originally shipped with shader instancing, which is notably less efficient due to constantly streaming vertex/index data that isn't actually changing at all; only the instance data changes!

For 1.12 Renaud wrote a path that brought back hardware instancing on the PC, and it does bring a hefty performance improvement... to drivers that support it. This is one of the loftier OpenGL requirements for the game that we've brought in exchange for lowering the system requirements in the general case, and even then, some drivers for older hardware may not even support it properly. For example, in flibitQA we've actually got FEZ 1.12 running on a GeForce 6200 now, but the hardware instancing has to be disabled even when it claims to support the feature due to visual bugs. If you're on older hardware and find visual glitches, turn this off and see if that doesn't fix it.

Lastly, we've got anti-aliasing. FNA also supports multisample anti-aliasing, and it supports it without any arbitrary caps. If you want to try FEZ with 32xMSAA, you can totally do that! In fact, here's a 4k screenshot with 32xMSAA on:

This was actually a feature that we fought for a really long time, but we finally locked it in just a couple days ago.

The good news is that MSAA made its way into 1.12. The bad news is that it's not actually supported. This sounds really dumb, so allow me to explain. If you thought MSAA was just a switch you could flip on and forget, get ready for a fun graphics programming exercise:

Multisampling is done by allocating multisample renderbuffer storage; a target's permanent attributes include multisample count along with its width and height. You can't opt in/out; it's either always on or always off. This sounds okay, but for FEZ in particular it gets kind of messy. Consider the following screenshots, with MSAA off and on:

Notice that silhouette up above the lit area when MSAA is on? That's unintended bleeding! How did it get there?

People like to make fun of PC gamers for senselessly staring at textures from nanometers away, but this is the best way to show you what's going on. First, here's what the unlit scene looks like:

The way lighting works is with an additional render pass that produces a light buffer, which looks like this:

Combine the two and you get the final image. Here's a close-up of one of the mushrooms sitting on the platform above:

Got it? Here's the MSAA version, with the final result:

Oh. Ohhhhh. Oh. Shit.

A lot of the game's assets look gorgeous with MSAA on, especially in 3D, but this is where some people will be a bit divisive on the matter. Apparently the GL itself isn't too big a fan, as it's giving us this crap in the final output.

While some of these cases were solved simply by adjusting the blending state, some bleeding still occurs in a number of places. We considered various solutions, but they all have their drawbacks:

- Moving to 1-bit alpha for the light pass would eliminate semi-transparent bleed, but there may be cases where that transparency is accurate even for 0xMSAA.
- Having more aggressive tests in the fragment shader would introduce a similar situation.
- Splitting up the rendering into separate targets would allow us to be selective about what gets multisampling, but would add a LOT of overhead to the renderer and degrade performance even for 0xMSAA.

Eventually I decided to try other AA methods that could be done on a final image, and that's where FXAA came in. It actually looked better than lower-grade multisampling in a LOT of places, but it was just in first-person mode (and even then, it didn't want to smoothen out all of the edges...?). After comparing no AA, MSAA, and FXAA side-by-side for a whole work day, I finally took notice of how bad it was making the game look, and after a lengthy discussion we opted to take MSAA out of the menu by default. FXAA in particular really put a spotlight on how bad AA can be in games like this; it made some basic elements look incredibly bad:

Most AA methods blur out the corners of objects, so when your game has lots of corners in it, you're in trouble. And remember, this was just the stuff that technically worked. The bug I described above was one of many that we found, several of which we couldn't figure out before we finally just killed the idea altogether. It's a shame, but we were at least able to put some of FNA's MSAA support to good use, and it does still make first-person mode and the cutscenes look incredibly good. Here's what we did in the end:

- MSAA is used if the multiSampleCount config option is set to a valid value.
- The menu option is made available if the multiSampleOption config option is set to true.
- Passing the --msaa-option command line argument sets the above option to true for you, and permanently stays on until you toggle it manually in the config.

This leaves multisampling out for those who don't care, and those who would like to mess with it for screenshots and other such fun can still make use of the in-game option I put together without having to constantly reboot and edit the config file to get the optimal image and/or keep the game performant when you want one or the other.

As of writing this is probably about as far as we're going to go with the options, unless we stumble upon something that brings a noticeable improvement to the game. A fair amount of work went into the new features, and it looks like the rest of our time is going to be spent fixing some long-standing bugs as well as some regressions that testers have been looking at, so expect the menus to look pretty much like this when it ships unless we have to cut something, which can happen sometimes.

The careful reader may have noticed that I didn't talk about the VSync option today. Believe it or not, the VSync option was actually a task that was larger than everything I described today. Next week, we talk about timesteps and the largest unfixable bug in the FEZ engine... and how we went about fixing it.

This week's Further Reading is a lot of Shawn Hargreaves:

SetRenderTarget changes from XNA 1.0->2.0:

SetRenderTarget changes from XNA 3.1->4.0:

Multisampling in XNA:

Instancing in XNA 4.0:

Various undocumented things in XNA that I found while verifying RenderTarget accuracy in FNA:

(Keep in mind, I have to carefully find/parse all this to keep FNA accurate to XNA4...)
Shared publiclyView activity