godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.58k stars 20.46k forks source link

Implement support for seamless display scaling on Windows and Linux (for multi-monitor setups with different DPI) #56341

Open hhyyrylainen opened 2 years ago

hhyyrylainen commented 2 years ago

Godot version

v4.0.dev.custom_build.91b97dac0

System information

Linux (Fedora 35), Wayland, Vulkan API 1.2.189 - Using Vulkan Device #0: AMD - AMD RADV SIENNA_CICHLID (6900 XT)

Issue description

When moving a Godot editor window from my 4K screen to my smaller screen, the GUI scaling stays the same, meaning that all the GUI elements take up a much larger proportion of the Godot window than they probably should. At least I can see much less content in the file and node trees, also the placeholder text is even cut off on the search bar. Instead Godot should scale down the GUI when moved to a monitor with a different DPI / resolution.

Here's how the really large scaling looks in 4.0: Kuvakaappaus - 2021-12-30 10-13-58

And for reference here's 3.4 (showing a different project) where I can actually see much more useful content: Kuvakaappaus - 2021-12-30 10-19-13

Steps to reproduce

Minimal reproduction project

problem is with the Godot editor itself, a new project / any project can be opened to see more of the issue

Calinou commented 2 years ago

Run-time editor scale changes would need to be supported to allow for this, but this would require a lot of work. https://github.com/godotengine/godot/pull/52170 might make this easier, but it's still far from trivial. See also https://github.com/godotengine/godot-proposals/issues/6.

https://github.com/godotengine/godot/pull/40084 implemented seamless display scaling on macOS, but the same approach can't be used on Windows and Linux. Linux in particular has a longstanding tradition of issues with hiDPI, especially on multi-monitor setups with different DPI – some of those issues can only be fixed in Wayland. Not all hiDPI support is created equal… :slightly_smiling_face:

As a workaround, you can force a specific editor scale to be used in the Editor Settings (Interface > Editor > Display Scale). You most likely want to use 100% in your case.

alvinhochun commented 2 years ago

Thanks @Calinou for kindly pointing me to this issue from IRC, and because of that I thought I'd put my two cents here...

To clarify, on Windows the Godot editor already seamlessly scales to different displays (at least on Windows 10) because it declares itself as "system DPI aware". Godot is aware of and renders in the scaling of the primary monitor, but being "system DPI aware" means that it doesn't know about the different scaling on non-primary monitors. Windows being "helpful", it automatically bitmap-up/downscale the Godot window when moved to another monitor with a different scaling, making everything into a blurry mess. It's worse when you have monitors using a non-integer scaling, because then you can have cases like windows rendered at 175% being bitmap-scaled to 100%.

For this reason, when I use the editor I choose to override the per-application DPI setting (right-click properties from Explorer) to "application" to cause Windows to not apply the bitmap scaling, and then manually set the scaling I want in the Godot editor. I prefer the application to not seamlessly adjust the scaling if that means doing bitmap scaling of the whole window, especially when it comes to text-heavy applications.

(The seamless display scaling on macOS seems to be the same deal achieved using bitmap scaling, so it suffers from similar issues. The difference is that macOS only has native x1 and x2 and any other scales are always bitmap-scaled from x2.)


I agree that the Godot editor needs to support changing the UI scale during run-time for this. In the case of Windows, after this is supported, we can change Godot to use "per-monitor v2" DPI awareness (with "per-monitor aware" and then "system aware" as fallbacks) and use WM_DPICHANGED to trigger dynamic change of scaling.

There is this old PR https://github.com/godotengine/godot/pull/28771, I wonder if it's still the right approach to aim for? I kind of want to look into this for a bit, but since I'm new to the Godot codebase I'll need to spend some time to explore.

Calinou commented 2 years ago

There is this old PR #28771, I wonder if it's still the right approach to aim for? I kind of want to look into this for a bit, but since I'm new to the Godot codebase I'll need to spend some time to explore.

Now that https://github.com/godotengine/godot/pull/52170 was merged, we can use it for run-time scaling on the editor. The downside of using this new scale factor instead of the current editor scaling code is that it will not cause editor icons to be regenerated at higher/lower resolutions. Nonetheless, for run-time scale changes to support multiple monitors with different DPI, I believe it's good enough. We can use the monitor with the highest DPI as a basis for the editor scale, and scale down on other monitors based on that (to ensure icons remain fairly crisp on all monitors).

