godotengine / godot

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

Unable to change fullscreen resolution? #14542

Closed TheSHEEEP closed 4 years ago

TheSHEEEP commented 6 years ago

Operating system or device, Godot version, GPU Model and driver (if graphics related): Windows 64bit & NVIDIA GeForce GTX 970 Linux 64bit & NVIDIA GeForce GTX 1060 Godot 2.1.4

Issue description: This is the bug report belonging to this question. Since I had no answers or comments in more than three days, I assume I have hit a bug? Or a missing feature? Either way, I am logging this as a bug - though please correct me if I'm just doing this wrong or this is actually a missing feature.

Steps to reproduce: Set your application to fullscreen via OS.set_window_fullscreen(true). Then, change the size of the window via OS.set_window_size(). It will mess up the rendering on Windows and on Linux it will mess up the rendering for one frame before returning the resolution to the previous one.

Link to minimal example project: Just use the Window Management demo (best on Windows, but also "messes up" in Linux for me). Activate fullscreen, then click resize. You'll see. If you don't and it happens only for me, I can provide screenshots.

MrJustreborn commented 6 years ago

Duplicate of #14446 ?

akien-mga commented 6 years ago

Duplicate of #14446 ?

Doesn't sound like it, no.

TheSHEEEP commented 6 years ago

This one comment on #14446 does sound important, though: "Godot currently does not support non-native fullscreen resolutions on any platforms."

So I guess my problem is just the result of this "does not support", so this is actually a feature request?

RayKoopa commented 6 years ago

Yeah, and I wish it would be supported some time soonℒ️

TheSHEEEP commented 6 years ago

Agreed. A game released on PC that does not offer a resolution selection in fullscreen mode will earn a lot of weird looks at the fact ;)

TheSHEEEP commented 6 years ago

As I just found out, you also cannot switch the current screen while in true fullscreen mode. You have to work around it by going into a different mode (windowed "or" borderless fullscreen) first, change target monitor, then apply fullscreen again. Should I open a new issue for this?

Calinou commented 6 years ago

@TheSHEEEP Yes, multi-monitor support is a separate issue from non-native fullscreen, so a new issue should be opened for that.

reduz commented 6 years ago

I think there is a misunderstanding here, this is not a bug, probably should be fixed with documentation.

Godot does not understand fullscreen video modes. It is kind of silly to support that nowadays, given that monitors all have fixed pixel densities.

The right way to do what you want to do is using viewport resizing and just change viewport size while keeping the regular fullscreen mode.

TheSHEEEP commented 6 years ago

That would lead to strange windowing programming, though. If the user wants to have his application in windowed mode, you'll have to set the actual window resolution. If the user wants to have his window in fullscreen (either borderless windowed or "true" fullscreen), you'll have to adjust the viewport size instead. Or do you have to always treat the viewport and window size as two completely different things that will not affect each other directly and change both in the case of windowed mode?

What you say makes sense, of course, but what I'm used to from other engines is to have only one setting for this. When I last used Ogre or Urho3D, for example, you could set the resolution to 640x480 and fullscreen to true and that would do exactly what you want - give you a fullscreen window at 640x480 resolution.

I'm not sure splitting these into window size and viewport size is really helping anyone.

Either way, as long as resolution change at fullscreen is possible - and leading to increased/decreased performance, as it should - how exactly that is done is a minor usability concern.

RayKoopa commented 6 years ago

You forget that mouse movement also feels different in a lower resolution than it would in upscaled games. This may sound neglectible and "fix your game", but my specific use case was to rebuild a very old game which runs natively at 640x480 as exactly as possible. I can simulate it by scaling the mouse coordinates from 640x480 to the desktop resolution though, I guess.

reduz commented 6 years ago

@TheSHEEEP Yes, you can have completely independent viewport size than Window size in Godot. There is a very flexible API for this in Viewport.

I can understand that some engines may still support this, but it's really pointless nowadays. Screen pixels and resolutions are nowadays completely fixed. Dealing with fullscreen modes is also a hassle because you have to obtain a list of the supportes ones so, if your game is not designed to handle this you have to work it around. Using the Viewport API, all this is handled transparently and nicely for you.

lukaskotik commented 6 years ago

