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

Heavy stuttering issue in simple 2D game [Windows 10, Nvidia] #19783

Closed
crystalnoir opened this issue Jun 26, 2018 · 145 comments · Fixed by #33414
Closed

Heavy stuttering issue in simple 2D game [Windows 10, Nvidia] #19783

crystalnoir opened this issue Jun 26, 2018 · 145 comments · Fixed by #33414

Comments

@crystalnoir
Copy link

crystalnoir commented Jun 26, 2018

Godot version:
Godot 3.1-dev / Godot 2.X

OS/device including version:
PC - Windows 10, GeForce GTX 1060 6 GB, 16 GB RAM.

Issue description:
Stuttering / jitters when moving a 2D Sprite. Reproduced on 2 computers (with nvidia graphic cards, the one above and a laptop), a friend of mine reproduce this issue too.

Steps to reproduce:
I just download the "First project" that we can make in the documentation. I have tested to reproduce the first part of this tutorial to only have the player and nothing else. If I run it without change anything. I have some stuttering at the same as the game run with 60 FPS. The motion is not smooth. I collaborate with a dev to try to reproduce this problem and try to understand the issue. I have made a lot of test (Run from editor, run after compilation without debug mode etc...But when I deleted the animation, all run smoothly.

PS : it seems that physic behaviour make sprite stuttering too (try with kinematic and Area2D node with collision). If I desactivate collision and replace Area2D with a simple node2D there is no stuttering (if I don't play any animation on the player).

Minimal reproduction project:
Here's a minimalist project (that comes from the first game project of the documentation). If I run it, I have some stuttering. If I delete the player animation I have no more stuttering.
FirstGame.zip

@crystalnoir
Copy link
Author

crystalnoir commented Jun 26, 2018

Hi, I have updated my report because I tested it on my other computer (home) and the issue is here at the same as I change the animation. So, this is not the animation that causes the issue. I think I believed it because when I saw that it was on my laptop, it has a small screen. Here's a video of the issue (the video is at 60 FPS) :
GodotStutter.zip

@akien-mga
Copy link
Member

If the video is an accurate representation of what's on screen in real time, then it's a hell lot more stutter than I've ever seen mentioned in any stutter-related issue here. There must seriously be something wrong with this Windows 10 / GTX 1060 setup (I see quite a few articles on the Internet detailing performance issue on Windows 10 / Nvidia after major Windows updates, but can't tell if it's related).

@akien-mga
Copy link
Member

Here's a video of the test project on my system, Mageia 6 x86_64 (Linux), Nvidia GTX 670MX with recent proprietary drivers (390.59). No stutter at all (on Openbox - on KWin the compositor messes things up and there's a very light stutter every 10 s or so).
StutterTest_LinuxNvidia_OK.zip

BTW here's a fixed version of the demo project firstGame_fixed.zip, the original one had files split over three different folders somehow ("firstgame", "firstGame" and "FirstGame").

@akien-mga akien-mga changed the title Stuttering issue when playing 2D animation Heavy stuttering issue in simple 2D game [Windows 10, Nvidia] Jun 26, 2018
@Zylann
Copy link
Contributor

Zylann commented Jun 26, 2018

The game gives me the same amount of stuttering as in the video.
However, turning vsync off gets rid of the stuttering completely (but the game runs at 4000 fps).
Windows 10 64 bits nVidia GTX 1060 too here.

@RaXaR
Copy link
Contributor

RaXaR commented Jun 26, 2018

I also tested as @Zylann suggested here and got the same results. I too have Win10 x64 and nVidia GTX 1060.

Edit: I use these drivers from nVidia:
398.11-desktop-win10-64bit-international-whql

@Ranoller
Copy link
Contributor

Win 7 64 bit GLES2 and GLES3 tested, GeForce GTX 660/PCIe/SSE2... no stutters. Turning on Aero, with the 2d editor of godot behind the game results in some little stutter (Godot editor render interfere with the rendering of the game).

However, Godot stuttering is the giant invisible enemy, we all know it is there but we do not want to look very closely because we know that the solution is not simple.

Your issue seems like different physics fixed fps with monitor refresh rate, i see this kind of stutter in monitors that have not same hz that editor configured physics fps, but can be other thing.

@Zylann
Copy link
Contributor

Zylann commented Jun 26, 2018

Your issue seems like different physics fixed fps with monitor refresh rate

The demo doesn't uses physics, only a simple _process.

@Ranoller
Copy link
Contributor

Ranoller commented Jun 26, 2018

True... i say that i only see that heavy stutter in this case, but it´s true that there is no physics process involved. I test changing hz of one of the monitors and no differences, 0 stutter in my gear.

Edit: I got win 7, win 8.1 and win 10 in this computer and take the time to test all. No stutters in win 8.1. I´m testing in win 10 now and is very smooth... no windows problem. Godot is angry with your 1060?

@crystalnoir
Copy link
Author

crystalnoir commented Jun 27, 2018

Here's the same test with my laptop. As you can see the problem is here too. But it seems that's less visible but it's here.

Laptop Specs : Windows 10 - Geforce 940M

Here's the Laptop video (it's a 60 FPS video) :
GodotStutterLap.zip

