Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabling fullscreen via code shows window border at screen edges (DisplayServer regression) #63500

Open
Tracked by #37734
RedMser opened this issue Jul 26, 2022 · 43 comments

Comments

@RedMser
Copy link
Contributor

RedMser commented Jul 26, 2022


Bugsquad note: This issue has been confirmed several times already. No need to confirm it further.


Godot version

4.0.dev (835da44)

System information

Windows 10, Vulkan Clustered, 3 monitor setup

Issue description

When changing fullscreen mode via code, the window's border is visible at the edges of the screen. This does not happen when enabling the fullscreen project setting instead.

Launching the MRP with a high contrast theme (where window borders are white) with x10 zoom on the top left corner of my screen:
image

When using the default Windows 10 theme:
image

This confirms that you see the window's borders.

Steps to reproduce

Create a fresh project. Then either in _ready, or when pressing a key, run:

get_tree().root.mode = Window.MODE_FULLSCREEN
# This does not help:
#get_tree().root.borderless = true

Minimal reproduction project

win_fullscreen_border.zip

@Calinou Calinou changed the title Enabling fullscreen via code shows window border at screen edges Enabling fullscreen via code shows window border at screen edges (DisplayServer regression) Jul 26, 2022
@Calinou Calinou added this to the 4.0 milestone Jul 26, 2022
@Calinou Calinou moved this to To Assess in 4.x Priority Issues Jul 26, 2022
@Sch1nken
Copy link
Contributor

I just tested the example and for me on Win10 the Taskbar is also visible (window seems to be behind the Taskbar)

@TokisanGames
Copy link
Contributor

TokisanGames commented Aug 23, 2022

I've noticed this on my system w/ Windows 11, Vulkan, RTX3070 and GD4a14. Dual monitor set up (1920x1200, 1920x1080). It exists on all games, including the editor. It's not just via code. It is the window mode set by DisplayServer.

The title of this ticket should be changed to something like Border on Full Screen.

DisplayServer.WINDOW_MODE_FULLSCREEN results in a 1px wide border in games and the editor when in full screen mode (Shift+11).

DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN removes the border.

I can fix it for my game, but I don't see an option to change the editor to exclusive mode. Its hard to see in a screenshot, so I added black borders around so you can see the white border.

image

@steelywing
Copy link

And the border cannot trigger mouse click event

@vpellen
Copy link

vpellen commented Feb 4, 2023

For what it's worth, you can actually get around this and achieve "true" borderless fullscreen by setting the window mode to a standard window (i.e. not maximized or minimized, not borderless, not unresizable), changing its size to your screen resolution, and setting its position to the monitor position (which is going to be 0,0 if you have only one monitor). It'll take up the full screen without triggering exclusive mode. Sometimes it leaves behind the taskbar and you have to alt tab a couple of times, but I find that's an issue with windows on multiple games anyway.

I have a hunch that's just how windows handles borderless fullscreen - if a window has a position and size equal to a monitor, it'll just fullscreen on that monitor. Enabling settings such as windowless or non-resizable actually stops this fix from working, though I don't know how much of that is windows getting confused and how much of it is Godot trying to be clever with your settings and doing the wrong thing by mistake.

Incidentally, exclusive fullscreen introduces a delay on mouse movements, which is quite frustrating if you've got an object tracking the mouse.

@bruvzg
Copy link
Member

bruvzg commented Feb 6, 2023

The border is added deliberately, since it's the only reliable way to ensure non-exclusive mode on all hardware, and Godot editor (or any other Godot app that is using multiple windows) require non-exclusive mode to work.

For what it's worth, you can actually get around this and achieve "true" borderless fullscreen by setting the window mode to a standard window (i.e. not maximized or minimized, not borderless, not unresizable), changing its size to your screen resolution, and setting its position to the monitor position (which is going to be 0,0 if you have only one monitor). It'll take up the full screen without triggering exclusive mode.

No it won't, at least no on all GPUs (it is controlled by the driver), only the window client area size/position matter, so window with the border outside the screen will still trigger exclusive fullscreen.

There's MODE_FULLSCREEN_EXCLUSIVE which is equivalent to 3.x fullscreen (or creating borderless window and change it to match screen size), which will not have a border (but won't work with multiple windows).

@Calinou
Copy link
Member

Calinou commented Feb 6, 2023

Incidentally, exclusive fullscreen introduces a delay on mouse movements, which is quite frustrating if you've got an object tracking the mouse.

It should be the opposite – borderless fullscreen has more input lag, except on Windows 11 with Direct3D 11/12 apps after enabling windowed gaming optimizations (these are disabled by default).

@vpellen
Copy link

vpellen commented Feb 6, 2023

No it won't, at least no on all GPUs

Works On My Machine:tm:, but I'll take your word on that
Is there any reliable way to achieve borderless windowed mode on multiple platforms, then? I never see these thin borders on other games.