For instance, if you have 3 monitors with the following automatically determined scale factors[^1]:

The editor scale will be set to 200% automatically. The run-time editor scale is set to the following value on each monitor:

[^1]: Godot does not support reading the OS-provided scale factor yet: https://github.com/godotengine/godot-proposals/issues/2661

alvinhochun commented 2 years ago

Now that #52170 was merged, we can use it for run-time scaling on the editor. The downside of using this new scale factor instead of the current editor scaling code is that it will not cause editor icons to be regenerated at higher/lower resolutions. Nonetheless, for run-time scale changes to support multiple monitors with different DPI, I believe it's good enough. We can use the monitor with the highest DPI as a basis for the editor scale, and scale down on other monitors based on that (to ensure icons remain fairly crisp on all monitors).

I'm not sure about this. The reason I want to look into implementing per-monitor scaling is to get crisp text and UI rendering in the editor seamlessly. I fear that this approach may end up with a lot of things being blurry, which will only be marginally better than the current situation (at least on Windows).

Calinou commented 2 years ago

I'm not sure about this. The reason I want to look into implementing per-monitor scaling is to get crisp text and UI rendering in the editor seamlessly. I fear that this approach may end up with a lot of things being blurry, which will only be marginally better than the current situation (at least on Windows).

The 2D scale factor I mentioned does keep things crisp – it's not just bitmap scaling of the entire window. Fonts will be re-rendered to match the final 2D scale, so they will remain crisp.

alvinhochun commented 2 years ago

How can I go about testing the 2D scale factor with a hardcoded scale? I tried adding:

diff --git a/main/main.cpp b/main/main.cpp
index c9a846155b..da12139f57 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1998,6 +1998,7 @@ bool Main::start() {
            ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect", PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"));
            GLOBAL_DEF("display/window/stretch/shrink", 1.0);
            ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::REAL, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "0.1,8,0.01,or_greater"));
+           sml->set_screen_stretch(SceneTree::STRETCH_MODE_DISABLED, SceneTree::STRETCH_ASPECT_IGNORE, Size2i(0, 0), 1.5);
            sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true));
            sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true));
            GLOBAL_DEF("gui/common/snap_controls_to_pixels", true);

on the 3.x branch, but the text are blurry (I assume the bitmaps being blurry is normal because editor scale is 100%). 圖片

Calinou commented 1 year ago

on the 3.x branch, but the text are blurry (I assume the bitmaps being blurry is normal because editor scale is 100%).

The editor or project manager doesn't have font oversampling enabled, as it didn't make use of font oversampling before your change. To resolve this, call sml->set_use_font_oversampling(true); just below the line you added. (This can be done in both master and 3.x.)

If we make use of built-in scaling options, font oversampling should always be enabled for the project manager and editor. The use of MSDF fonts could also be considered (with an opt-in editor setting).

alvinhochun commented 10 months ago

Disregarding the editor for now, I wonder if Godot can just add an extra option to enable per-monitor DPI by changing this call to pass PROCESS_PER_MONITOR_DPI_AWARE instead? https://github.com/godotengine/godot/blob/f82bf35a03502b33e0b3a5987573e3c6987912ce/platform/windows/display_server_windows.cpp#L4603-L4615

Games don't really need to handle WM_DPICHANGED when a lot of them just scale the viewport without needing to care about the DPI. The current mode of PROCESS_SYSTEM_DPI_AWARE really messes up the scaling on secondary monitors, especially for pixel games. Seeing that Godot 4.2 adds an integer scaling option, it's quite a bummer for it to be ruined by the system DPI scaling.

Though now is probably too late to add such an option to Godot 4.2 :(

Calinou commented 9 months ago

Though now is probably too late to add such an option to Godot 4.2 :(

Now that 4.2 is released, feel free to open a PR against master so we can look into this for 4.3 :slightly_smiling_face:

Remember that we still aim Godot to be able to run on Windows 7-8.1 (on a best-effort basis), so compatibility must be checked first before attempting to use APIs only present on Windows 10/11.