@Ranoller
Copy link
Contributor

Can anyone with the stutter problem try to execute the demo changing in Player.gd _process with _physics_process?

@crystalnoir
Copy link
Author

crystalnoir commented Jun 27, 2018

I will test this evening on my home PC this is where I have the problem all the time. But I have a weird thing : This morning, I gave you a video with the project on my laptop and as you can see you have the same kind of stuttering. The problem is that now, if I run it again I don't have this stuttering again, like it's random. And I didn't change anything on my laptop, I worked on it all the morning on a TSE session.

Warning : I talk only for my laptop. On my home PC with GTX 1060 the problem is always here. But on my laptop the problem seems to occur randomly. That's why I think that for now, I will let my laptop on the side for testing purpose and I will test only on my home PC that have the problem all the time, to be able to isolate the "bug".

@RaXaR
Copy link
Contributor

RaXaR commented Jun 27, 2018

@Ranoller I tested it and got the same result. The stutter is still there and it pretty much looks the same.

@crystalnoir
Copy link
Author

@Ranoller Made the test and the same à @RaXaR that doesn't change anything. Got the same problem.

@Ranoller
Copy link
Contributor

Ranoller commented Jun 27, 2018

This not look well....

To exactly specify the bug I would do this tests:

  1. Full screen on - off
  2. If more than 1 monitor:
    Disable- Enable shared desktop
  3. Aero on-off

Your cards run well other games? ...

Reading the first post about the animation -> stutter / no animation -> no stutter i read the code and i see some stuff that i don´t think it´s correct... exactly: changing animation every frame. I think that code should check current animation. Probably it didn´t change nothing, but if someone want test to change Player.gd in this way:

extends Area2D

# class member variables go here, for example:
# var a = 2
# var b = "textvar"
export (int) var SPEED #How fast the player will move (pixel/sec)
var screenSize #size of the game window
onready var AnimSprite = $AnimatedSprite


func _ready():
	# Called when the node is added to the scene for the first time.
	# Initialization here
	screenSize = get_viewport_rect().size
	#Engine.target_fps = 60
	pass

func _process(delta):
#	# Called every frame. Delta is time since last frame.
#	# Update game logic here.
	var velocity = Vector2() #Player movement vector
	if Input.is_action_pressed("ui_right") :
		velocity.x += 1
	if Input.is_action_pressed("ui_left") :
		velocity.x -= 1
	if Input.is_action_pressed("ui_down") :
		velocity.y += 1
	if Input.is_action_pressed("ui_up") :
		velocity.y -= 1
	if velocity.length() > 0 :
		velocity = velocity.normalized() * SPEED
		if !AnimSprite.is_playing():
			AnimSprite.play()
	else :
		if AnimSprite.is_playing():
			AnimSprite.stop()
		
	if velocity.x != 0 :
		if AnimSprite.animation != "right":
			AnimSprite.animation = "right"
		AnimSprite.flip_v = false
		AnimSprite.flip_h = velocity.x < 0
	elif velocity.y != 0 :
		if AnimSprite.animation != "up":
			AnimSprite.animation = "up"
		AnimSprite.flip_v = velocity.y > 0
		
	position += velocity * delta
	position.x = clamp(position.x, 0, screenSize.x)
	position.y = clamp(position.y, 0, screenSize.y)

This is the last idea... probably nosense for the problem, but... your graphic card is very common in players, so godot should execute well in it.

@crystalnoir
Copy link
Author

@Ranoller