Almost every (not only AAA) desktop game offers possibility to somehow change fullscreen resolution for performance-wise reasons. So at least good explanation in Docs or detailed tutorial on this topic could be very helpful.

TheSHEEEP commented 6 years ago

I'd also add a log message of some kind if the window resolution is changed when in fullscreen mode. I'm fairly sure I'm not the only one who was surprised by it not working - knowing that you instead have to change the viewport size isn't really self-explanatory.

bengtsts commented 6 years ago

After some testing on X11 and Android builds this is what I found: OS.set_window_size() is for setting the size of the window not the resolution of the rendered image in the window. For use when not in full screen. Setting the window size lower than the view port size appears to cause artifacts. Either set a minimal window size to match your view port or update your view port to fit within the window.

To solve the problem in the original post I used the following:

extends Node

# My fps output
#onready var fps = get_node("UI/FPS")

func _ready():
    # Wait for window resize event before changing resolution upon going full screen or
    # resolution update won't work as expected with a single button press
    get_viewport().connect("size_changed", self, "on_window_resize")
    set_process(true)

func _process(delta):
    # I Use OS.get_frames_per_second() on a Label to watch for performance changes
    #fps.set_text(str(OS.get_frames_per_second()))
    pass

func _on_Button_pressed():
    OS.set_window_fullscreen(true)
    # On Android setting full screen isn't useful thus window resizing events aren't
    # called so get_viewport().set_rect() should be called here

func on_window_resize():
    # Change resolution to 640x360 pixels
    get_viewport().set_rect(Rect2(0, 0, 640, 360))

Special note for full resolution of full screen windows. Godot 2.1 doesn't use my full screen resolution when in full screen mode on my PC and phone. My 1920x1080 PC monitor and 1280x720 phone both render at 1066x600 in the view port. This is inconsistent with windowed mode where the default resolution matches with window size. It is however easily fixed with:

func on_window_resize():
    var screen_size = OS.get_window_size()
    get_viewport().set_rect(Rect2(Vector2(0, 0), screen_size))

I personally like having control over view port size independent of window size. Its useful for making low res pixel games where a high res view port on a large window serves no purpose except to lower performance and drain batteries -- not accounting for style. I hope this feature doesn't go away even if simpler commands are implemented.

Hopefully this is helpful to someone.

sporeservant commented 4 years ago

For anyone viewing this in the future, replace set_rect with set_attach_to_screen_rect.

I noticed this was necessary when I had a nested viewport w/ an additional camera (2D) - even when you select Full Screen startup, a resize on the primary viewport is occurring. However, the sub viewport was not resizing, and had to be updated manually. Failure to do so resulted in all sorts of incorrect local mouse coordinates, etc.

func on_window_resize():
    print("SCREEN RESIZED")
    var screen_size = OS.get_window_size()
    var screen_rect = Rect2(Vector2(0, 0), screen_size)
        # set primary viewport - it isn't clear to me if this is actually necessary
    get_viewport().set_attach_to_screen_rect(screen_rect)
         # set child viewport - this is necessary
    nested_viewport_container.get_viewport().set_attach_to_screen_rect(screen_rect)
Calinou commented 4 years ago

By the way, there is now a demo showcasing the use of a secondary viewport for 3D render scaling independently of 2D elements: https://github.com/godotengine/godot-demo-projects/tree/master/viewport/3d_scaling

Feel free to open pull requests to add a 2D example scene or support for mouse coordinate conversion.

Tobi-La commented 4 years ago

I'm still struggling to change the fullscreen resolution. For windowed mode it works fine with OS.set_window_size(resolution). When fullscreen is active I tried get_viewport().set_rect(resolution), which says that the set_rect method doesn't exist. When using get_viewport().set_attach_to_screen_rect(resolution) like @sporeservant suggested, the game rendered at the lower resolution I selected, but not scaled up. I have instead some weird artifacts on the part that should be covered by the scaled-up viewport. grafik

Calinou commented 4 years ago

