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

Noticable mouse jitter, but only when mouse mode is Input.MOUSE_MODE_CAPTURED #97294

Open
ivsbex opened this issue Sep 21, 2024 · 5 comments
Open

Comments

@ivsbex
Copy link

ivsbex commented Sep 21, 2024

Tested versions

  • Reproducible in: v4.4.dev2.mono.official.97ef3c837, v4.4.dev.custom_build.97ef3c837, v4.3.stable.arch_linux, v4.0.stable.official [92bee43], v3.6.stable.official [de2f0f1], v.3.3.stable.official, v.3.2.3.stable.official, v.3.2.2.stable.official
  • Not reproducible in: v.3.0.stable.official, v.3.1.stable.official, v.3.2.stable.official, v.3.2.1.stable.official

As for me, this bug in 3.3.stable.official is noticeable more than in 3.2.3.stable.official.

Also I can say nothing about 3.2.2.rc1. It feels between 3.2.1 when there is definitely no such a bug, and from 3.2.3+ where it can be clearly seen.

System information

Godot v4.4.dev2.mono - Arch Linux #1 SMP PREEMPT_DYNAMIC Thu, 12 Sep 2024 17:21:02 +0000 - Wayland - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4060 Laptop GPU - AMD Ryzen 7 7735HS with Radeon Graphics (16 Threads). 144Hz monitor

Issue description

It's pretty difficult to explain what's wrong, but it looks like something is wrong. Camera movements are too sharp. For example when you move mouse by 1px, camera immediately changes its rotation by larger amount. It's really noticeable and looks very ugly. As for me it's even better to play my own game when mouse is visible (it's FPS) than with this bug. I'm not really sure, but that may be visible only on high refresh rate screens (like 144hz that I have).

Looks like #54818, but sensitivity doesn't change in this case. Also may be a regression of #45592, because there something also was wrong only when mouse mode is ..._CAPTURED.

I already tried making my own camera smoothing to fix it and it smoothed camera movement, but didn't fix this issue at all. Only fix is to change mouse mode. Also, enabling physics interpolation seems to make no difference.

Steps to reproduce

  1. Set mouse mode to Input.MOUSE_MODE_CAPTURED.
  2. Rotate camera with normal speed. Not that high when you see nothing and not that low when everything is almost static.
    3 (optional). Set mouse mode to Input.MOUSE_MODE_VISIBLE to compare how much smoother that is.
    4 (optional). Move camera without rotating to compare how much smoother that is.

Also to see it better, try to visually lock on an object and rotate in such way, that this object will always be in center of screen and just move in circle around it.

Also more noticeable when camera is moving at relatively high speed.

Minimal reproduction project (MRP)

The smallest reproducible project when you already have Godot Engine is the editor itself. Hold right mouse button and move your mouse to rotate camera (in 3D tab) and move using one of WASD keys, also you can press Shift to speed up to make bug more noticeable. But in empty project it's pretty difficult to see the difference, so I made this MRP (you can actually rotate camera if you run it, move using WASD keys, and speed up while holding Shift):
bugjitter3d.zip

If you see some weird glitches, because you opened this project in earlier version of Godot (not 4.4), just run it. If you are getting errors about screen_relative, replace it to relative in GDScript code.

For Godot 3, just create CSGBox3D or simply rotate camera even without it (this bug still should be seen).

@ivsbex
Copy link
Author

ivsbex commented Sep 21, 2024

git bisect result:

087a83fd54974fc03acf0ea571c505ea1456dd5c is the first bad commit
commit 087a83fd54974fc03acf0ea571c505ea1456dd5c
Author: Hugo Locurcio <hugo.locurcio@hugo.pro>
Date:   Mon Jun 15 19:30:39 2020 +0200

    Improve the low processor mode sleep precision
    
    The Low Processor Usage Mode Sleep Usec setting is now used as a
    FPS limiter rather than a constant sleep duration.
    
    This will increase CPU/GPU usage slightly due to the higher
    effective FPS, but the increase in overall smoothness is worth it.
    
    If both Force Fps and Low Processor Usage Mode settings are enabled
    in the project settings, only the setting that causes the highest
    sleep duration will be retained.
    
    This closes #11030.
    
    (cherry picked from commit 1c28b269d8e691c9da2605c712120864ee1b467f)

 main/main.cpp | 39 +++++++++++++++++++++++++++------------
 1 file changed, 27 insertions(+), 12 deletions(-)

I have no idea why, this commit looks unrelated. I checked previous commit and it works just fine. Then from previous commit I changed main/main.cpp in following way:

#define G___BREAK_CAMERA
#ifdef G___BREAK_CAMERA
	const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
	if (frame_delay) {
		// Add fixed frame delay to decrease CPU/GPU usage. This doesn't take
		// the actual frame time into account.
		// Due to the high fluctuation of the actual sleep duration, it's not recommended
		// to use this as a FPS limiter.
		OS::get_singleton()->delay_usec(frame_delay * 1000);
	}
	// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
	// previous frame time into account for a smoother result.
	uint64_t dynamic_delay = 0;
	if (OS::get_singleton()->is_in_low_processor_usage_mode() || !OS::get_singleton()->can_draw()) {
		dynamic_delay = OS::get_singleton()->get_low_processor_usage_mode_sleep_usec();
	}
	const int target_fps = Engine::get_singleton()->get_target_fps();
	if (target_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
		// Override the low processor usage mode sleep delay if the target FPS is lower.
		dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / target_fps));
	}
	if (dynamic_delay > 0) {
		target_ticks += dynamic_delay;
		uint64_t current_ticks = OS::get_singleton()->get_ticks_usec();

		if (current_ticks < target_ticks) {
			OS::get_singleton()->delay_usec(target_ticks - current_ticks);
		}

		current_ticks = OS::get_singleton()->get_ticks_usec();
		target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay);
	}
