godot icon indicating copy to clipboard operation
godot copied to clipboard

[3.x] Add `max_render_size` ProjectSettings

Open BimDav opened this issue 8 months ago • 5 comments

The size at which a game is rendered plays a huge part in the game's performance. However, before this there was no way to set a maximum render size: setting the stretch mode to Viewportwould work to set the render size to the viewport size, but there are often cases where the viewport's internal size is large but the render size has to be smaller, e.g. a 4k game giving a high performance option for players who have a large screen size.

This adds a Project Setting "max_render_size" which is applied when the stretch mode is 2D. This will help the performance of a lot of games with high viewport size.

BimDav avatar Nov 29 '23 14:11 BimDav

Is this change not relevant for 4.x? If it is this should be implemented for 4.x first and can then be considered for cherry picking to 3.x.

AThousandShips avatar Nov 29 '23 14:11 AThousandShips

Is this change not relevant for 4.x?

It is, and I will make the PR for Godot 4. I had to make the fix for my game which is in production and made in a Godot 3 fork, so I did it in 3 first.

BimDav avatar Nov 29 '23 14:11 BimDav

I think the core issue is worth tackling, but I'm not sure about the way this is implemented for two reasons:

  • If no bilinear filtering is applied (nearest-neighbor is the default when using the viewport stretch mode), then the result will feature uneven pixel scaling unless max_render_size is exactly half the window size (or any other integer divisor).
  • It encourages hardcoding a maximum resolution, which is bad for future-proofing games. I've seen too many old games have hard caps on the resolution they can internally render at, and it still happens today sometimes. I don't want to be prevented from rendering a game internally at 8K if I want to[^1]. It may not be viable now, but it may be viable in 5 years from now :slightly_smiling_face:

[^1]: This doesn't require a 8K display to be useful, since you can use DSR/DLDSR/VSR to benefit from supersampling.

Instead, a resolution scale multiplier should be preferred. 4.x already exposes this with the Scaling 3D project settings, which only affects 3D rendering without affecting 2D. This already has a 3.x implementation in https://github.com/godotengine/godot/pull/74935.

If a separate resolution scale factor for 2D is really needed, it should be implemented as a multiplier of the base viewport resolution as is done for 3D in 4.x. This makes it more flexible, although it may also have to affect 3D at the same time depending on how it's implemented (just like this PR does).

Calinou avatar Nov 29 '23 14:11 Calinou

* If no bilinear filtering is applied (nearest-neighbor is the default when using the `viewport` stretch mode), then the result will feature uneven pixel scaling unless `max_render_size` is exactly half the window size (or any other integer divisor).

The maximum render size is only applied in the 2d stretch mode, so I'm not sure this is relevant? I may have missed your point.

* It encourages hardcoding a maximum resolution, which is bad for future-proofing games. I've seen too many old games have hard caps on the resolution they can internally render at, [and it still happens today sometimes](https://github.com/Lyall/MGSHDFix). I don't want to be prevented from rendering a game internally at 8K if I want to[1](#user-content-fn-1-e2de57130e6216d951f63ca554014a65). It may not be viable now, but it may be viable in 5 years from now 🙂

This is true. We could make it only accessible from code, and not from the project settings so that it is clearer it is intended for quality/performance options and not a hardcoded value. But it could make it too hidden to be found.

I don't really get how a scale multiplier brings a solution to your second point? I am open to a scale multiplier instead of a fixed resolution, but if the intention is to limit the render resolution below the screen resolution (it is at least my intention), then it will limit the resolution. The game dev will have to add a "high quality" option that sets max_render_size to (0, 0) in order to be future proof. That is how I used it in my game.

Thanks for the feedback!

BimDav avatar Nov 29 '23 15:11 BimDav

The maximum render size is only applied in the 2d stretch mode, so I'm not sure this is relevant? I may have missed your point.

My point is more generally that by default, Godot uses nearest-neighbor filtering when the viewport size doesn't match the window size. I believe this issue trickles down to your implementation as well, but I haven't tested it locally so far.

In 3.x, it is possible to switch to bilinear filtering for the root Viewport without using a secondary Viewport, but currently only when using GLES2 and this isn't exposed as a project setting.

I don't really get how a scale multiplier brings a solution to your second point? I am open to a scale multiplier instead of a fixed resolution, but if the intention is to limit the render resolution below the screen resolution (it is at least my intention), then it will limit the resolution. The game dev will have to add a "high quality" option that sets max_render_size to (0, 0) in order to be future proof. That is how I used it in my game.

For reference, I have a precedent with some desktop games using a fixed maximum 3D resolution by default (regardless of window size), which breaks expectations compared to every other game I've played.[^1] You need to go through their settings and figure out which setting actually unlocks rendering resolution, which is hardly an ideal experience for someone not knowledgeable with graphics settings. There's generally an assumption that increasing window size increases rendering resolution by default.

[^1]: Example in Serious Sam Fusion

There's also the concern that low-end mobile devices may need an even lower resolution to run smoothly either way. A multiplier handles this automatically, assuming low-end mobile devices have 720p displays, mid-range devices have 1080p displays and flagships have 1440p displays. On the contrary, if you set a fixed maximum resolution of 720p, the low-end devices will keep rendering at native resolution which is probably too much for them (480p would be more suited).

Most mobile games default to a resolution scale between 50% and 70%, with a slider available in the options menu (or a few button presets with associated FPS caps).

Either way, I would rather have the 3.x implementation follow what 4.x does as closely as possible as to make porting projects between 3.x and 4.x as seamless as possible. In particular, I want to avoid a situation where we only add a feature to 3.x and never implement it in 4.x. This can make porting some projects more difficult.

Sorry for coming out of the blue on this, but I think it's important we don't take decisions that trickle down to every Godot game and negatively impact them as a result :slightly_smiling_face:

PS: How are different aspect ratios handled? A multiplier handles this out of the box with no ambiguity, while this PR doesn't specify what happens if the window's aspect ratio differs from the max render size's aspect ratio (stretching, anamorphic rendering, or automatic compensation of the actual pixel count). To me, this is another reason to prefer exposing a multiplier.

Calinou avatar Nov 29 '23 19:11 Calinou