@Tobi-La Try using the Viewport set_stretch_override methods instead (I don't remember the exact name). Or was it in SceneTree?

Tobi-La commented 4 years ago

@Calinou Thanks. I tried set_size_override of viewport, but it scales down my ui and repositions it. Just like if my games base resolution was turned up. Activating set_size_override_stretch didn't change anything.

Calinou commented 4 years ago

@Tobi-La Are you using two Viewport nodes (one for the 3D viewport, one for the 2D nodes)? Note that you only need to create one Viewport node, as there's already an implicit root Viewport.

You need to do this to scale the 2D and 3D views separately.

Tobi-La commented 4 years ago

@Calinou I have not created a separated viewport, so I am only using the implicit root one. I am accessing it by calling get_viewport(). It would be totally okay if 2d and 3d are scaled the same. I only want to offer the user a way to configure the resolution while in fullscreen and try to stay as close as possible to what the players are used to by other games. I got from this thread that the true fullscreen resolution is not changeable in Godot (in which case the monitor would do the upscaling) but Godot can handle the upscaling itself. That's what I'm trying to do.

lukaskotik commented 4 years ago

I would strongly advise against using only one viewport and enable users to change rendering resolution. Godot now only uses nearest neigbour upscaling and the result is really rough if you do not make any postprocessing.

My experience is that you now cannot get close to experience gamers are used to. The best result you get when follow the 3d scaling demo mentioned by Calinou. This aproach is far from straightforward and quite unintutive, at least for me. Anyway, you have to render viewport in texturerect and set the filtering on to antialias the upscaled viewport (but it still looks worse than you are probably used to from games made in Unreal or Unity). I am not sure if you also have to pass mouse input events with transformed position or if this has been changed.

As far as I know Unreal render controls to independently to viewport resolution, hence if you want to be close to experience of gamers I would recommend to render controls in viewport in screen resolution.

lukaskotik commented 4 years ago

If I remmeber correctly I am using viewport.size = resolution when in fullscreen and not set_rect(). In project setting I have stretch mode to 2d and aspect to expand.

Calinou commented 4 years ago

Anyway, you have to render viewport in texturerect and set the filtering on to antialias the upscaled viewport (but it still looks worse than you are probably used to from games made in Unreal or Unity)

This is not antialiasing, but linear upscaling. Do Unreal or Unity offer better (but more expensive filters) like bicubic or Lanczos? This could explain their upscaling looking slightly better.

Antialiasing is what you get when enabling MSAA or FXAA (which you can still do with a downsampled viewport).

lukaskotik commented 4 years ago

OK. Bad terminology from side, sorry (and yes, I in my project MSAA I usually prefer MSAA >= 4x regardless viewport size). Call it whatever we want, e.g. filtering or smoothing after upscaling the viewport. Any game made in Unreal I have played looked significantly better than viewport in texturerect with filtering turn on. There are at least some serious 3D games or project made in Godot and I am very curious how they deal with it (e.g. Interpid).

Maybe bicubic can work good? Most likely simple to implement and not too demanding.

Not sure if this the right link: Unreal upscaling, but as far as (little) I know you can choose your method. Godot is 0 or 1 and my estimate is that majority of realeased games in Unreal are >= 3. Default is something called "5-tap Catmull-Rom bicubic, approximating Lanczos 2 ". Maybe it can be good idea to allow user choose the quality of upscaling in Godot 4? There is probably no point in reinventing the wheel, so why not implement the same algorithm as Unreal.

Calinou commented 4 years ago

@lukaskotik Keep in mind those upscaling algorithms can be significantly more expensive compared to linear filtering, which means you need to decrease the rendering scale slightly to get the same FPS. You need to factor this in your comparisons :slightly_smiling_face:

UE4 also has the ability to use temporal upsampling, which we don't use since Godot doesn't use deferred rendering or TAA techniques. This is a design decision to make the renderer simpler and shaders easier to write, as users don't have to specify motion vectors in their vertex shaders.

lukaskotik commented 4 years ago

What I keep in my mind is probably very insignificant since I am not programmer, just mathematician (the worst case - in the field of applied mathematics). I am just an user of Godot engine.

But I think that we can be very sure that Unreal developers keep it mind and thus their choice of default algorithm is a result of extensive research.

Tobi-La commented 4 years ago

@lukaskotik @Calinou

I tried to implement @lukaskotik advice and moved all my 3d scenes into a separate viewport. I then gave this viewport the same size as my base resolution. I added a viewport texture to my scene and made sure that the separate 3d viewport will stay the same as the root viewport by using this code:

func _ready():
    _root_viewport_size_changed()
    # Required to change the 3D viewport's size when the window is resized.
    # warning-ignore:return_value_discarded
    get_viewport().connect("size_changed", self, "_root_viewport_size_changed")

func _root_viewport_size_changed():
    viewport.size = get_viewport().size

viewport is my 3d viewport. So I have no scaling implemented yet, just trying to get the 3d viewport working for now.

It seems to work fine for resolutions higher than my project's base resolution. But the 3d viewport doesn't fill the window for lower resolutions: https://drive.google.com/file/d/13arpk36i7NFC2KkVSKSJfIwJVPM6SWly/view?usp=sharing

Probably because I have set the scaling mode to 2d and the stretch mode to expand. I could activate scaling in the viewport texture, but that breaks a lot of other things like functions Camera.unproject_position(), which I was not able to get a correct mapping for to still work.

What would be the best solution to get this working?

Calinou commented 4 years ago

I could activate scaling in the viewport texture, but that breaks a lot of other things like functions Camera.unproject_position() , which I was not able to get a correct mapping for to still work.

I suppose you'll have to calculate the scaling factor manually based on the "expand" formula, and use it to multiply/divide the projected coordinates. Godot doesn't seem to expose this scaling factor right now.

Tobi-La commented 4 years ago

@Calinou Thanks, trying to adapt that. Could you tell me what the difference between size and content_scale_size in the code is?

Tobi-La commented 4 years ago

Thanks, @Calinou . I think I managed to get my scale factor by using this formula:

    var ratioFactorOrig = 1280.0/720
    var currentRatio = float(viewportSize.x) / viewportSize.y

    if currentRatio > ratioFactorOrig:
        internalScale = 720.0 / viewportSize.y 
    else:
        internalScale = 1280.0 / viewportSize.x

1280 x 720 is my base resolution

However, I noticed that the performance is worse than before, my fps drops noticeably in comparison to using only the root viewport. Is this expected? I could do some benchmarks if this helps.

Calinou commented 4 years ago

However, I noticed that the performance is worse than before, my fps drops noticeably in comparison to using only the root viewport. Is this expected? I could do some benchmarks if this helps.

Yes, using two viewports will unfortunately be slower than only using one viewport, not to mention the additional cost required by the TextureRect displaying the ViewportTexture. I'm not sure if anything can be done about this.

The cheapest way to render a viewport is to attach it directly to the screen, but this prevents screen-reading shaders from working and is only available in GLES2. (This is what games in the 90s/2000s did before the norm was to split the HUD from 3D world rendering.)

@clayjohn Is it possible to use multiple viewports with set_attach_to_screen_rect?

clayjohn commented 4 years ago

@Calinou yep. You can use as many as you like. You will have to disable the root Viewport though as it draws last and will cover the entire screen.

Tobi-La commented 4 years ago

Thanks again @Calinou I have noticed two more issues though:

  1. Particle effects in my 3d viewport are not rendered on GLES2 (works fine on GLES3).
  2. I have weird looking artefacts on lower resolution scales on mobile.

100% res: Screenshot_20200531-174503_2 BER Bausimulator 50% res: Screenshot_20200531-174456_2 BER Bausimulator

Any idea where that could be coming from?

Calinou commented 4 years ago

Particle effects in my 3d viewport are not rendered on GLES2 (works fine on GLES3).

Are these Particles or CPUParticles nodes? Only CPUParticles are supposed when using GLES2.

Tobi-La commented 4 years ago

Yes, of course. I just moved the nodes to the new viewport, everything worked fine before when I only had the root viewport.

vitorbalbio commented 4 years ago

Hi! First i want to say that deliver a 3D Game for PC without fullscreen resolution setting is unconceivable nowadays. The current "oficial" workarounds using viewports are overcomplicated and far to be good. Also they impact your game architecture and workflow. (https://github.com/godotengine/godot/issues/20619, https://github.com/godotengine/godot-proposals/issues/1465)

A proper easy and build-in solution for it can be found in all other general purpose Game Engines as a simple setting and it seems wrongly ignored in Godot. But everything is not lost if you use C# in Windows.

One easy solution that anyone can implement in your game is just change the user display resolution using the OS Calls. It can be done with C# and the user32.dll and probably also with GDNative (But i know too little about GDNative to make sure) You can find a lot of tutorials in internet about it like: https://www.codeproject.com/Articles/6810/Dynamic-Screen-Resolution https://www.codeproject.com/Articles/36664/Changing-Display-Settings-Programmatically

That way you can make a game that just works and it will run in the native user display that you can set by code.

Notice though that it's very dangerous if done wrongly since you're handling the user system settings! If you broke something probably the user will need to recover the system to make their display work again or if they have the luck to have another monitor (as i did) they will need to remove some register keys. https://answers.microsoft.com/en-us/windows/forum/windows_10-hardware/windows-10-reset-external-monitors-settings/b3a53cef-e54f-4410-b09e-6846fa297a3f

You need to absolutely guarantee that in any case you will set the monitor settings to the original setup if not in game. If something done wrong during the set of some resolution it also need to automatically get back to some safe config.

Also even if the user do ALT+TAB or rage quit with Alt + F4 you need to guarantee that everything get back to normal.

For all this Godot is gorgeous since it provide notifications when the application loses focus or quit an by what i see it works nicelly. https://docs.godotengine.org/pt_BR/stable/getting_started/workflow/best_practices/godot_notifications.html

I did implemented this solution in my game and by now it's working as should (after i did break my system some times testing). In future i want to check about Exclusive Mode too. But it's off Topic.

I would like to see this handled in a more streamlined and integrated way inside the engine but for now it's the best option i found. So if you're struggling on it also consider this solution.

Calinou commented 4 years ago

The modern way to handle screen resolution changes is fully supported in Godot, and there's a demo that showcases it. If you look around, it's what a lot of AAA games do now :slightly_smiling_face:

I don't think we need to go out of our way to provide modesetting, which provides a very bad experience to macOS and Linux players (especially when the game crashes, since the native resolution won't be set back).

vitorbalbio commented 4 years ago

The modern way to handle screen resolution changes is fully supported in Godot, and there's a demo that showcases it. If you look around, it's what a lot of AAA games do now πŸ™‚

I don't think we need to go out of our way to provide modesetting, which provides a very bad experience to macOS and Linux players (especially when the game crashes, since the native resolution won't be set back).

