• Projects
  • Godot Super Scaling, upscale or downscale your game for increased performance or visuals

Godot Super Scaling, a comprehensive post-processing effect for 3D and 2D Godot Engine games that allows you to upscale or downscale the viewport for increased performance or higher quality visuals. Supports up to 200% native resolution or down to 10%.

The code is all done and tested, just need to create the Github repo and submit to the AssetLib, which I can probably do tomorrow. This will be free and open-source MIT license. Note also that the video is a little choppy because I was recording and that ate a lot of performance. Without the video recording it is very smooth.

So I decided to just submit to the AssetLib tonight. Feels exciting. This is my first substantial open source contribution. Hopefully it will be approved soon, I think it may take a couple days. Thanks.

Congratulations, that's an important step, here's to many more!

@cybereality said: So I decided to just submit to the AssetLib tonight. Feels exciting. This is my first substantial open source contribution. Hopefully it will be approved soon, I think it may take a couple days. Thanks.

@Calinou might be inclined to take a look at it perhaps. ;)

Okay, I realize I had the settings wrong in OBS Studio, which was why it was so choppy. I just recorded a new video at full framerate, uploading now.

Here's a look at what Godot Super Scaling does with real game assets. The first shot is 1080p (but rendered with the shader) and the second is at 540p. While there is obvious pixelation and blurriness, I think it still looks quite good for 960 x 540 resolution upscaling.

Godot Super Scaling has been accepted on the Godot Asset Library. Feels great to see my work in the editor.

Call me old fashioned but I actually prefer the upscaled blurry look. Very crisp hi res textures always push things towards uncanny valley for me. I'd use this as a matter of aesthetic choice rather than for simply technical reasons.

Honestly, the downscaling part of it is not as important. The performance cost is very high (at 200% you basically get 25% performance of native) and it doesn't look 4 times as good. I wanted it more for the upscaling, which does increase performance and still looks okay. For example, the spinning cube scene on my RX 6800 XT can get around 1,400 fps at 1080p but 2,500 fps at 540p. So that is a big improvement. Obviously 50% is kind of low, but you could do say 80% with minimal loss to picture quality. And even if performance was the same, I think it looks way better than bilinear. It is a different unique look.

Looks really cool. I'll have to wait till I get a little farther in my game, though. Still working on the basics. Thanks for the contribution.

Nice. Yeah, it's definitely something to add once you are finishing and polishing your game. Thanks.

@cybereality said: Obviously 50% is kind of low, but you could do say 80% with minimal loss to picture quality. And even if performance was the same, I think it looks way better than bilinear. It is a different unique look.

Does your scaling variable allow for floating point values such as say 88.87%? Trying to get say 1600x900 out of these resolution scale systems can be a pain in my experience because many limit you only to integral values.

Yes, it is a floating point between 0.1 and 2.0. However, there is no need to find exact numbers, because you'll have no idea what resolution your users are playing at. It handles any arbitrary resolution.

Yeah, I mean if the variable gets exposed for players and some were to try and squeeze a more specific resolution out of it. I know I've tried to get 900p specifically out of some games and was bummed about the limit being integrals.

Okay, I see what you are saying. Yes, it is possible to expose a resolution number (like 1600x900) and then let the code convert that to a floating point (based on your native resolution). The thing is, the full-screen effect always runs at the native resolution of your monitor. So if you are playing on a 1080p screen, you can set the scale to any acceptable value, the resulting image is always 1080p. The scale just affects the quality of the render and how it is sampled. So I don't think there is any practical value to choosing specific resolutions, I was trying to get away from that because it is limiting.

or just expose the percentile slider and let the players do the conversions such as multiplying width and height of native and target resolutions to get the image megapixels then figure out the delta for the percentage value.

