Variable dt causes game to run twice as fast (DEF-3146) (SOLVED)

Maybe I’m just not understanding what the ‘variable_dt’ project setting does but this feels like a bug to me. Update frequency is set to 30 but if I tick the variable_dt setting then the game seems to run twice at fast as if it were at the default update frequency of 60.

1 Like

You will likely get more calls to update() but dt should be smaller and go.animate() and similar should still work as normal.

2 Likes

I have encountered the same issue with my game. Disabling dynamic dt works around the issue. Also, I believe @Tomires and somebody else on Slack have the same issue.

Here’s my completely un-scientific findings:

Update frequency: 60
Dynamic dt: checked

Windows Desktop:
Windows 10, NVIDIA GTX 1060, 60Hz display: 60s in-game = 40s real time, 1.5x speedup

Windows Laptop #1:
Windows 7, NVIDIA 610M, tried with both 60Hz internal display and 60Hz external display: 60s in-game = 55s real time, 1.09x speedup

Windows Laptop #2:
Windows 10, Intel HD Graphics 620, 60Hz internal display: 60s in-game = 40s real time, 1.5x speedup
Also tested HTML build in Chrome on this laptop: works as intended

MacBook:
macOS 10.13.3, NVIDIA 650M, 60Hz internal display: works as intended

3 Likes

Thank you for taking the time to investigate this! This is definitely something we need to look into.

1 Like

Can confirm @dapetcu21 's findings, here are mine:

Windows 7, 2x GTX 880 - slightly faster (10%)
Windows 10, HD 620 - 50% faster
Windows 10, UHD 620 - 50% faster
elementaryOS 0.4 Loki, HD 620 - works as intended
macOS 10.12 Sierra, Iris 5100 - works as intended
HTML across all tested devices - works as intended

All displays on tested machines are 60Hz. It seems like the issue is specific to Windows builds. It does not affect the timer native extension, only the animate methods and the update loop.

1 Like

I stopped investigating this once I activated variable dt for my game and I perceived it to be the same speed on my android phone and windows build.

But, I noticed that with variable dt unchecked (and all scenarios = 60fps), the game went a LOT faster on Windows than Android.

@britzl Hi! Sorry for insisting, but did this get an issue number? It’s pretty serious and reading the forum I see other people are having seemingly similar timing issues:

Additionally, a colleague noticed that there are issues even when variable dt is turned off (animations with go.animate() go too slowly, while the dt we get in update goes at the right rate). I’ve yet to confirm this, though (I’ll only get to Win PC tomorrow). Might have been just frame skips.

1 Like

When variable_dt is not checked the engine will rely on hardware vsync for each frame. The engine also assumes as a refresh rate of 60Hz.

With variable_dt checked the engine will update as fast as possible and calculate dt accordingly. This means that the size of dt can vary greatly between two frames which can be a problem for physics simulations for example.

From what is described in this thread and the fact that the issue seems to be isolated to Windows, there might be some precision issue with the dt calculation when running with variable_dt on Windows. Created issue DEF-3146 for this.

It is important to remember to use dt in your script update functions correctly. With variable_dt your update functions will most likely be called much more often, but with a smaller dt, than when running your game without variable_dt

3 Likes

Don’t be sorry. You’re right to insist! This is a critical issue and something we need to look into. Windows seems to be the problematic platform, and if I recall correctly there were differences between version (Win 7 and 10).

Johan created a ticket that hopefully will be picked up as soon as possible.

5 Likes

Wait, does that mean that checking variable_dt should turn off vsync? That doesn’t happen on OS X and that probably shouldn’t happen.

The way I understood variable_dt is that when it’s turned off:

  • The game displays frames on vsync
  • update() is called with a dt always equal to 1/update_frequency
  • update() might not be called every frame if the screen is drawn faster than update_frequency.
  • update() might be called twice for a frame if the screen is drawn slower than update_frequency.

And when variable_dt is turned on:

  • The game displays frames on vsync
  • update() is called with a dt based on diffing system time.
  • update() is only called exactly once per frame.

Did I understand correctly?

In which case, what happens in the following scenarios?

  1. variable_dt off, update_frequency = 60, user’s display is 50Hz? Does the game run 1.2 times slower or does update() get called twice per frame once every fifth frame?
  2. variable_dt off, update_frequency = 30, user’s display is 60Hz? Does update() only get called once every other frame?
  3. variable_dt off, update_frequency = 60, user’s display is 60Hz, but his GPU/CPU is too slow and sometimes misses the vsync. Does the game still assume it’s running smoothly at 60Hz and the frame skips cause the animations to slow down? Or does it call update() twice after a missed vsync?