I'm not really against this approach but it need to be better integrated to be considered a "feature of the engine". Currently it's more like a handy workaround.

First you need to make your game around this workflow. That means that in every single 3D scene you will need a viewport container, viewport and attach a script to control resolution. Or you need a Master Scene that will handle all 3D scenes on it. It only gets a bit better in Godot 4 since you can set Linear Filtering directly in the ViewportContainer but in Godot 3x you yet need to band-aid it with a TextureRect. 😞

I really don't think decouple UI and 3D is a bad idea. It can indeed be great. But if it's a "engine feature" or "engine design" it need to be much better integrated on the engine itself and stop rely on multiple node setups to work. So it would be great if we have a proper "Viewport Screen Resolution" setting decoupled from the base window size that handle all viewports. The best approach to this i can think is to have this setting internally the ViewportContainers and allow us to overwrite if needed, or something like that. The root node in Godot 4 is also one of those already right? I can fill a more detailed proposal for this if you think it worth. πŸ˜‰

That should handle all this in a elegant way in all viewports. It probably yet would need a proper way to get all available screen sizes (Which also can be done by now with user32.dll) but only this would be already a much better solution than any currently available (and probably better then mine also).

This seams feasible?

starry-abyss commented 4 years ago

Also "elegant Godot way" (suggested in several different issue tickets on various resolution-related topics) to do things with a combination of several viewports has a limitation, which makes the suggestion a hell to use: #20619