It should be the opposite – borderless fullscreen has more input lag, except on Windows 11 with Direct3D 11/12 apps after enabling windowed gaming optimizations.

After testing it just now, it actually seems to introduce a delay on all input, not just a mouse. I agree it shouldn't be that way, but I have no idea if it's a bug in Godot or an anomaly of my setup (running a second monitor connected to a laptop).

@bruvzg
Copy link
Member

bruvzg commented Feb 6, 2023

I have analyzed borderless full-screen in some games, and might find a combination of window style flags that is working, but it needs testing on various GPUs to make sure it is #72787

@vpellen
Copy link

vpellen commented Feb 6, 2023

Even a borderless mode that only has partial compatibility would be a great step up, honestly
Actually, thinking back on it, I think SFML and SDL had borderless fullscreen modes that worked fairly gracefully? Although it's been a while, so I might be misremembering

@a-johnston
Copy link
Contributor

#81553 was closed as a duplicate of this issue so just to recap some info from the other ticket -- 4.1.1 and onward do not support borderless fullscreen when set from project settings, versus this ticket which at least originally specifies the issue is when changing the mode through code. This was the result of #79016. This change references issues predating 4.1.stable but was cherry-picked for 4.1.1, and at least on my system borderless fullscreen worked with 4.1.stable.

@TokisanGames
Copy link
Contributor

TokisanGames commented Sep 15, 2023

I use DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN and have no border, nor delay on mouse input whether the cursor is visible or not.

I connect this to F11 to switch between windowed and full screen mode:

func toggle_fullscreen() -> void:
	if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN or \
		DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
		DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
		DisplayServer.window_set_size(Vector2(1280, 720))
	else:
		DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)

And my project settings:
image

@bruvzg
Copy link
Member

bruvzg commented Sep 21, 2023

That border makes this mode is useless for many developers, if this is not going to change I would vote for removal of this mode entirely, leaving only exclusive full-screen mode (Of course, I may be wrong and that mode is useful on other OS/platforms, so it just my thoughts).

I guess we can add a third mode instead (WS_OVERLAPPED + screen size), which will work as "borderless full-screen" on NVIDIA and Intel GPUs, and not guarantee multi-window support, like this:

  • MODE_MULTIWINDOW_FULLSCREEN - (WS_OVERLAPPED | WS_BORDER + screen size), with border, multi-window, used by editor.
  • MODE_FULLSCREEN - (WS_OVERLAPPED + screen size), w/o border, might be "borderless" or "exclusive" depending on GPU.
  • WINDOW_MODE_EXCLUSIVE_FULLSCREEN - (WS_POPUP + screen size), always "exclusive".

@TempoLabGames
Copy link

I made an issue a while back (#77596) trying to figure out exactly why Godot was introducing input lag. I got very confused when I started running into the same issue with other engines. I also eventually discovered that manually limiting the FPS and setting the vsync mode to mailbox achieved the best of both worlds, sometimes, occasionally, except sometimes things would fall out of sync and you'd get weird stuttering with skipped frames or some such.

@vpellen The cause will vary based on the implementation but ultimately the problem is frames being queued. Sometimes they are queued intentionally to guard against frame rate fluctuation, and sometimes they are queued unintentionally because of an inefficient implementation. But generally speaking, the input is being processed when you expect it to; you just need to wait for all the queued frames to pass before you can actually see it.

I've created a project you can use to visualise this delay: https://github.com/TempoLabGames/UnityRenderingLatencyDemo

The grey bands indicate where the mouse ought to sit if the renderer implementation is achieving optimal latency for the given settings. When you see tearing, you can move the mouse up and down the screen to get a better understanding of how the tear lines impact latency. Unity's Direct3D12 implementation achieves these optimal values on my machine (tested with 2021.3.x).

@dogboydog
Copy link
Contributor

dogboydog commented Sep 23, 2023

I'm here from the issue that was just linked to this one (sorry for the duplicate, searched but didn't see this) and a bit confused on how the new integer scaling feature is supposed to be used with this issue. Exclusive full screen was suggested, but that is Windows OS only according to the documentation.

So, if you make your game viewport resolution divisible by a common resolution like 1080p, there wouldn't really be a way to use integer scaling with full screen on any OS other than Windows, right? Because the window would always be 2 pixels smaller in both dimensions.

It also would mean regardless of integer scaling, if you want any kind of full screen on a non-Windows platform it would have a border like this which hurts the visuals of the game.

This mode exists specifically for the Godot editor (and similar non-game applications), with the intention to ensure support for multiple windows in all cases. It's not intended for games and should not be used in most cases.

Doesn't that mean there is no intended way to have a full screen game on Linux for example? If the only full screen option isn't intended to be used for games. That's quite surprising, but maybe I'm misunderstanding something.

