godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Add automatic hiDPI UI scaling project setting #7968

Open lostminds opened 11 months ago

lostminds commented 11 months ago

Describe the project you are working on

An editor app with multiple windows and popup dialogues

Describe the problem or limitation you are having in your project

We now have the Allow hiDPI project setting, which is great since it enables rendering game content at the monitor resolution on high density displays. However, for working with UI it just takes the first step, and doesn't add any automatic scaling to compensate for the monitor hiDPI scale. So basically, things will just be smaller on a hiDPI display, instead of retaining the same size and rendering the content with more pixels as the system usually does.

To compensate for this you need to get the screen scaling factor with DisplayServer.get_screen_scale() (Only on macOS so far https://github.com/godotengine/godot-proposals/issues/2661) or guess based on DisplayServer.get_screen_dpi(). Then apply this to Window.content_scale_factor and scale up the Window.size to match. This is cumbersome and prone to errors since you also need to take into account that the user might move a window to/from a hiDPI monitor and some things don't scale correctly (https://github.com/godotengine/godot/issues/59882#issuecomment-1744362885). And I've noticed that if you increase the content_scale_factor and size at while the window is on screen the window will still seem to render at the old lower resolution and just scale up the viewport resulting in larger but blurry content.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Add a new DPI project setting Automatic hiDPI scaling that requires Allow hiDPI and complements it by automatically applying the appropriate monitor hiDPI scale to all windows when appropriate.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

If this enhancement will not be used often, can it be worked around with a few lines of script?

It can be worked around as described above, but requires a bit of code in many places and it's hard to get right as you.

Is there a reason why this should be core and not an add-on in the asset library?

These days this is something that is integrated in at least macOS and Windows and as such I think it's expected that it should work "out of the box" in Godot projects.

lostminds commented 11 months ago

This would also make it easier to get UI controls like PopupMenu and AcceptDialog to scale and render correctly, which currently requires a lot of work and sometimes fails ( https://github.com/godotengine/godot/issues/71403, https://github.com/godotengine/godot/issues/54030 )

lostminds commented 10 months ago

After working some more with this I think part of the issue is that content_scale_factor feels like it's meant to be derived from window stretching (like the stretch modes) where the window size is already correct and you just adapt the content scale. This makes it a little cumbersome to use it the other way around to force a window to render at a higher resolution as just changing the content_scale_factor will just scale up the content and clip it in the same size window. You can then manually resize the window as well as described above, but in some cases like popups that automatically resize to wrap their controls this is not reliable. And if you do it at the wrong time the result will be scaled up, but rendered at a lower resolution.

Perhaps a more radical solution to this would be to introduce a new hidpi_scale_factor on Windows that scales both size and content, and is set automatically to the monitor scale factor of the window (and updated if this changes, dpi_changed signal already in place for this) if an "automatic hiDPI scaling" setting is enabled. This would leave content_scale_factor for use in stretching and GUI-scale settings to separate that from resolution scaling, and let the logic for setting and applying hidpi-scaling be separated from window size-dependent stretch-mode scaling.

Calinou commented 10 months ago

Perhaps a more radical solution to this would be to introduce a new hidpi_scale_factor on Windows that scales both size and content

This can be DisplayServer.screen_get_scale() if automatic scaling is implemented, as there shouldn't be a reason to change it at runtime from the project itself. If you want to provide a scale factor for accessibility reasons, content_scale_factor remains the best option. Window size should be changed by users themselves in this case, although minimum/maximum window sizes should scale with the screen scale if automatic scaling is enabled.

lostminds commented 10 months ago

This can be DisplayServer.screen_get_scale() if automatic scaling is implemented, as there shouldn't be a reason to change it at runtime from the project itself.

Yes, but having it as a settable property you could allow using the resolution scaling functionality even if you don't have it enabled to be automatic in the project.

Window size should be changed by users themselves in this case, although minimum/maximum window sizes should scale with the screen scale if automatic scaling is enabled.

I agree that the window size should not be changed in unexpected ways. However, the issue is that the current godot window implementation seems to set and return the window size in actual pixels, which counters the automatic hiDPI OS window scaling system that at least macOS uses. This is already in a sense changing the windows size in an unexpected way in my mind. I'm not sure how this works on windows or linux, but on macOS if you set and get an NSWindow frame, the values are always in "non scaled" nominal pixel values. So as a developer you just set the window to be 1000x500 and this will automatically be the same size on a 1080p or 4k retina display, same with controls in windows. The scaling to double resolution is handled automatically in the displayserver somewhere lower down. This also means that when changing monitor dpi resolution (like moving a window to a non-retina monitor) the window and all controls stay the same size. If I currently set a window in Godot to be 1000x500 on a macOS 2x retina display it must be detecting the resolution and scaling the window size down to 500x250 to get it to be 1000x500 pixels. When what it should do in my mind is set the window as 1000x500 in OS window units and instead increase the backing rendering size to 2000x1000 pixels and scale all 2d rendering to match this scale.