vitorbalbio commented 4 years ago

Also "elegant Godot way" (suggested in several different issue tickets on various resolution-related topics) to do things with a combination of several viewports has a limitation, which makes the suggestion a hell to use: #20619

Yep! I posted a proposal to this some days ago. By the time i was not aware it was already a bug reported but i already think it could be because it's a hard limitation or flaw design of how editor handles viewports. https://github.com/godotengine/godot-proposals/issues/1465

TheSHEEEP commented 4 years ago

The modern way to handle screen resolution changes is fully supported in Godot, and there's a demo that showcases it. If you look around, it's what a lot of AAA games do now

Just to re-iterate: That incurs a hefty performance cost as was said some replies above (due to using another viewport in addition to the root one), as well as kinda nightmarish editor handling.

The other way is setting the window to fullscreen and the root viewport to whatever resolution you want (e.g. lower than whatever the fullscreen resolution is for performance or style reasons) and stretching set to "viewport".

So users can basically decide if they want:
Non-stretched UI + negative performance impact + editor issues
vs
Stretched UI + best performance + best editor handling

Is that correct?

Calinou commented 4 years ago

@TheSHEEEP Yes, more or less. That said, when you use the root viewport approach, keep in mind you can't enable filtering yet (this is implemented by https://github.com/godotengine/godot/pull/30039). Therefore, you want to enforce an integer scaling ratio to avoid scaling artifacts (1/2, 1/3, 1/4, …).