2 Likes

With variable_dt turned off the engine will set the GL swap interval to 60 / update_frequency (OpenGL set swap interval) and will only call script update() at each display refresh, with dt set to 1/update_frequency

With variable_dt turned on the engine does not care about vsync at all (vsync can be enabled/disabled in GPU drivers settings which we have no way of controlling) and will just update as fast as possible (calling script update() each time) with dt being calculated using platform timing functions . This might result in screen tearing (screen tearing) and big variations in dt if for example a fast frame is followed by a frame doing lots of heavy computations.

Defold does not have any decoupling between an engine update and calling script update() and we don’t skip frames to compensate for mismatching refresh rates, we rely entirely on hardware vsync (e.g. the combination of the set swap interval and the driver vsync-settings). A caveat; you can use set_vsync message to achieve software vsync in defold.

Perhaps the documentation around this should be improved. Questions about, variable_dt and vsync etc. pop up every now and then here on the forums. I’ll make a note of it!

Correct me if I’m wrong or missed anything @sven @Mathias_Westerdahl @Andreas_Tadic.

3 Likes

Yes, your answer is probably the best summary of the impact of variable_dt on and off that we have anywhere.

I’m using a 165hz monitor with nvidia g-sync, does this also have an effect? I have yet to sit down in my reading lounge with these last explanations so I’m a bit confused.

Edit: Just moving the running dmengine.exe from my 165hz monitor to my 60hz monitor speeds up and slows down the game immensly, so yes, it does have an effect :slight_smile:

2 things I’m still confused about now is:

  1. Is there a performance hit for settings msg.post("@system:", "set_vsync", { swap_interval = 0 } ) ? I tried that, and that removes the “sped up” effect on my 165hz monitor, both monitors seems to act the same.
    This is with variable dt unchecked btw.

  2. What settings gives the most similar/consistent experience between my dev pc and slower mobile phones that might drop below 60 fps here and there? I suppose the answer is dependant on what I define as similar experience…

Is it correct to assume that with variable dt, the game could actually slow down due to frame drops but not let the player “miss” anything, but without variable dt, the game could drop frames and the choppy gameplay could make it harder for the player to react to things, essentially “missing” stuff happening between the frames?

The way I understood it:

WITHOUT variable dt, the game will slow down, but not let the player miss things. WITH variable dt, the game will run at constant speed, but frame skips might make the player miss things

Without variable dt, there is no way to make sure the game runs at the same speed if the display refresh rate is not 60Hz (Which makes this option a no-go for me), UNLESS you use swap_interval = 0, which updates the game on a timer, ignoring vsync (which I believe might cause tearing).

With variable dt, you rely on whatever the driver setting for vsync is and the game will always run at the same speed. Also physics updates might be imprecise due to variable dt.

There is no performance penalty for swap_interval = 0 except your FPS will be fixed at update_frequency.

2 Likes

This makes it a bit clearer for me, but I’m not sure I’m 100% there.

I don’t want tearing, and I don’t want imprecise physics.

@devs, documentation suggestion, maybe have like a simple table with different approaches and their pro’s/con’s, and a comment on different types of games that would benefit from which type of approach?

4 Likes

I’m working on my game on Windows, which runs the game slightly faster than on my Galaxy S7.

I noticed that if I just bump up the timestep on my collection proxy, the game feels a lot better (like on my computer), so I think I’m going to lower the timestep instead during development, adjust gameplay feel accordingly, and then put it to 1.0 when finishing up.

It’s not hard to do since I’m already tweaking timestep due to gameplay features. Only drawback off the bat is that the ratio between “full speed” and slowed down events are off if I keep switching the full speed timestep value and not the others.
The “others” being for example slow down on player death or dramatic events in game.

1 Like

That is a great idea.

3 Likes

In the meantime, I made this work-around to the variable_dt on Windows bug. It measures game time and real time for an interval of 1 second and tells you the time step you should use in your collection proxies.

2 Likes

Hi! Any news on this? @Johan_Beck-Noren? @britzl? It’s a pretty serious bug and my workaround is not perfect.

1 Like

We added ticket DEF-3146 for tracking this issue, but no progress so far I’m sorry to say.

As a first step we talked about better documenting how vsync, variable_dt and monitor refresh rate play together in Defold.

As the problem is described in this thread it definitely seems like a problem with how we measure time, perhaps too low os timer resolution. We are working on it!

8 Likes