Stabilize viewport behavior under FSR2#110005
Stabilize viewport behavior under FSR2#110005mechalynx wants to merge 1 commit intogodotengine:masterfrom
Conversation
c967d9c to
33a0624
Compare
Upscaling needs a somewhat large viewport resolution to work well, it'll never be useful at sizes below 64x64. It's fine to fall back to bilinear in this case. |
Perhaps. I just felt that the less the fix intrudes upon the user's choices, the better. Falling back to Bilinear is permanent unless the user sets it to FSR2 again. The fix already will cause surprise and problems for some people and by looking at the way people use I will however show an example of the difference so it's easier to decide if it's worth preserving FSR2 at these low resolutions or not.
All screenshots are from the artifact of this PR. You can see that, for example, the icon's edges are better antialiased with FSR2, whereas with Bilinear it eats the right, bottom and top edge but keeps the left edge sharp. The background is a bit sharper with FSR2 as well. Whether that's significant in practice or useful, I don't know. I guess when I was testing this, I wrongly compared 0.67 bilinear with effectively 1.0 FSR2, so they looked much more different than they really are. Edit - also, if the fix remains as I wrote it, I feel uncomfortable with it being backported to 4.4 because it may break projects. It's not severe enough to not be fixed in 4.5 in my opinion but I also don't know what's considered truly worth backporting or fixing early. But I felt like I had to clarify my position on that. I am cleaning up a spreadsheet I made back when I was writing the code to see how the size change code behaves with different combinations of dimensions, which should make clearer whether this fix is risky or not and how much, when it comes to potential compatibility with existing projects. Edit 2 - added spreadsheet to main comment Footnotes
|
|
I have re-implemented this fix to not do any resizing and just fall back to Bilinear if any of the two dimensions of a viewport, after 3d scaling, fall below 64px. It's here: https://github.com/mechalynx/godot/tree/viewport_limit_fix_no_resize. If it's decided that resizing is not needed, then that version supercedes this one. I'm neutral on this: resizing is more minimal in taking control from the user but just falling back to bilinear is much simpler, much less code and will work for virtually everyone. I'm not sure if I should push the changes into this pull request or make a new one that supercedes it, so I'll wait for advice on that so that I don't trigger build actions needlessly. |
|
Closing this in favor of #110119 since this one only does the extra resizing which is likely too tedious to review and also unnecessary as a fix. Can reopen and rebase if needed of course. |








Edit - Fix without the resizing is here:
Below is the original message
When a subviewport using FSR2 upscaling falls below 64px on at least one dimension it produces this error spam:
Additionally, it was possible through the scaling factor to have either dimension fall below
2px, crashing the editor.While testing using the MRP from the linked issue, I found that it was also possible to set the dimensions of the viewport above
16384, which does not crash the editor but causes updating of the subviewport to stop (at least when rendering to a viewport texture) until the editor is restarted (or presumably until rendering of the subviewport is restarted somehow). It seems to me that the code intended to clamp sizes within a valid range but the enforcing was inconsistent, possibly because it assumes the viewport provided to_configure_3d_render_buffers()has a valid size, as the original code makes much more sense with that assumption.Also, when the code falls back to Bilinear from FSR2, the scaling type is not re-evaluated, leading to it rendering with Bilinear scaling but still using jitter, which causes a lot of wobble, especially on smaller subviewport sizes.
This PR:
Enforces an upper limit of
16384to subviewport dimensions1.0.Restores the proper scaling mode type after fallback to Bilinear. This fixes jitter that can occur due to fallback to Bilinear.
Ensures that the render size of a subviewport that uses FSR2 will always be of valid dimensions, preserving aspect ratio and also ensuring the target size is always equal to or larger than the render size, to preserve a scaling of
1.0or less. This fixes the error spam and editor crash referenced in the linked issue.This is done by first scaling up the render size so the smaller dimension is at least
2px, then scaling it up again so the larger dimension is at least64px. The scaling is done on both dimensions proportionally on each pass so as to preserve the aspect ratio and prevent stretching. The scaling up is the minimal possible. The target size is then put through the same process to ensure it is always equal to or larger than the render size on both dimensions (so we're never accidentally downscaling, since this is not supposed to be done with FSR2).Scaling factor is not preserved due to this adjustment but I feel that the intent of the user to use FSR2 is to improve performance and render to a lower resolution, so it makes more sense to preserve the aspect ratio while also keeping it as small as possible, rather than trying to preserve the user-defined render scale. Trying to preserve the original scaling factor can result in the target size growing enormously, which defeats the purpose of using FSR2 on any scale other than
1.0, and at a scaling of1.0all we need to do is make sure the dimensions are valid for the subviewport, which is already guaranteed due to the previous changes.Possible concerns:
The changes don't fix FSR2 so that it won't choke on render sizes that are below 64px on both dimensions, nor does it address it crashing the editor when the size is below 2px on either of them. I'm not sure if this is due to FSR2's nature or if there is a bug somewhere but if it is the latter, this PR's fix will mask this error and make it harder to trigger, so likely harder to debug and fix in the future. Since the previous behavior can't be toggled back, anyone trying to fix the actual error might be in for a lot of trouble if they don't know about this adjustment.
Although the target size is adjusted, if the subviewport is set to
2x2, the preview seems to show just 4 pixels being rendered, when the render size internally should be64x64due to the adjustment and so should the target size. I'm not sure what to make of this or if it's a concern but it feels like I may have misunderstood how this whole thing works and I've made the wrong changes. I went through this code with gdb several times but I think the rendering is done on a separate thread so I could only step through the render buffer creation but not what follows it up to the point of rendering (and I'm guessing it would take forever just to step through all that, given how often even_configure_3d_render_buffers()gets triggered even on the UI).The new code contains multiple
ifstatements and I'm unsure if I should write it differently to make branch prediction easier. It isn't a particular performance issue in most cases but it might be if someone is just using the slider in the editor, since every change requires going through this code. However, this new code only runs if the dimensions are too small, so for most cases it doesn't run anyway but it might cause failed branch predictions on the cpu. I'm uncertain about whether something needs to be done about this or not. I wrote things in a way that seemed nice to me but i'm not sure if that's the most clear and maintainable way to do it. I would also have liked to do this whole thing branchless, with just one fancy calculation but I couldn't figure out how to do it and even then, it might be a bad idea for maintainability. Plus, the clamps and max macros already run conditionals as it is. Fingers crossed for the compiler to guess correctly and optimise it.I would have preferred to make a utility function to do this size adjustment, since it's done once for the render size and once again for the target size and it's the same calculation. It can be done without side-effects, so it would be a pure function and it would make the code more concise and thus easier to read, whereas now it is a block of ifs, but i have no idea where i would put this function or even if it should start with an underscore and things like that. It feels icky because I removed repetitive code only to introduce repetitive code of my own but I was unsure about what is preferred within the codebase.
possible questions:
addendum:
but I'm unsure if I should make them in one proposal or separately:Edit - Added link to spreadsheet that shows how the resizing code behaves from small viewports, high aspect ratios, up to large viewports.
Edit 2 - added link to alternative PR at the top