Well one thing that would be easy (and is already supported) is using a slider for the percentage scale, but then querying the actual resolution of the render target. This already works, and is shown in the video (the resolution number is dynamic, I didn't hard code it). So that would be easy to do, and on the game level. The add-on allows access to all that information.

Shaders are sorcery to me, but could this effect be applied based on the depth buffer such that distant objects are lower resolution (so performance gains for minimal visual impact)?

No but distant objects already take up less pixels so what you are thinking of likely already happens by default.

No that wouldn't work because I am not changing the actual rendering code. But as @Megalomaniak says, far objects take less pixels, and the depth buffer is non-linear, so this already happens in OpenGL normally.

But what you are actually asking about is called Variable Rate Shading, which is an Nvidia DX12 feature but AMD also has the same thing called Variable Shading.

https://gpuopen.com/fidelityfx-variable-shading/

This is much more advanced and currently requires DirectX12. Though I guess we could make an open-source version for Vulkan in Godot 4.0, though it would need to be part of the core rendering code, not an add-on.

Yeah variable rate shading would help with the calculation cost of shading for things in peripheral view and distance at the inverse cost in visual fidelity.

For Godot 4.0, it is FSR that they integrated (FidelityFX Super Resolution). Not Variable Shading. FidelityFX is the brand name of AMDs' open-source APIs, that include many different features, like upscaling, sharpening, this variable shading thing, ambient occlusion, a denoiser, and many other features. But they are all basically separate modules you need to integrate by themselves, not just one package. You can read more here: https://www.amd.com/en/technologies/radeon-software-fidelityfx

Also, my shader works in a similar way to FSR and shares a lot in common, though I am using a different algorithm. FSR is superior overall for sure, at least I think in terms of performance and quality at scales around 80%. But I did some tests, and it looks like my solution actually looks better at lower resolution, like 50% which is still acceptable with Godot Super Scaling but looks horrible with FSR. I would not go as far to say that my shader is better across the board, but depending on the situation it can look substantially better.

Hmm.. just tried to port my project to Godot 4.0 and it didn't go well. The pre-alpha is pretty rough.

That's good to know, @Calinou . I love FSR and it looks great in the games I've played (like Terminator and Godfall) but I watched a video of the FSR in Godot 4.0 and it didn't look that great. And when I tried myself I couldn't get it working (but to be fair, I tried to port an existing project, I will try again with a new project when I have time).

9 days later

Here is a deep dive into the technology behind Godot Super Scaling.

@cybereality This video was really informative and helped me actually get a grasp of what you've been up to. Knowing MSA and FXAA is cool but a real test of intelligence will be to see if you can figure out what my comment means. I hope I can use your tech later on a game! I also wish I could give you crap for saying Umm and uhh so much. (can't tho- too much respect)

LOL. Yeah, I spent a lot of time on this shader and I don't think most people really understood what it does. So I figured I'd do a video so people can easily understand. Most of the time I script my videos and write the text beforehand, but this was longer and unscripted. There was just too much I needed to do on the computer that there was no way I could write it all. So I just started recording and did it in basically one take. Didn't really know what I was going to say, so it's just natural when you make a 25 minute video with no plan.

Umm... well I do seem to say umm quite a lot. Here is the game: when you watch the video, every time I say "umm" you have to take a shot of tequila!

So I went through several revisions before I landed on this solution. The whole thing was done in about 2 weeks while I was still working on the demo.

Originally it was only super sampling anti-aliasing, which is still in there, but ended up being too blurry by itself and also tanked performance when increasing resolution scaling too high.

I experimented with Lanczos, and had something sort of working, but it didn't have the quality I wanted and would have needed a sharpening pass like FSR does, but the code just got too complex and I didn't like it.

I also considered just porting FSR, since that is open source, but I read through the code and it is extremely complicated and long. It also uses features that require Vulkan/DirectX12 and I'm working in OpenGL so it would have made porting difficult. So I abandoned that idea.

Finally I discovered a method called directional averaging, which is fairly straight-forward and not complex. The idea is that instead of sampling in a fixed manner (for example, just grabbing the 4 nearest pixels) you scan the image and look for patterns in the diagonals.

So here I am sampling in a sort of star/cross kind of pattern (9 taps) which computes the color difference of each pair of pixels, and then finds the most relevant 2 pixels to blend (based on the pixels that are most visible in color and also closest in color so they likely come from the same object).

This actually works well, but results in a somewhat pixelated image (though still much better than nearest neighbor or bilinear interpolation). So to fix this I add a super sampling pass, which runs at any resolution (it helps above or below native).

The super sampling takes 4 taps, but in a rotated box formation, so the pixels do not coincide, which gives better results than standard sampling (which may just pick the 4 closest pixels).

I have sliders so you can blend between the upscaling and the super sampling, which allows you to tweak how sharp or soft the image is, though in my experiments, just leaving it at 50% almost always gives the best result.

@cybereality said: Umm... well I do seem to say umm quite a lot. Here is the game: when you watch the video, every time I say "umm" you have to take a shot of tequila!

*dies of alcohol poisoning 2 minutes in Expert explanation of directional averaging! Fascinating stuff

12 days later

@cybereality I'm having trouble getting Super Scaling to work. I dropped the SuperScaling.tscn into my main scene and moved the whole game under a new spatial node (my game is 3d) When I open the SuperScaling inspector I dont see the Game World property. Any idea what I'm doing wrong? Am I supposed to do something with SuperScaling.gd and SuperScaling.tres? Thanks!

You have to attach the script SuperScaling.gd to the SuperScaling node in the scene tree. It should do this automatically, so I wonder why it is not working. How did you download/add the add-on to your project and what version of Godot are you using.

@cybereality To install I searched for Super Scaling in the asset library then downloaded the file. I drag and dropped the SuperScaling.tscn file into godot. After I read your response I attached the script and was able to connect SuperScaling to my spatial node. But now I'm getting the white screen of death.

Which I assume means theres a problem with the shader? Here are my current settings: Any ideas? My Godot is 3.3.2. Thank you again!

So Godot 3.3.2 should work, I just tested it. However, you should not be able to download via the AssetLib as the add-on is marked for Godot 3.4. So that is weird it allowed you to download. In any case, I would delete the SuperScaling node and the SuperScaling folder first, and then download the latest release from my Github and manually copy the folder into your project. https://github.com/cybereality/godot-super-scaling/releases/tag/v1.0.3