As for the editor handling issue, I believe you could figure out a way to automatically add your game scene to the viewport (for 2D projects). Maybe you can use an autoload for this?

For 3D projects, it shouldn't be an issue if you're already using the root viewport for your GUI elements.

jamie-pate commented 4 years ago

I think there is a misunderstanding here, this is not a bug, probably should be fixed with documentation.

Godot does not understand fullscreen video modes. It is kind of silly to support that nowadays, given that monitors all have fixed pixel densities.

The right way to do what you want to do is using viewport resizing and just change viewport size while keeping the regular fullscreen mode.

With laptop manufacturers producing laptops with Intel 620 gpus that want to power 4k screens it's more important than ever!

Calinou commented 4 years ago

@jamie-pate I think the best solution is still to manually decrease the resolution to 1080p or lower in this case. I have a 4K laptop with integrated graphics, and it's what I ended up doing after realizing not even the desktop was able to update smoothly at 60 FPS.

jamie-pate commented 4 years ago

Yes, after thinking about it I agree, just having a stressful time getting things working well on these trash tier laptops which are the most common platform of my demographic 😭

I already scan dxdiag output so maybe I can just put up a warning on the title screen

Another thing that 4.0 will hopefully bring that will make my life that much easier πŸ˜†

jamie-pate commented 3 years ago

With 4k being a huge performance suck, I wonder if there would be a way to just switch to normal dpi resolutions on any high dpi monitor on the system for the duration of the game, and switch back on exit πŸ€” that would make a sweet plugin

vitorbalbio commented 3 years ago

Hi Jamie. That's exactly what I'm doing. But it only works in C# and Windows builds since you need to access and setup the user32.dll. check this out: https://www.codeproject.com/Articles/6810/Dynamic-Screen-Resolution. Someone should notice that even if this was "closed" it's clearly that it's yet a requirement for many real users. I'm quite disappointed that it is considered solved by putting a close tag and a line in documentation saying that it will not be done while so many people yet ask for a proper solution or at least a best workflow to the current design.

Calinou commented 2 years ago

Note that in the master branch, you can now change the 3D resolution scaling (with any float value) without using a separate viewport. It won't affect 2D/GUI rendering, so your HUD will remain crisp.

FidelityFX FSR 1.0 was also merged to optionally improve scaling quality.

nonchip commented 2 years ago

@Calinou sadly also in the master branch, all of Viewport's members set_rect,set_attach_to_screen_rect,set_size_override,set_size_override_stretch seem to be gone (while some of them are still documented), and size, while writable, does not affect the viewport in any way. do you happen to know if/how it's still possible to "downscale"/"undersample"/... the 2D/GUI rendering in that way? because setting the window size in the project settings and then enabling stretch mode still works as expected, but i'd need to set that size dynamically. (i'm explicitly trying to change the resolution the whole fullscreen is rendered at, not "just" downsample the 3d part, because i'm doing this for style reasons too instead of pure performance)

EDIT: apparently there's a Window.content_scale_size now, which does what I want, and SubViewport.size_2d_override still exists, so i guess that would be how to change them now?