For :

  • The first point : Already tried full screen or not, it doesn't change anything

  • 2 : Already tried to run with only one monitor (that's my common config but I have a second monitor too sometimes, so I tried both) and it doesn't change anything.

  • 3 : I have to test (should be able to do this, this evening (France Time).

  • 4 : I have to test your code (should be able to do this, this evening (France Time).

@crystalnoir
Copy link
Author

crystalnoir commented Jun 28, 2018

Just tested your code and it doesn't change anything :(

@Ranoller
Copy link
Contributor

Although this problem may not be directly related to godot, godot must revise well stuttering issues... it´s not true that all games stutters as it was said in another thread. Today i was playing n++, full screen, windowed, trying to see any stutter and no.... no stutters at all. Same with Ori and the blind forest, to many bad things have to do to have any stutter in this game (windowed with other programs in background, etc.... and only 2 o 3 frame skips in a hour...). Godot, on starting execution, always stutter x number of seconds, later it stabilizes, but every X seconds you are going to have frame skips (if you don´t have the problem of the gtx1060 of course). We shouldn´t treat this issue as a minor problem.

@crystalnoir
Copy link
Author

I try to do my best to isolate the problem but at my level it's a bit difficult. I tried testing different setups but without result. I also tested to but a background image instead of use a color with clearscreen. I've already seen (don't remembre witch) an engine with this problem because of rendering a 2D sprite on a "void screen" causes this problem, but it seems that's not the case here. So I don't have any idea for now.

@Zylann
Copy link
Contributor

Zylann commented Jun 28, 2018

Out of curiosity, try to profile how long SwapBuffers takes in context_gl_win.cpp around line 68. If it takes longer than 16ms, then you are likely dropping a frame here.

@crystalnoir
Copy link
Author

If someone that knows the sources of Godot could test that I'm interesting in the result (sorry for my english...)

@RaXaR
Copy link
Contributor

RaXaR commented Jun 28, 2018

I was playing with this issue yesterday and I it magically resolved itself after the game windows was running for about 60 seconds. Then it was smooth, this tells me that it could be a caching thing?

@Ranoller
Copy link
Contributor

Out of curiosity, try to profile how long SwapBuffers takes in context_gl_win.cpp around line 68. If it takes longer than 16ms, then you are likely dropping a frame here.

Maybe could be helpful to know if the problem happends in GLES2, we don´t test that

@crystalnoir
Copy link
Author

I tried to play with options in Godot about it, but it doesn't change anything for me, may be I don't know exactly what to change ?

Tried to let the game for more than 2 minutes but the problem is always here not solved for me after 60 s.

@meld-cp
Copy link

meld-cp commented Jul 6, 2018

I had a similar issue with 3.0.3. (Win10 64 bit, Nvidia 660) I didn't notice it with 3.0.2.

I think it has something to do with the AnimatedSprite Node because I see the performance issues with levels which have this node. I get the shuttering when running from the IDE or if I export to Win 32bit, but If I export to Win 64bit everything runs as it should, no stuttering.

@meld-cp
Copy link

meld-cp commented Jul 6, 2018

.. interestingly, I don't have the issue with the example project 'FirstGame.zip'... but still do with my game, FPS drops to 5 when run from the IDE and 32bit version, GPU sits about 2%... but, with the 64bit export GPU is at 30% and everything is fine.

@crystalnoir
Copy link
Author

Hi there, is any news about this problem ? I've just tested with the pong demo (I didn't do that before, just with the tutorial game) and it seems the problem is here with this sample project too. I use the last version of Godot on Steam to test it.

Updating Nvidia drivers didn't change anything, so I come to take some news about this issue. I didn't find how to get ride of it yet.

@Calinou
Copy link
Member

Calinou commented Oct 27, 2019

@TerminalJack The fork is no longer available (or it's set to be private), could you make it available again please?

@TerminalJack
Copy link
Contributor

@Calinou Sorry. I jacked the repository up so I deleted it. I will fork it again and re-commit the changes here shortly.

@TerminalJack
Copy link
Contributor

@Calinou The new commit is here.

@ghost
Copy link

ghost commented Oct 27, 2019

@TerminalJack Your fix seems to be only for Windows but I face the same issue on Ubuntu.

@TerminalJack
Copy link
Contributor

@TerminalJack Your fix seems to be only for Windows but I face the same issue on Ubuntu.

Yes, this is definitely a Windows-only fix. Sorry.

I don't know if it is necessary to do something similar on Ubuntu or not. I'm not even sure if it uses a compositor.

@Calinou
Copy link
Member

Calinou commented Oct 27, 2019

I don't know if it is necessary to do something similar on Ubuntu or not. I'm not even sure if it uses a compositor.

It does, in fact, there's no way to disable it on some window managers (including Ubuntu's default) 🙂

@starry-abyss
Copy link
Contributor

@TerminalJack Might also need some logic for when Aero is disabled in e.g. Windows 7 (IIRC it doesn't v-sync, so Godot probably should still v-sync itself in this case)

@TerminalJack
Copy link
Contributor

@starry-abyss I'm hoping that case will be caught. I have an old laptop that has Windows 7 on it. If it still works I'll do some testing with it and see if any changes are necessary.

@TerminalJack
Copy link
Contributor

I fired-up my 10-year old laptop that has Windows 7 on it and tested my changes. I had to use the simplest of projects for testing. (Laptop GPUs were extremely bad back then.) I used the project from this post. I added the following so that I could switch into/out-of full screen.

func _input(event):
	if event is InputEventKey && event.scancode == KEY_F && event.pressed:
		# Switch into or out of full screen mode.
		OS.window_fullscreen = !OS.window_fullscreen

I ran the project both with my changes and without and there weren't any noticeable differences either way. With my changes, the compositor would be used for v-syncing when expected (windowed mode, compositor enabled) and OpenGL double-buffering would be used in all other cases.

The good news is that there won't be any changes necessary to the code. The code detects whether the compositor is enabled or not like it should. It even handles the case where you enable or disable the compositor while the app is running. This is a case that I didn't foresee, however, so I didn't include it in the comments in swap_buffers() regarding the cases where the v-syncing strategy changes on the fly. So that's the only thing I need to change so far as I know.

@lawnjelly
Copy link
Member

One of the things brought up on irc today discussing this (and TerminalJack's PR) is isolating error in the measured input delta from error in the output delta.

Calinou pointed out we can test this to an extent by running with the command line switch --fixed-fps 60. This will treat the input delta as though it is always 1/60th of a second.

It would be very helpful if those having the problem (especially on windows) could let us know if this has any effects on jitter.

@starry-abyss
Copy link
Contributor

@lawnjelly I tried quickly with and without the command line option, but sadly I can't repro the issue today %) Except OBS case, which is still the same.
Is value of the option stored between the editor runs by chance?

@Calinou
Copy link
Member

Calinou commented Oct 29, 2019

Is value of the option stored between the editor runs by chance?

No, it's only a command-line argument (which is stateless).

@starry-abyss
Copy link
Contributor

OK. Also, BTW, is the option available in Godot 3.1.1 (the version on which I do most of the testing here)?

@lawnjelly
Copy link
Member

OK. Also, BTW, is the option available in Godot 3.1.1 (the version on which I do most of the testing here)?

If you are using 3.1.1 to test this, you will need to be moving objects a distance proportional to the delta during _process, as there is no fixed timestep interpolation.

@Calinou
Copy link
Member

Calinou commented Oct 29, 2019

@starry-abyss That command-line argument was added in 3.1, so it should be available in 3.1.1 as well.

@starry-abyss
Copy link
Contributor

starry-abyss commented Oct 30, 2019

So, --fixed-fps 60 didn't help with the issue for me. And running game from command line directly didn't help either (I still had a separate instance of editor on the screen though to quicker reproduce).

And also tried both at once, in case the --fixed-fps 60 request implied this, still jitters.

The difficulty to reproduce yesterday was because I had v-sync off in options from previous tests. :/

there is no fixed timestep interpolation.

Of course, I'm testing methods one by one, not all at once (not like interpolation plugin + dwmflush + any new idea).
Also please next time include the specific steps to try the new idea, so I don't have to guess Godot versions, to run editor or game directly, etc. I don't have the desire to try all possible combinations of everything (with every idea doubling the amount of combinations). :P

@lawnjelly
Copy link
Member

lawnjelly commented Oct 30, 2019

Of course, I'm testing methods one by one, not all at once (not like interpolation plugin + dwmflush + any new idea).
Also please next time include the specific steps to try the new idea, so I don't have to guess Godot versions, to run editor or game directly, etc. I don't have the desire to try all possible combinations of everything (with every idea doubling the amount of combinations). :P

I understand as it isn't mentioned in the docs, and there is no fixed timestep interpolation in core. Perhaps I should try and write something on this for the docs, I've not added documentation before.

The upshot is this:
Irrespective of other issues (delta, OS), if you use physics or move objects in _physics_process, currently Godot will give jitter as a result of aliasing between physics ticks and actual frames. This will occur to some extent in all physics tick rate / monitor refresh rate combinations, some will be worse than others.

The 'jitter fix' method was an attempt to bodge around this aliasing by making it occur in a less pronounced way (it will still occur). Think staircase aliasing.

In order to prevent this 'base level' jitter you currently either need to

  1. (Available in all Godot versions) Move objects in _process and move them by a distance proportional to the delta. This is not ideal for games as you can't use physics, and behaviour is frame rate dependent, however it is ok for jitter testing.

  2. (Available in Godot 3.2 onward) Used fixed timestep interpolation. This is only truly possible in 3.2 with the Engine->get_physics_interpolation_fraction() function. See https://github.com/lawnjelly/smoothing-addon for an example of how to use this.

These are the prerequisites BEFORE you start investigating jitter. They will give a linear relationship between the position of an object and time, which is what we want.

An alternative (more newbie friendly) method of achieving this is with semi-fixed timestep, which I've had a PR in for since July #30798.

This is the basis of scientific investigation and hypothesis testing. The idea is reduce as many of the confounding effects as possible and examine one at a time.

There are 3 main factors at play here:

  1. Linear relationship between object position and time from the game (see above)
  2. Error in input timings
  3. Error in output timings

Eliminate (1) as above. Eliminate (2) by using the command line argument, then you can examine (3) in isolation.

Also, for any investigation of jitter, you should be moving objects directly, not via physics, as physics can potentially add jitter itself.

Edit:
I've just had a look at the minimum repro project in this thread (dodge the creeps) and it is moving with velocity * delta in _process, which should be fine. It does rely on is_action_pressed and personally I'd remove that as a possible problem, but its probably okay. If using another project to test jitter though, beware the points above.

@starry-abyss
Copy link
Contributor

starry-abyss commented Oct 30, 2019

@lawnjelly I see. I was under impression that interpolation plugin is still also something tested only by a few guys, and may be buggy (and even add to jitter). So I took what's stable (3.1.1), that's how "scientific" works for me.
I'll try with the new considerations next time.

I've just had a look at the minimum repro project in this thread

I'm using my own version of the project, as the original one is using animations (can mask the jitter) and has grey sprite over grey background. I'll adjust it so it meets your criteria.

@lawnjelly
Copy link
Member

So, --fixed-fps 60 didn't help with the issue for me.

That is very useful info. This does seem to move the pointy finger of blame towards the output delay (and the compositor), and lend weight to the kind of approach in the PR. This is assuming its using double / triple buffering and keeping the pipeline fed, and not dropping frames.

Is this also the case for @TerminalJack with the command line argument?

@lawnjelly
Copy link
Member

lawnjelly commented Oct 30, 2019

I'm using my own project, as the original one is using animations (can mask the jitter) and has grey sprite over grey background. I'll adjust it so it meets your criteria.

Ah good, thank you! 👍

@lawnjelly I see. I was under impression that interpolation plugin is still also something tested only by a few guys, and may be buggy (and even add to jitter).

A little off topic, but it should be fine (I haven't touched it in a few weeks), shouldn't introduce jitter. If you find any bugs let me know on the issue tracker or even make a PR. 👍 Am intending to put it on the official addons for 3.2 when I get around to it 😄 .

@TerminalJack
Copy link
Contributor

@lawnjelly

Is this also the case for @TerminalJack with the command line argument?

Sorry, I haven't had a chance to try your suggestions out. I kind of went down a rabbit hole on the issue that I'm working on.

@TerminalJack
Copy link
Contributor

@lawnjelly Per our discussion in the PR thread, I just thought that I would mention that you were correct about the --fixed-fps <fps> command-line option. It does, in fact, keep vsync enabled. So, like you were saying, using it is a good way to factor-out any issues with the calculation of the delta. I must have messed up when I tried it earlier and I had stuttering because that's not the case now.

I've been using that option along with my changes to establish that using DwmFlush() does help when running outside of the editor. If you disable DwmFlush() then you can cause stutter in the game simply by dragging some other window around or hovering your mouse over one of the running tasks in the taskbar. Obviously, since the game is running at an 'Above Normal' priority, this shouldn't happen. (And it doesn't if DwmFlush() is being used.)

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