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

Optionally render 3D content at scaled resolution #51870

Merged
merged 1 commit into from
Aug 26, 2021

Conversation

BastiaanOlij
Copy link
Contributor

@BastiaanOlij BastiaanOlij commented Aug 19, 2021

This PR adds a tickbox to viewports that instructs Godot to render 3D content of a viewport in a scaled resolution:
image

There is also a project setting that defines the default behavior (and thus lets you set the main viewport):
image

Or alternatively you can obviously do this in code:

get_viewport().scale_3d = Viewport.SCALE_3D_75_PERCENT

Note that the settings are ignored in the editor, the editor always renders at normal resolution.

image

@BastiaanOlij
Copy link
Contributor Author

Owh and just in case someone asks, yes this is on a 165hz monitor with vsync enabled :)

@Calinou
Copy link
Member

Calinou commented Aug 19, 2021

Looks good to me, some comments about the feature itself that could be addressed in future PRs:

  • It should be possible to use any scale factor, rather than being limited to half resolution on each axis. Supporting anamorphic scaling (e.g. 50% on X axis, 100% on Y axis) would be great too, but will require more changes (namely with the 3D camera FOV that needs an automatic anamorphic adjustment). This will pave the way to supporting dynamic resolution scaling, where anamorphic scaling can improve the visuals without compromising on performance.
    • Rendering at a higher resolution than 100% could also be supported to allow for supersampling 3D with a slightly lower cost compared to the current approach. If nearest-neighbor filtering is implemented, linear filtering would be forced in this case as it's required for supersampling to work.
  • It should be possible to toggle between linear filtering and nearest-neighbor filtering. Nearest-neighbor filtering looks significantly crisper, but is not compatible with scaling ratios that are not integer divisors (such as 1/2, 1/3, 1/4, …). Linear filtering will remain the default because of this, but nearest-neighbor filtering would be nice to have. It's not just for pixel art games 🙂
  • AMD FidelityFX Super Resolution #51679 also implements lower-resolution 3D rendering if I'm not mistaken. I wonder which approach is the "best" on a technical standpoint.
  • I wonder if this can be implemented in 3.x too, especially in GLES2 which would benefit a lot from it on mobile devices.

All this is not essential for a MVP, but I'd like to take a look at it eventually.

@BastiaanOlij
Copy link
Contributor Author

BastiaanOlij commented Aug 19, 2021

Having different scaling options shouldn't be too hard to add, when scaling on different axis the biggest issue is going to be keeping track of the aspect ratio. Instead of a boolean we could make this a dropdown with common options.

If I recall correctly we've already created the camera projection matrix prior to hitting this code so it's likely that it would just work. There may be a few places in the source code, especially within shaders, where we're using the resolution of the buffer but that's about it.

Personally one of the ideas I've been playing around with is to create the buffer at its full resolution, but restrict rendering to a portion of the buffer. Not 100% sure what the Vulkan equivelant is but in OpenGL using glViewport to only render to a portion of the buffer. This way you could dynamically alter the target size as a response to FPS. This would be a much larger project to implement and get right then I have time to invest right now.

I've not looked into any filtering options, I think there is definitely merit in building upon this and improving this with different filtering strategies. Right now I'm just using the existing settings for the tonemap stage and I'm not even sure that does any filtering so it looks blocky. Probably won't be noticeable on a tiny phone screen. Should just be a matter of setting the right filtering settings on the render buffer.
I've also not really read up on FSR. I guess it would be a worthwhile technique to do the up-sampling with if its fast enough on mobile.

Retrofitting this to GLES2 in Godot 3 will be challenging simply because we're rendering everything into one buffer there and we'd almost be rewriting it to the same approach as in the GLES3 renderer.
It should in theory be easy enough to apply to the GLES3 renderer but there are too many mobile unfriendly techniques applied there. Wonder if its worth the trouble though I guess Godot 4 is still some months away before its viable...

@reduz
Copy link
Member

reduz commented Aug 21, 2021

I suggest this is made an enum, so we can support common use cases like:

SCALE_3D_DISABLE
SCALE_3D_75_PERCENT
SCALE_3D_50_PERCENT
SCALE_3D_25_PERCENT

Some phones are too bad that you will really need to scale your 3D to quarter size in some games if you still intend to play them. Having this as an enum should help a lot with having dynamic quality settings.

@reduz
Copy link
Member

reduz commented Aug 21, 2021

@Calinou Scaling non uniform is kind of undesired because post processing will not work properly.
For FidelityFX this can be an upscale_mode option that we can add later on.

@BastiaanOlij
Copy link
Contributor Author

I suggest this is made an enum, so we can support common use cases like:

SCALE_3D_DISABLE
SCALE_3D_75_PERCENT
SCALE_3D_50_PERCENT
SCALE_3D_25_PERCENT

Some phones are too bad that you will really need to scale your 3D to quarter size in some games if you still intend to play them. Having this as an enum should help a lot with having dynamic quality settings.

All done, let me know what you think :)

@BastiaanOlij BastiaanOlij changed the title Optionally render 3D content at half resolution Optionally render 3D content at scaled resolution Aug 21, 2021
@Calinou
Copy link
Member

Calinou commented Aug 21, 2021

Is it possible to add a 33% scale option? This would be a good fit for mid-range mobile devices, which would render at 360p or 480p depending on whether they're 1080p or 1440p.

@BastiaanOlij
Copy link
Contributor Author

Is it possible to add a 33% scale option? This would be a good fit for mid-range mobile devices, which would render at 360p or 480 depending on whether they're 1080p or 1440p.

Sure, should only be a few lines of code extra

@BastiaanOlij
Copy link
Contributor Author

Ok, added the 33% option. @reduz if you're happy with this it can be merged imho

@Shatur
Copy link
Contributor

Shatur commented Aug 22, 2021

Modern games (e.g. Fortnite, Overwatch, etc.) usually can scale 3D resolution to any value in settings to increase FPS:
image

@@ -186,7 +186,7 @@ class RasterizerSceneDummy : public RendererSceneRender {
void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override {}

RID render_buffers_create() override { return RID(); }
void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) override {}
void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, bool p_is_scaled, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) override {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing p_is_scaled here, just check where relevant if the render target size is different than the render buffer size.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or do you mean setting is_scaled inside of render_buffers_configure by querying the size of p_render_target ?

@BastiaanOlij
Copy link
Contributor Author

@reduz have a look at how I changed this to no longer pass the is_scaled around. I had to add a method to render device to obtain the size of a texture.

@reduz reduz merged commit 227ab24 into godotengine:master Aug 26, 2021
@Calinou
Copy link
Member

Calinou commented Nov 25, 2021

For future reference, it's now possible to set floating-point number as a resolution scale between 25% and 200% thanks to #52215. Values above 100% can be used to perform supersampling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants