hajimehoshi / ebiten

Ebitengine - A dead simple 2D game engine for Go
https://ebitengine.org
Apache License 2.0
10.41k stars 635 forks source link

monitor resolutions unexpectedly change when entering into fullscreen with HiDPI GNOME (3 or newer) and a franction scale #2225

Open tinne26 opened 1 year ago

tinne26 commented 1 year ago

I was testing a live usb with Ubuntu 22.04, which uses GNOME 42. After going to settings, display, enable fractional scaling and setting the scale to 125%, I noticed that game displays get too big and are shifted. I found this on Dr. Kobushi, but then also tested Bindless and found the same happening. DeviceScaleFactor is used. Unlike https://github.com/hajimehoshi/ebiten/issues/1307, I was using a single screen (a laptop).

Windows 11 seems to work properly instead under similar conditions.

hajimehoshi commented 1 year ago

Thanks. Does a similar thing happen with a regular GLFW application?

tinne26 commented 1 year ago

Sorry for the late reply. I've been digging a bit more, the summary would be:

The code that I've used to test this is available on this gist. I also recorded a video... really weird stuff going on:

strange_things_spinoff.webm

Since it's quite difficult to understand what's going on, here's the explanation:

Since it's now quite clear that this is not Ebitengine's fault, feel free to do whatever you want with the issue. I'm not super motivated to follow this up with GLFW and Ubuntu at the moment, but I'll let you know if I manage to make any more progress on this. I probably have to try other distros and desktop environments first.

(Note to self: maybe there's some way to mitigate the issue through software, for example using ebiten.ScreenSizeInFullscreen or similar as a hard limit under certain conditions or setups.)

tinne26 commented 1 year ago

So, I've been digging even deeper. I don't know why I said that it's clear that this is not Ebitengine's fault; it may actually be, because GLFW actually behaves correctly, despite the weirdness and taking its time to properly set the screen at the right size.

The summary is that GNOME and other desktop environments do some shenanigans to get fractional scaling working with xorg. Using Wayland works, but many apps have blurry text as they work on XWayland (compatibility mode for xorg), which is the main issue preventing wider Wayland adoption in my opinion. To summarize, when using integer scaling, the resolution of a 1920x1080 screen remains the same. When using fractional scaling, for example 125%, GNOME is not configuring the resolution as 2400x1350, but rather 3072x1728 (x1.6), and then indicates the apparently incorrect dpi of 200% to the application. If I manually override Xft.dpi in ~/.XResources and reload it with xrdg (X window server resource database utility), Ebitengine windows will be too small, because GNOME is still doing the correction internally and the resolution of the screen has been changed quite drastically (which critically doesn't happen with integer scaling, making things so much easier there). So, a massive mess.

Now, there's a method behind all this madness, and somehow GLFW or GNOME (it's not xlib, because I actually wrote some code with xlib too in C to test the behavior there and xlib kinda only cares about whatever xrandr resolution is currently set) are making things work when I fullscreen on GLFW.

So, the critical part is that period where even GLFW looks incorrect before readjusting (and this is what I don't know if it's done by GLFW itself or GNOME). What I suspect may happen is that once that readjusting happens, there's a callback invoked somewhere that leads Ebitengine to get confused again. So my question would be this: is there any callback on screen resize internally on Ebitengine that may explain these problems?

The most telling sign to me has been that when I modify Xft.dpi to 120, which is the value that GLFW reads and therefore Ebitengine reports, when I go into fullscreen, Ebitengine has the correct size for a second or two and then resizes again, like with GLFW, but instead of setting the correct size, now it still sets an incorrect and bigger fullscreen size. The surprising part is that both 192DPI and 120DPI lead Ebitengine fullscreen to be equally misproportioned, iirc. GLFW does fine with both values, because in the end it ends up obeying monitor resolution only. And Ebitengine also seems to do that if you follow the SetFullscreen code under GLFW, and that's why I suspect some callback may be involved in all this.

hajimehoshi commented 9 months ago

Hi, is this still an issue?

tinne26 commented 9 months ago

Yes. I just set up a live usb and tested again, and it's still broken.

The situation is basically that GLFW handles it properly and Ebitengine doesn't, because as you mentioned on https://github.com/hajimehoshi/ebiten/issues/1307#issuecomment-694393662, no one knows if Linux needs scaling or not. The answer is that it depends on the desktop environment. Since GNOME supports fractional scaling by saying that the screen resolution is much bigger than it actually is and then downscaling internally, this is kinda weird and it breaks everything.

In fact, I think the situation has become worse. First, after trying on v2.6.0 I hit this same crash https://github.com/hajimehoshi/ebiten/issues/2794 (although for a different reason, maybe the "fake" resolution causing the mouse to go out of bounds or something). Then I updated to the commit that fixed this, and the scaling issue still exists. In fact, in windowed mode the window went crazier than I've ever seen it go.

broken1

broken2

The resolutions from the pngs are accurate, from actual screenshots, so you can see how there's a 3072x1728 upscaling on windowed mode. For fullscreen, Ebitengine also believes that the resolution is 3072x1728, as that's what GNOME has configured and what xrandr probably reports, but then it's downscaled again to 1920x1024, leading to the complete disaster that you see in the second screenshot. Mouse coordinates also break badly.

Honestly, while this should be fixable in Ebitengine, it's all the result of madness with fractional scaling in Linux in general. Maybe GLFW uses the actual monitor size while in fullscreen or has extra checks to self-correct when things go wrong, I don't know. It's hard to determine what would be the best way to go here. GNOME itself may change the behavior in the future, and other desktop environments may do the same and we simply aren't aware of it. It's a massive mess.

hajimehoshi commented 9 months ago

CC @divVerent per https://github.com/hajimehoshi/ebiten/commit/5bc7b93e6fc657033c76bc374b8c7d72cd16f113

hajimehoshi commented 9 months ago

In fact, in windowed mode the window went crazier than I've ever seen it go.

Is the issue that only the bottom-left part is rendered?

hajimehoshi commented 9 months ago

I could not reproduce the issue with Cinnamon and examples/windowsize. The window could be enlarged correctly in the second display with 174%. Do we need GNOME to reproduce the issue, or a special code?

display

hajimehoshi commented 9 months ago

https://wiki.archlinux.org/title/HiDPI

In order to reproduce this, do we need to setup GNOME's scaling factor to 2? (Cinnamon doesn't have such a setting)

hajimehoshi commented 9 months ago

I think I could reproduce a similar issue:a fullscreen window is rendered with an almost half scale display Screenshot from 2023-09-30 13-12-56

EDIT: This was a bug on the main branch. Fixed.

hajimehoshi commented 9 months ago

I think I could reproduce @tinne26's case with GNOME + 200% scaling + a fractional scaling: the rendering result is unexpectedly cut and only the bottom-left part is rendered.

Screenshot from 2023-09-30 14-00-04

The temporal fix is always using GLFW's monitor size instead of XRandR size, but I am not sure when to use one and when to use the other. This is pretty confusing!

When fullscreening in this situation, the monitor blacked out once, so the entire content scale might change. I'll try to fix #2343 first, and then revisit this later.