godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.14k stars 96 forks source link

Add a dynamic resolution setting for 3D viewports #917

Open Calinou opened 4 years ago

Calinou commented 4 years ago

Describe the project you are working on:

The Godot editor. Nothing prevents this feature from being usable in the editor after all. It could be useful for heavy 3D projects on slower hardware :wink:

Describe the problem or limitation you are having in your project:

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

Dynamic resolution adjustments can be performed to increase the FPS in heavier scenes during gameplay, without requiring the player to change their graphics settings. This approach has become increasingly popular in recent AAA games.

This feature is helpful for VR as well, where achieving framerates higher than 90 FPS is crucial to a good experience.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:

All the aspects below are well-described in this video from Digital Foundry, which also showcases the feature in action.

The rendering scale factor represents the 3D viewport resolution and will vary depending on the current FPS (or milliseconds per frame, to be precise).

For instance, if using a 3840×2160 viewport:

Note that in any case, the viewport's physical size remains the same. Only its rendering resolution changes. Also, the viewport must use linear filtering for this to work, not nearest-neighbor filtering.

The rendering scale could be allowed to go above 1.0 to create a supersampling effect on high-end hardware. The default values for dynamic resolution would likely constrain the value between 0.5 and 1.0.

The rendering scale is decided this way:

Since tweaking the adjustment speed factor is more of an art than a science, it should also be configurable. More aggressive settings will help increase performance, at the cost of making resolution changes more noticeable during gameplay. The speed at which the rendering scale can drop should be adjustable separately from the speed at which it can raise. The default setting should probably be fairly aggressive (for a 60 FPS target), as dynamic resolution is only worthwhile if it truly improves overall gameplay smoothness.

To avoid interfering with 2D elements which should generally always be drawn at native resolution, this proposal will internally adjust Viewport's scaling_3d_scale property. To configure this behavior on a per-Viewport basis, a "Dynamic Resolution" properties should be added to Viewport. Project settings should also be added to configure dynamic resolution on the root viewport.

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

Not really, as it takes a fair amount of effort to do it right. See also the performance issues described below.

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

It could be made as an add-on, but it's too slow to be worth the effort. I'm not sure if this can be alleviated with Vulkan. See godot-dynamic-resolution-demo where I attempted this in 3.2.1. The viewport creation costs made it slower than not using dynamic resolution in the first place :slightly_frowning_face:

vagrantG commented 4 years ago

How does someone would set the scaling in the editor? Manually (maybe a list to switch between values depending on what you are working on? scaling factor or target FPS?) or custom script as one would do in a project?

Another point is considering (in the future?) dynamic resolution for certain elements in separate buffers and compositing things together in a final image. A common idea is to render particles in a separate small resolution buffer to reduce their render time. Maybe something that could be done in a custom render in Godot 4 reusing the classes for the scaling of the whole viewport?

The old article about dynamic resolution by Intel

https://software.intel.com/content/www/us/en/develop/articles/dynamic-resolution-rendering-article.html

talks about deciding which LOD to use based also on the resolution used, another possible way to control this system in the future. Reminds me how in Terra Nova: Strike Force Centuari you could see a so large terrain scene (for that time) but the devs said it was possible because the resolution was so low that you couldn't notice the geometry at a distance was so simplified!

SIsilicon commented 4 years ago

I'm going to be speaking from the perspective OpenGL, but this can apply to Vulkan as well. The current framebuffer structure can stay the same with dynamic resolution. What should change is the viewport size when rendering 3D stuff glViewport; not the framebuffer size mind you. When rendering, only part of the framebuffer is affected. When it is required to sample the render target, such as when using SCREEN_TEXTURE in shaders, the framebuffer can be upscaled in a temporary framebuffer and reapplied to the original one. This all would happen in the scene renderer and not the canvas renderer.

Calinou commented 2 years ago

Posting a comment from clayjohn for reference, if anyone is interested in implementing this:

IMO before we make 3d rendering scale fully dynamic we need to allow dynamically changing rendertarget resolution without allocating new textures.

In OpenGL this can be done with glViewport and some trickery in fragment shaders. In Vulkan I think it can be done with texture views.

Also, note that the current version of this proposal no longer proposes to use anamorphic scaling due to it being difficult to expose to user shaders in a safe way: https://github.com/godotengine/godot/pull/52232#issuecomment-908448190