#else
	if (OS::get_singleton()->is_in_low_processor_usage_mode() || !OS::get_singleton()->can_draw())
		OS::get_singleton()->delay_usec(OS::get_singleton()->get_low_processor_usage_mode_sleep_usec()); //apply some delay to force idle time
	else {
		uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
		if (frame_delay)
			OS::get_singleton()->delay_usec(Engine::get_singleton()->get_frame_delay() * 1000);
	}

	int target_fps = Engine::get_singleton()->get_target_fps();
	if (target_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
		uint64_t time_step = 1000000L / target_fps;
		target_ticks += time_step;
		uint64_t current_ticks = OS::get_singleton()->get_ticks_usec();
		if (current_ticks < target_ticks) OS::get_singleton()->delay_usec(target_ticks - current_ticks);
		current_ticks = OS::get_singleton()->get_ticks_usec();
		target_ticks = MIN(MAX(target_ticks, current_ticks - time_step), current_ticks + time_step);
	}
#endif

If i define G___BREAK_CAMERA, camera breaks (this bug occurs), otherwise it doesn't.

@ivsbex
Copy link
Author

ivsbex commented Sep 21, 2024

main.cpp.zip

With file:

  1. Clone Godot from commit 087a83f
  2. Unpack this zip file (you will get main.cpp file)
  3. Replace main/main.cpp file from cloned Godot with unpacked file

Without file:

  1. Clone Godot from commit 087a83f
  2. Replace lines 2166-2197 with lines I wrote in my previous comment

After that, comment out / uncomment #define G___BREAK_CAMERA as you want, recompile it and run.

@ivsbex
Copy link
Author

ivsbex commented Sep 21, 2024

https://github.com/godotengine/godot/blob/master/core/os/os.cpp#L582
That's link to latest version of Godot having almost the same code, that breaks camera in 3.2.2

@ivsbex
Copy link
Author

ivsbex commented Sep 22, 2024

Replacing OS::add_frame_delay function body with:

void OS::add_frame_delay(bool p_can_draw) {
	if (is_in_low_processor_usage_mode() || !p_can_draw) {
		delay_usec(get_low_processor_usage_mode_sleep_usec()); //apply some delay to force idle time
	} else {
		uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
		if (frame_delay)
			delay_usec(Engine::get_singleton()->get_frame_delay() * 1000);
	}
}

fixed issue only in the editor itself. Enabling Low processor mode in project settings helped to fix it in game, but FPS dropped from 144 stable to 130 with 2% CPU load and 65% GPU (that's expected, because of code patch I did). Changing Low processor mode sleep makes this bug visible again (even if changing to lower value, like 1000).

I added following code in _enter_tree in my GDExtension:

    double refresh_rate = DisplayServer::get_singleton()->screen_get_refresh_rate();
    if (refresh_rate < 0.0) refresh_rate = 60.0;
    // 900_000 is 1_000_000 * 0.9, where 0.9 is some random coefficient that makes it work
    OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(static_cast<int32_t>(900000.0 / refresh_rate));

Now it's close to 144 FPS (140-141 FPS), but also without this bug.

I know there is a way to make it work without Low processor mode, but it's just a temporary workaround for people that experienced this issue like me.

@ivsbex
Copy link
Author

ivsbex commented Oct 30, 2024

Of course, OS::add_frame_delay is not the cause, it just makes it really visible. I tried setting Prefer Wayland in editor and changing display driver to wayland in project settings and this bug totally disappeared. It means that this is only bug with X11.

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

No branches or pull requests

2 participants