@bruvzg
Copy link
Member

bruvzg commented Sep 23, 2023

Doesn't that mean there is no intended way to have a full screen game on Linux for example?

Exclusive Fullscreen works on all desktop platforms and is the intended mode.

The only Windows specific thing is the border, on Linux there's a flag to disallow disabling composition, and on macOS exclusive mode won't show dock when the mouse is on the edge of the screen.

@dogboydog
Copy link
Contributor

Oh good. Sorry then that I must have been looking at the wrong doc page or version before. Good news - thanks and sorry for misunderstanding

@bruvzg
Copy link
Member

bruvzg commented Sep 23, 2023

Docs are a bit out of sync (#82179), DisplayServer has more up-to-date description than Window.

@aitorciki
Copy link
Contributor

As someone totally new to Windows' window management, I wonder if other projects approaches could be of help?

An open source project that performs similar window management (minus the multi-window aspect, focusing on the gaming experience) that I use frequently on Windows is RetroArch. They allow windowed or exclusive fullscreen, and at least on my setup (Windows 11 + NVIDIA + Vulkan) the windowed variant doesn't result in the anoying mode switching discussed earlier (OpenGL is a different story though). They seem to achieve both fullscreen modes with WS_POPUP, so maybe worth checking if they're doing something that could be of use in this context?

@Nintorch
Copy link

I think this has been fixed with 4.2 beta
I just tested a small (private) 2D project of mine and an empty 2D project in both 4.1.1 and 4.2 beta versions and can confirm the white border only appears in 4.1.1

@Chaosus
Copy link
Member

Chaosus commented Oct 17, 2023

I think this has been fixed with 4.2 beta

Nope, nothing changed for me in my project.

@Nintorch
Copy link

Nintorch commented Oct 19, 2023

Nope, nothing changed for me in my project.

Sorry for late reply
May you provide a sample project in 4.2 that shows that the border is still present?

Edit: What's your OS version? Mine is Windows 11

@a-johnston
Copy link
Contributor

I also am still experiencing the issue in 4.2.beta1, and similarly saw the issue in each of the 4.2 dev releases (the latest version not affected afaik was 4.1.stable). Attached is a simple project with a purely 2d scene and a purely 3d scene. In both cases with "borderless" toggled on I see the 1px gray border in Fullscreen mode but not Windowed or Exclusive Fullscreen. This is on Windows 10 with Nvidia. I could believe that Windows 10 vs 11 affects this issue but it looks like others have also run into it with W11.

borderless_test.zip

@Nintorch
Copy link

After testing the project a-johnston sent in 4.2 (beta 1) I have some notes:

  • If you change the clear color and make the window fullscreen at runtime, then the border color will be the set clear color
    RenderingServer.set_default_clear_color(Color.PURPLE) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
  • If you first go fullscreen, then change the clear color, it doesn't change the border color, but if you set the mode to WINDOW_MODE_FULLSCREEN again then the border color is changed

@crashfort
Copy link

crashfort commented Oct 21, 2023

It is not fixed in 4.2 and is still an issue. The border being present also breaks the canvas items scale mode, since the border reduces the size of the window and the UI will become a blurry mess.

All that was changed in 4.2 was that the color of the border changes according to the clear color. Sorry, but seriously? Again, this is entirely a flaw in the Godot code, and yet again not any driver specific thing. The way to present graphics on Windows is to use DXGI. Stating that users can override the presentation behavior in the Nvidia control panel is not useful. Are you going to prompt in your game to tell all your users to go dig in the control panel to change a specific setting to play the game?

@filipworksdev
Copy link

filipworksdev commented Oct 28, 2023

Hi everyone.

I think @bruvzg's idea of WINDOW_BORDERLESS_FULLSCREEN could be useful! Removes the need to actually do that from script. I have tried borderless + fullscreen and does remove the 1pixel border but otherwise fps appears to be same as windowed.

The exclusive fullscreen caps the fps at 60 which makes me think Windows is taking over and forcing vsync on it. I'm on the lasted update Windows 10.

@valkyrienyanko
Copy link

I was getting this white border in 4.1.1-stable-mono but then I built PR #80566 [v4.2.dev.mono.custom_build [e4a4a40]] from source for mono and now I don't see it anymore.

@TheFoxKnocks
Copy link

Can confirm this still exists in 4.2, in fact in my case it didn't exist until I upgraded from 4.1 to 4.2. Thin grey border that I cannot change or get rid of.

@filipworksdev
Copy link

Can confirm this still exists in 4.2, in fact in my case it didn't exist until I upgraded from 4.1 to 4.2. Thin grey border that I cannot change or get rid of.

The only possible fix is maximize_window + borderless creating a "fake fullscreen". Works perfectly!

@FernandoWF
Copy link

The only possible fix is maximize_window + borderless creating a "fake fullscreen". Works perfectly!

This worked for me as well. Seems like a good enough workaround for now. Thanks, @filipworksdev!

@popcar2
Copy link

popcar2 commented Feb 16, 2024

I created a new MRP to track down the best way to get around this and what I discovered is that borderless windows might be more busted than I thought. I got WEIRD behavior but if you're here for the solution the TL;DR is that this works (yes, you need to set the window mode TWICE):

get_window().mode = Window.MODE_MAXIMIZED
get_window().borderless = true
get_window().mode = Window.MODE_MAXIMIZED
get_window().borderless = true

Okay, so here's my findings... I made a MRP where you can change the window mode with hotkeys. Windowed and exclusive windowed work as expected. Exclusive fullscreen has a size of (1920, 1080). Borderless fullscreen has a size of (1918, 1078) due to the 1px border around it.

Maximizing the window without a border makes NO sense however and it might be where everything breaks. My first thought was to do this:

get_window().borderless = true
get_window().mode = Window.MODE_MAXIMIZED

What this code actually does is just turn on borderless fullscreen mode. As in, get_window().mode is literally Window.MODE_FULLSCREEN and not maximized, so this might be what borderless fullscreen literally does under the hood. The window size is (1918, 1078) again. What about maximizing first, then setting borderless?

get_window().mode = Window.MODE_MAXIMIZED
get_window().borderless = true

The first time you run this it works as expected. It maximizes then turns borderless, not going over the taskbar. HOWEVER if you run this code again (ie maximize->borderless->maximize->borderless), the window size becomes (1922, 1082). There is no visible border but it might be getting rendered around the window, hence the extra two pixels on each axis.

Running that code a third time just turns it back into borderless fullscreen.

What's interesting is that (maximize->borderless->maximize) gives us a window size of (1920, 1080) but actually turns on exclusive fullscreen despite saying it's windowed. If you add another get_window().borderless = true after that does it add 2px to each axis, giving us a borderless windowed mode that has borders on the outside rather than inside which is probably as good as we're going to get. I have no idea why that is and maybe someone familiar with Godot source code can pick this up from here and figure out a solution.

Tested with Godot 4.2.1, Windows 11 23H2. I went insane writing this. Make it make sense.

borderless_test.zip

Edit 1: Actually, as pointed out by ManpreetSingh2004 in the other thread, maximize->borderless->maximize turns on exclusive fullscreen but reports it as windowed. Another bug. max->bord->max->bord does work but might get changed if that PR goes through, check out this comment for another workaround

@SirManfred
Copy link

I still have this issue in Godot 4.3 beta1 using Windows 10 Pro version 22H2

@gokiburikin
Copy link

gokiburikin commented Jul 2, 2024

Out of curiosity I set the rect of my game window through WinExplorer after setting borderless to true and it seems to work as intended (1920x1080, no pixel border, no exclusive fullscreen.) The only difference seems to be WS_POPUP being unset (false) when I use WinExplorer which prevents the unwanted behaviour.

Unfortunately Godot (probably with good reason) does not allow me to set WS_POPUP off and won't let me disable the flag anyway despite handling WinExplorer forcing that flag off just fine.

This may not work for every video driver or version of Windows, but it does work for me and Godot won't let me utilize this fix assuming it would work. My project doesn't need multi window support, so any borderless non-exclusive fullscreen option that is actually the intended window size would be appreciated.

This is mostly relevant from a game recording perspective. Right now if someone wants to record or stream the game in fullscreen these are the available options:

Option 1) Make the game window a very slightly different size using Windowed + Set Size. Now OBS game capture will capture a 1920x1081 source leading to either blurred capture (stretch), fit to screen capture (black bar on the right), off screen capture (missing pixel at the bottom) or the streamer has to notice and crop the window by 1 pixel vertically to get the correct aspect ratio (missing pixel at the bottom.)

Option 2) Use Fullscreen (not Exclusive) mode. OBS game capture is now 1918x1078 leading to blurriness because the aspect ratio is off. Due to a bug using Fullscreen with display capture leaves a 1 pixel transparent border around my game which is very unsightly. Streamers also aren't fans of using display capture in the first place for privacy reasons.

Option 3) Use Exclusive Fullscreen. For people who choose to play this way this is an acceptable workaround, but in my experience it's common for streamers to prefer borderless windowed to prevent issues with alt tabbing during streams.

Images of the blur as captured through OBS at 1920x1080 (Open in new tab to compare at full size)

1920x1081 Borderless Blur
gtb_blur_bless

1918x1078 fullscreen Fullscreen Blur
gtb_blur_fs

1920x1080 Exclusive Fullscreen No Blur (WinExplorer no WS_POPUP is the same result)
gtb_blur_efs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: To Assess
Development

Successfully merging a pull request may close this issue.